@weapp-vite/miniprogram-automator 1.0.1 → 1.0.3
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/README.md +95 -4
- package/dist/index.d.mts +6 -2
- package/dist/index.mjs +119 -25
- package/dist/{launch-IFPMxQYb.mjs → launch-Didv0lMX.mjs} +1880 -277
- package/package.json +1 -1
|
@@ -27,7 +27,12 @@ function createAppInstance(definition) {
|
|
|
27
27
|
return instance;
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
-
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance.ts
|
|
30
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/shared.ts
|
|
31
|
+
const ARRAY_INDEX_PATH_RE$1 = /\[(\d+)\]/g;
|
|
32
|
+
const ARRAY_INDEX_SEGMENT_RE$1 = /^\d+$/;
|
|
33
|
+
function cloneObject$1(value) {
|
|
34
|
+
return JSON.parse(JSON.stringify(value));
|
|
35
|
+
}
|
|
31
36
|
function bindFunction$1(target, key, value) {
|
|
32
37
|
if (typeof value !== "function") {
|
|
33
38
|
target[key] = value;
|
|
@@ -35,19 +40,19 @@ function bindFunction$1(target, key, value) {
|
|
|
35
40
|
}
|
|
36
41
|
target[key] = (...args) => value.apply(target, args);
|
|
37
42
|
}
|
|
38
|
-
function cloneObject$1(value) {
|
|
39
|
-
return JSON.parse(JSON.stringify(value));
|
|
40
|
-
}
|
|
41
43
|
function cloneValue$1(value) {
|
|
42
44
|
if (Array.isArray(value)) return JSON.parse(JSON.stringify(value));
|
|
43
45
|
if (value && typeof value === "object") return cloneObject$1(value);
|
|
44
46
|
return value;
|
|
45
47
|
}
|
|
48
|
+
function cloneRecord(value) {
|
|
49
|
+
return cloneObject$1(value);
|
|
50
|
+
}
|
|
46
51
|
function parseDataPath$1(path) {
|
|
47
|
-
return path.replace(
|
|
52
|
+
return path.replace(ARRAY_INDEX_PATH_RE$1, ".$1").split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
48
53
|
}
|
|
49
54
|
function isArrayIndexSegment$1(segment) {
|
|
50
|
-
return
|
|
55
|
+
return ARRAY_INDEX_SEGMENT_RE$1.test(segment);
|
|
51
56
|
}
|
|
52
57
|
function createContainerByNextSegment$1(nextSegment) {
|
|
53
58
|
return isArrayIndexSegment$1(nextSegment ?? "") ? [] : {};
|
|
@@ -64,17 +69,74 @@ function assignByPath$1(target, path, value) {
|
|
|
64
69
|
if (!next || typeof next !== "object") current[normalizedSegment] = createContainerByNextSegment$1(nextSegment);
|
|
65
70
|
current = current[normalizedSegment];
|
|
66
71
|
}
|
|
67
|
-
const leafSegment = segments
|
|
72
|
+
const leafSegment = segments.at(-1);
|
|
68
73
|
const normalizedLeafSegment = isArrayIndexSegment$1(leafSegment) ? Number(leafSegment) : leafSegment;
|
|
69
74
|
current[normalizedLeafSegment] = value;
|
|
70
75
|
}
|
|
76
|
+
function mergeRecord(...records) {
|
|
77
|
+
return Object.assign({}, ...records.filter(Boolean));
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/observers.ts
|
|
81
|
+
function runPropertyObservers(definition, instance, changedKeys, previousProperties) {
|
|
82
|
+
const propOptions = definition.properties;
|
|
83
|
+
if (!propOptions || typeof propOptions !== "object" || Array.isArray(propOptions)) return;
|
|
84
|
+
for (const changedKey of changedKeys) {
|
|
85
|
+
const option = propOptions[changedKey];
|
|
86
|
+
if (!option || typeof option !== "object" || Array.isArray(option)) continue;
|
|
87
|
+
const observer = option.observer;
|
|
88
|
+
if (typeof observer !== "function") continue;
|
|
89
|
+
observer.call(instance, instance.properties[changedKey], previousProperties[changedKey]);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function normalizeObserverPattern(pattern) {
|
|
93
|
+
return pattern.split(",").map((item) => item.trim()).filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
function resolveObservedValue(instance, pattern) {
|
|
96
|
+
if (pattern === "**") return {
|
|
97
|
+
...instance.properties,
|
|
98
|
+
...instance.data
|
|
99
|
+
};
|
|
100
|
+
const segments = parseDataPath$1(pattern);
|
|
101
|
+
if (segments.length === 0) return;
|
|
102
|
+
const [rootSegment, ...restSegments] = segments;
|
|
103
|
+
let current = (Object.hasOwn(instance.properties, rootSegment) ? instance.properties : instance.data)?.[rootSegment];
|
|
104
|
+
for (const segment of restSegments) {
|
|
105
|
+
const normalizedSegment = isArrayIndexSegment$1(segment) ? Number(segment) : segment;
|
|
106
|
+
current = current?.[normalizedSegment];
|
|
107
|
+
}
|
|
108
|
+
return current;
|
|
109
|
+
}
|
|
110
|
+
function matchObserverPattern(pattern, changedKeys) {
|
|
111
|
+
if (pattern === "**") return changedKeys.length > 0;
|
|
112
|
+
const patterns = normalizeObserverPattern(pattern);
|
|
113
|
+
return changedKeys.some((changedKey) => {
|
|
114
|
+
return patterns.some((item) => {
|
|
115
|
+
if (item === "**") return true;
|
|
116
|
+
if (changedKey === item) return true;
|
|
117
|
+
return changedKey.startsWith(`${item}.`) || changedKey.startsWith(`${item}[`);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function runComponentObservers(definition, instance, changedKeys, previousProperties = {}) {
|
|
122
|
+
if (changedKeys.length === 0) return;
|
|
123
|
+
runPropertyObservers(definition, instance, changedKeys, previousProperties);
|
|
124
|
+
const observers = definition.observers;
|
|
125
|
+
if (observers && typeof observers === "object" && !Array.isArray(observers)) for (const [pattern, handler] of Object.entries(observers)) {
|
|
126
|
+
if (typeof handler !== "function" || !matchObserverPattern(pattern, changedKeys)) continue;
|
|
127
|
+
const args = normalizeObserverPattern(pattern).map((item) => resolveObservedValue(instance, item));
|
|
128
|
+
handler.call(instance, ...args);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/properties.ts
|
|
71
133
|
function resolveInitialData$1(definition) {
|
|
72
134
|
const rawData = definition.data;
|
|
73
135
|
if (typeof rawData === "function") {
|
|
74
136
|
const next = rawData.call(definition);
|
|
75
|
-
return next && typeof next === "object" && !Array.isArray(next) ?
|
|
137
|
+
return next && typeof next === "object" && !Array.isArray(next) ? cloneRecord(next) : {};
|
|
76
138
|
}
|
|
77
|
-
return rawData && typeof rawData === "object" && !Array.isArray(rawData) ?
|
|
139
|
+
return rawData && typeof rawData === "object" && !Array.isArray(rawData) ? cloneRecord(rawData) : {};
|
|
78
140
|
}
|
|
79
141
|
function isBehaviorDefinition(value) {
|
|
80
142
|
return !!value && typeof value === "object" && !Array.isArray(value) && value.__isHeadlessBehavior__ === true;
|
|
@@ -89,9 +151,6 @@ function flattenBehaviors(definition) {
|
|
|
89
151
|
}
|
|
90
152
|
return flattened;
|
|
91
153
|
}
|
|
92
|
-
function mergeRecord(...records) {
|
|
93
|
-
return Object.assign({}, ...records.filter(Boolean));
|
|
94
|
-
}
|
|
95
154
|
function normalizeComponentDefinition(definition) {
|
|
96
155
|
const behaviors = flattenBehaviors(definition);
|
|
97
156
|
if (behaviors.length === 0) return definition;
|
|
@@ -184,7 +243,7 @@ function resolveInitialProperties(definition, properties) {
|
|
|
184
243
|
const resolved = {};
|
|
185
244
|
const propOptions = definition.properties;
|
|
186
245
|
if (propOptions && typeof propOptions === "object" && !Array.isArray(propOptions)) for (const [key, option] of Object.entries(propOptions)) {
|
|
187
|
-
if (Object.
|
|
246
|
+
if (Object.hasOwn(properties, key)) {
|
|
188
247
|
resolved[key] = coerceComponentPropertyValue(properties[key], option);
|
|
189
248
|
continue;
|
|
190
249
|
}
|
|
@@ -198,61 +257,13 @@ function resolvePropertyDefaultValue(option) {
|
|
|
198
257
|
if (!option || typeof option !== "object" || Array.isArray(option) || !("value" in option)) return;
|
|
199
258
|
return cloneValue$1(option.value);
|
|
200
259
|
}
|
|
201
|
-
function runPropertyObservers(definition, instance, changedKeys, previousProperties) {
|
|
202
|
-
const propOptions = definition.properties;
|
|
203
|
-
if (!propOptions || typeof propOptions !== "object" || Array.isArray(propOptions)) return;
|
|
204
|
-
for (const changedKey of changedKeys) {
|
|
205
|
-
const option = propOptions[changedKey];
|
|
206
|
-
if (!option || typeof option !== "object" || Array.isArray(option)) continue;
|
|
207
|
-
const observer = option.observer;
|
|
208
|
-
if (typeof observer !== "function") continue;
|
|
209
|
-
observer.call(instance, instance.properties[changedKey], previousProperties[changedKey]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function normalizeObserverPattern(pattern) {
|
|
213
|
-
return pattern.split(",").map((item) => item.trim()).filter(Boolean);
|
|
214
|
-
}
|
|
215
|
-
function resolveObservedValue(instance, pattern) {
|
|
216
|
-
if (pattern === "**") return {
|
|
217
|
-
...instance.properties,
|
|
218
|
-
...instance.data
|
|
219
|
-
};
|
|
220
|
-
const segments = parseDataPath$1(pattern);
|
|
221
|
-
if (segments.length === 0) return;
|
|
222
|
-
const [rootSegment, ...restSegments] = segments;
|
|
223
|
-
let current = (Object.prototype.hasOwnProperty.call(instance.properties, rootSegment) ? instance.properties : instance.data)?.[rootSegment];
|
|
224
|
-
for (const segment of restSegments) {
|
|
225
|
-
const normalizedSegment = isArrayIndexSegment$1(segment) ? Number(segment) : segment;
|
|
226
|
-
current = current?.[normalizedSegment];
|
|
227
|
-
}
|
|
228
|
-
return current;
|
|
229
|
-
}
|
|
230
|
-
function matchObserverPattern(pattern, changedKeys) {
|
|
231
|
-
if (pattern === "**") return changedKeys.length > 0;
|
|
232
|
-
const patterns = normalizeObserverPattern(pattern);
|
|
233
|
-
return changedKeys.some((changedKey) => {
|
|
234
|
-
return patterns.some((item) => {
|
|
235
|
-
if (item === "**") return true;
|
|
236
|
-
if (changedKey === item) return true;
|
|
237
|
-
return changedKey.startsWith(`${item}.`) || changedKey.startsWith(`${item}[`);
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
function runComponentObservers(definition, instance, changedKeys, previousProperties = {}) {
|
|
242
|
-
if (changedKeys.length === 0) return;
|
|
243
|
-
runPropertyObservers(definition, instance, changedKeys, previousProperties);
|
|
244
|
-
const observers = definition.observers;
|
|
245
|
-
if (observers && typeof observers === "object" && !Array.isArray(observers)) for (const [pattern, handler] of Object.entries(observers)) {
|
|
246
|
-
if (typeof handler !== "function" || !matchObserverPattern(pattern, changedKeys)) continue;
|
|
247
|
-
const args = normalizeObserverPattern(pattern).map((item) => resolveObservedValue(instance, item));
|
|
248
|
-
handler.call(instance, ...args);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
260
|
function normalizeComponentPropertyValue(definition, key, rawValue) {
|
|
252
261
|
const option = definition.properties?.[key];
|
|
253
262
|
if (rawValue == null) return resolvePropertyDefaultValue(option);
|
|
254
263
|
return coerceComponentPropertyValue(rawValue, option);
|
|
255
264
|
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance/index.ts
|
|
256
267
|
function createComponentInstance(options) {
|
|
257
268
|
const definition = normalizeComponentDefinition(options.definition);
|
|
258
269
|
const instance = {
|
|
@@ -2828,9 +2839,11 @@ function parseDocument(data, options) {
|
|
|
2828
2839
|
return handler.root;
|
|
2829
2840
|
}
|
|
2830
2841
|
//#endregion
|
|
2831
|
-
//#region ../../mpcore/packages/simulator/src/runtime/render.ts
|
|
2832
|
-
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
2842
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/shared.ts
|
|
2833
2843
|
const TEMPLATE_INTERPOLATION_RE$1 = /\{\{([^{}]+)\}\}/g;
|
|
2844
|
+
const DATASET_NAME_RE$3 = /-([a-z])/g;
|
|
2845
|
+
const BRACKET_INDEX_RE = /\[(\d+)\]/g;
|
|
2846
|
+
const LEADING_SLASH_RE$4 = /^\/+/;
|
|
2834
2847
|
const EVENT_BINDING_ATTRS = [
|
|
2835
2848
|
"bindtap",
|
|
2836
2849
|
"bind:tap",
|
|
@@ -2853,22 +2866,20 @@ const STRUCTURAL_ATTRS = [
|
|
|
2853
2866
|
"wx:key"
|
|
2854
2867
|
];
|
|
2855
2868
|
const WX_ELSE_ATTRS = new Set(["wx:elif", "wx:else"]);
|
|
2856
|
-
const DATASET_NAME_RE$2 = /-([a-z])/g;
|
|
2857
|
-
const BRACKET_INDEX_RE = /\[(\d+)\]/g;
|
|
2858
2869
|
const CLASS_SPLIT_RE = /\s+/;
|
|
2859
2870
|
const JS_FILE_RE = /\.js$/;
|
|
2860
2871
|
function isMustacheOnly(value) {
|
|
2861
2872
|
const trimmed = value.trim();
|
|
2862
2873
|
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
2863
2874
|
}
|
|
2864
|
-
function toDatasetKey$
|
|
2865
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE$
|
|
2875
|
+
function toDatasetKey$3(attributeName) {
|
|
2876
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$3, (_match, char) => char.toUpperCase());
|
|
2866
2877
|
}
|
|
2867
|
-
function collectDataset$
|
|
2878
|
+
function collectDataset$3(node) {
|
|
2868
2879
|
const dataset = {};
|
|
2869
2880
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
2870
2881
|
if (!key.startsWith("data-") || key === "data-sim-scope" || key === "data-sim-tap" || key === "data-sim-component") continue;
|
|
2871
|
-
dataset[toDatasetKey$
|
|
2882
|
+
dataset[toDatasetKey$3(key)] = String(value);
|
|
2872
2883
|
}
|
|
2873
2884
|
return dataset;
|
|
2874
2885
|
}
|
|
@@ -2962,21 +2973,46 @@ function resolveComponentAttributeValue(value, scope) {
|
|
|
2962
2973
|
}
|
|
2963
2974
|
return interpolateTemplate$1(value, scope.data);
|
|
2964
2975
|
}
|
|
2965
|
-
function
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2976
|
+
function applyNodeBindings(node, scope) {
|
|
2977
|
+
if (!isTagNode(node)) {
|
|
2978
|
+
if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplate$1(node.data, scope.data);
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
node.attribs ??= {};
|
|
2982
|
+
node.attribs["data-sim-scope"] = scope.getScopeId();
|
|
2983
|
+
for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
|
|
2984
|
+
for (const [key, value] of Object.entries({ ...node.attribs })) {
|
|
2985
|
+
if (EVENT_BINDING_ATTRS.includes(key)) {
|
|
2986
|
+
node.attribs["data-sim-tap"] = value;
|
|
2987
|
+
continue;
|
|
2974
2988
|
}
|
|
2975
|
-
|
|
2976
|
-
} catch {
|
|
2977
|
-
return /* @__PURE__ */ new Map();
|
|
2989
|
+
node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
|
|
2978
2990
|
}
|
|
2979
2991
|
}
|
|
2992
|
+
function createMergedScopeData(pageData, componentProperties, componentData) {
|
|
2993
|
+
return {
|
|
2994
|
+
...pageData,
|
|
2995
|
+
...componentProperties,
|
|
2996
|
+
...componentData
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
function evaluateConditionalBranch(node, scope) {
|
|
3000
|
+
const condition = node.attribs?.["wx:if"] ?? node.attribs?.["wx:elif"];
|
|
3001
|
+
if (condition == null) return true;
|
|
3002
|
+
return Boolean(resolveRawValueByPath(scope.data, condition));
|
|
3003
|
+
}
|
|
3004
|
+
function createLoopScope(scope, itemName, indexName, item, index) {
|
|
3005
|
+
return {
|
|
3006
|
+
...scope,
|
|
3007
|
+
data: {
|
|
3008
|
+
...scope.data,
|
|
3009
|
+
[indexName]: index,
|
|
3010
|
+
[itemName]: item
|
|
3011
|
+
}
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
//#endregion
|
|
3015
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/component.ts
|
|
2980
3016
|
function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, alias) {
|
|
2981
3017
|
const componentBasePath = resolveUsingComponents(ownerJsonPath, ownerFilePath).get(alias);
|
|
2982
3018
|
if (!componentBasePath) return null;
|
|
@@ -2991,6 +3027,21 @@ function resolveComponentRegistryEntry(context, ownerJsonPath, ownerFilePath, al
|
|
|
2991
3027
|
absoluteTemplatePath
|
|
2992
3028
|
};
|
|
2993
3029
|
}
|
|
3030
|
+
function resolveUsingComponents(ownerJsonPath, ownerFilePath) {
|
|
3031
|
+
try {
|
|
3032
|
+
const usingComponents = JSON.parse(fs.readFileSync(ownerJsonPath, "utf8")).usingComponents;
|
|
3033
|
+
if (!usingComponents || typeof usingComponents !== "object" || Array.isArray(usingComponents)) return /* @__PURE__ */ new Map();
|
|
3034
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
3035
|
+
for (const [alias, rawPath] of Object.entries(usingComponents)) {
|
|
3036
|
+
if (typeof rawPath !== "string") continue;
|
|
3037
|
+
const basePath = rawPath.startsWith("/") ? rawPath.replace(LEADING_SLASH_RE$4, "") : path.posix.normalize(path.posix.join(path.posix.dirname(ownerFilePath), rawPath));
|
|
3038
|
+
resolved.set(alias, basePath.replace(LEADING_SLASH_RE$4, ""));
|
|
3039
|
+
}
|
|
3040
|
+
return resolved;
|
|
3041
|
+
} catch {
|
|
3042
|
+
return /* @__PURE__ */ new Map();
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
2994
3045
|
function collectComponentEventBindings(hostNode) {
|
|
2995
3046
|
const eventBindings = /* @__PURE__ */ new Map();
|
|
2996
3047
|
for (const [key, value] of Object.entries(hostNode.attribs ?? {})) {
|
|
@@ -3006,7 +3057,7 @@ function collectComponentEventBindings(hostNode) {
|
|
|
3006
3057
|
return eventBindings;
|
|
3007
3058
|
}
|
|
3008
3059
|
function buildComponentTrigger(componentScopeId, context, hostNode) {
|
|
3009
|
-
const hostDataset = collectDataset$
|
|
3060
|
+
const hostDataset = collectDataset$3(hostNode);
|
|
3010
3061
|
const hostId = hostNode.attribs?.id ?? "";
|
|
3011
3062
|
return (instance, eventName, detail, triggerOptions) => {
|
|
3012
3063
|
const interactionTarget = instance.__lastInteractionEvent__?.target;
|
|
@@ -3041,22 +3092,6 @@ function buildComponentTrigger(componentScopeId, context, hostNode) {
|
|
|
3041
3092
|
}
|
|
3042
3093
|
};
|
|
3043
3094
|
}
|
|
3044
|
-
function applyNodeBindings(node, scope) {
|
|
3045
|
-
if (!isTagNode(node)) {
|
|
3046
|
-
if (node.type === "text" && typeof node.data === "string") node.data = interpolateTemplate$1(node.data, scope.data);
|
|
3047
|
-
return;
|
|
3048
|
-
}
|
|
3049
|
-
node.attribs ??= {};
|
|
3050
|
-
node.attribs["data-sim-scope"] = scope.getScopeId();
|
|
3051
|
-
for (const key of STRUCTURAL_ATTRS) delete node.attribs[key];
|
|
3052
|
-
for (const [key, value] of Object.entries({ ...node.attribs })) {
|
|
3053
|
-
if (EVENT_BINDING_ATTRS.includes(key)) {
|
|
3054
|
-
node.attribs["data-sim-tap"] = value;
|
|
3055
|
-
continue;
|
|
3056
|
-
}
|
|
3057
|
-
node.attribs[key] = typeof value === "string" ? String(resolveAttributeValue(value, scope)) : String(value);
|
|
3058
|
-
}
|
|
3059
|
-
}
|
|
3060
3095
|
function syncComponentProperties(instance, definition, nextProperties, bindingExpressions, changedPageKeys) {
|
|
3061
3096
|
const changedRootKeys = [];
|
|
3062
3097
|
const previousProperties = {};
|
|
@@ -3079,31 +3114,62 @@ function syncComponentProperties(instance, definition, nextProperties, bindingEx
|
|
|
3079
3114
|
if (changedRootKeys.length === 0) return;
|
|
3080
3115
|
runComponentObservers(definition, instance, changedRootKeys, previousProperties);
|
|
3081
3116
|
}
|
|
3082
|
-
function
|
|
3117
|
+
function createComponentScope(clonedNode, scope, componentScopeId, componentInstance) {
|
|
3118
|
+
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
3083
3119
|
return {
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3120
|
+
alias: clonedNode.name,
|
|
3121
|
+
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
3122
|
+
data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
|
|
3123
|
+
dataset: collectDataset$3(clonedNode),
|
|
3124
|
+
eventBindings: collectComponentEventBindings(clonedNode),
|
|
3125
|
+
getMethod: (methodName) => {
|
|
3126
|
+
const method = componentInstance?.[methodName];
|
|
3127
|
+
return typeof method === "function" ? method : void 0;
|
|
3128
|
+
},
|
|
3129
|
+
getScopeId: () => componentScopeId,
|
|
3130
|
+
hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3131
|
+
id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3132
|
+
listenerScopeId: scope.getScopeId(),
|
|
3133
|
+
ownerScopeId
|
|
3087
3134
|
};
|
|
3088
3135
|
}
|
|
3089
|
-
function
|
|
3090
|
-
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3136
|
+
function resolveComponentProperties(clonedNode, scope) {
|
|
3137
|
+
const nextProperties = {};
|
|
3138
|
+
const bindingExpressions = {};
|
|
3139
|
+
for (const [key, value] of Object.entries(clonedNode.attribs ?? {})) {
|
|
3140
|
+
if (key.startsWith("bind")) continue;
|
|
3141
|
+
if (isMustacheOnly(String(value))) bindingExpressions[key] = String(value).trim().slice(2, -2).trim();
|
|
3142
|
+
nextProperties[key] = resolveComponentAttributeValue(String(value), scope);
|
|
3143
|
+
}
|
|
3093
3144
|
return {
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
...scope.data,
|
|
3097
|
-
[indexName]: index,
|
|
3098
|
-
[itemName]: item
|
|
3099
|
-
}
|
|
3145
|
+
nextProperties,
|
|
3146
|
+
bindingExpressions
|
|
3100
3147
|
};
|
|
3101
3148
|
}
|
|
3102
|
-
function
|
|
3103
|
-
const
|
|
3104
|
-
|
|
3105
|
-
|
|
3149
|
+
function createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId) {
|
|
3150
|
+
const componentInstance = createComponentInstance({
|
|
3151
|
+
definition: componentEntry.definition,
|
|
3152
|
+
properties: nextProperties,
|
|
3153
|
+
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
3154
|
+
});
|
|
3155
|
+
componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
|
|
3156
|
+
componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
|
|
3157
|
+
componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
|
|
3158
|
+
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
|
|
3159
|
+
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.session.selectOwnerComponent(componentScopeId) : null;
|
|
3160
|
+
runComponentLifecycle(componentInstance, "created");
|
|
3161
|
+
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(nextProperties), {});
|
|
3162
|
+
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
3163
|
+
runComponentLifecycle(componentInstance, "attached");
|
|
3164
|
+
context.componentCache.set(componentScopeId, componentInstance);
|
|
3165
|
+
return componentInstance;
|
|
3166
|
+
}
|
|
3167
|
+
function renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes) {
|
|
3168
|
+
const componentDocument = parseTemplateDocument(readTemplateSource(componentEntry.absoluteTemplatePath));
|
|
3169
|
+
return renderNodeTree((componentDocument.children ?? [])[0] ?? componentDocument, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, seenComponentScopes);
|
|
3106
3170
|
}
|
|
3171
|
+
//#endregion
|
|
3172
|
+
//#region ../../mpcore/packages/simulator/src/runtime/render/index.ts
|
|
3107
3173
|
function expandNodeByFor(node, scope) {
|
|
3108
3174
|
const forExpression = node.attribs?.["wx:for"];
|
|
3109
3175
|
if (!forExpression) return [{
|
|
@@ -3173,49 +3239,14 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
3173
3239
|
if (componentEntry) {
|
|
3174
3240
|
const componentScopeId = `${instancePath}/${clonedNode.name}`;
|
|
3175
3241
|
const ownerScopeId = scope.getScopeId().includes("/") ? scope.getScopeId() : void 0;
|
|
3176
|
-
const nextProperties =
|
|
3177
|
-
const bindingExpressions = {};
|
|
3178
|
-
for (const [key, value] of Object.entries(clonedNode.attribs ?? {})) {
|
|
3179
|
-
if (key.startsWith("bind")) continue;
|
|
3180
|
-
if (isMustacheOnly(String(value))) bindingExpressions[key] = String(value).trim().slice(2, -2).trim();
|
|
3181
|
-
nextProperties[key] = resolveComponentAttributeValue(String(value), scope);
|
|
3182
|
-
}
|
|
3242
|
+
const { nextProperties, bindingExpressions } = resolveComponentProperties(clonedNode, scope);
|
|
3183
3243
|
let componentInstance = context.componentCache.get(componentScopeId);
|
|
3184
|
-
if (!componentInstance)
|
|
3185
|
-
|
|
3186
|
-
definition: componentEntry.definition,
|
|
3187
|
-
properties: nextProperties,
|
|
3188
|
-
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
3189
|
-
});
|
|
3190
|
-
componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
|
|
3191
|
-
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
|
|
3192
|
-
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.session.selectOwnerComponent(componentScopeId) : null;
|
|
3193
|
-
runComponentLifecycle(componentInstance, "created");
|
|
3194
|
-
runComponentObservers(componentInstance.__definition__ ?? componentEntry.definition, componentInstance, Object.keys(nextProperties), {});
|
|
3195
|
-
componentInstance.__propertySnapshots = Object.fromEntries(Object.entries(componentInstance.properties).map(([key, propertyValue]) => [key, cloneValue$1(propertyValue)]));
|
|
3196
|
-
runComponentLifecycle(componentInstance, "attached");
|
|
3197
|
-
context.componentCache.set(componentScopeId, componentInstance);
|
|
3198
|
-
} else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
|
|
3244
|
+
if (!componentInstance) componentInstance = createRuntimeComponentInstance(componentScopeId, context, clonedNode, componentEntry, nextProperties, ownerScopeId);
|
|
3245
|
+
else syncComponentProperties(componentInstance, componentInstance.__definition__ ?? componentEntry.definition, nextProperties, bindingExpressions, context.changedPageKeys);
|
|
3199
3246
|
seenComponentScopes.add(componentScopeId);
|
|
3200
|
-
const componentScope =
|
|
3201
|
-
alias: clonedNode.name,
|
|
3202
|
-
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
3203
|
-
data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
|
|
3204
|
-
dataset: collectDataset$2(clonedNode),
|
|
3205
|
-
eventBindings: collectComponentEventBindings(clonedNode),
|
|
3206
|
-
getMethod: (methodName) => {
|
|
3207
|
-
const method = componentInstance?.[methodName];
|
|
3208
|
-
return typeof method === "function" ? method : void 0;
|
|
3209
|
-
},
|
|
3210
|
-
getScopeId: () => componentScopeId,
|
|
3211
|
-
hostId: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3212
|
-
id: typeof clonedNode.attribs?.id === "string" ? clonedNode.attribs.id : void 0,
|
|
3213
|
-
listenerScopeId: scope.getScopeId(),
|
|
3214
|
-
ownerScopeId
|
|
3215
|
-
};
|
|
3247
|
+
const componentScope = createComponentScope(clonedNode, scope, componentScopeId, componentInstance);
|
|
3216
3248
|
context.componentScopes.set(componentScopeId, componentScope);
|
|
3217
|
-
const
|
|
3218
|
-
const renderedComponentRoot = renderNodeTree((componentDocument.children ?? [])[0] ?? componentDocument, componentScope, context, path.resolve(context.project.miniprogramRootPath, `${componentEntry.filePath.replace(JS_FILE_RE, "")}.json`), componentEntry.filePath, componentScopeId, seenComponentScopes);
|
|
3249
|
+
const renderedComponentRoot = renderRuntimeComponentTemplate(context, componentEntry, renderNodeTree, componentScope, componentScopeId, seenComponentScopes);
|
|
3219
3250
|
if (renderedComponentRoot.attribs) renderedComponentRoot.attribs["data-sim-component"] = clonedNode.name;
|
|
3220
3251
|
if (!componentInstance.__ready__) {
|
|
3221
3252
|
runComponentLifecycle(componentInstance, "ready");
|
|
@@ -3287,7 +3318,7 @@ function registerComponentDefinition(registries, definition) {
|
|
|
3287
3318
|
return definition;
|
|
3288
3319
|
}
|
|
3289
3320
|
//#endregion
|
|
3290
|
-
//#region ../../mpcore/packages/simulator/src/host/wx.ts
|
|
3321
|
+
//#region ../../mpcore/packages/simulator/src/host/wx/index.ts
|
|
3291
3322
|
function invokeWxApi(operation, option) {
|
|
3292
3323
|
try {
|
|
3293
3324
|
const result = operation();
|
|
@@ -3312,9 +3343,65 @@ function resolveCapabilityValue(source, schema) {
|
|
|
3312
3343
|
function createHeadlessWx(driver) {
|
|
3313
3344
|
const capabilityTree = {
|
|
3314
3345
|
canIUse: true,
|
|
3346
|
+
canvasToTempFilePath: true,
|
|
3347
|
+
chooseImage: { return: {
|
|
3348
|
+
errMsg: true,
|
|
3349
|
+
tempFilePaths: true,
|
|
3350
|
+
tempFiles: true
|
|
3351
|
+
} },
|
|
3352
|
+
chooseMessageFile: { return: {
|
|
3353
|
+
errMsg: true,
|
|
3354
|
+
tempFiles: true
|
|
3355
|
+
} },
|
|
3356
|
+
chooseMedia: { return: {
|
|
3357
|
+
errMsg: true,
|
|
3358
|
+
tempFiles: true,
|
|
3359
|
+
type: true
|
|
3360
|
+
} },
|
|
3361
|
+
chooseVideo: { return: {
|
|
3362
|
+
duration: true,
|
|
3363
|
+
errMsg: true,
|
|
3364
|
+
height: true,
|
|
3365
|
+
size: true,
|
|
3366
|
+
tempFilePath: true,
|
|
3367
|
+
width: true
|
|
3368
|
+
} },
|
|
3369
|
+
compressImage: { return: {
|
|
3370
|
+
errMsg: true,
|
|
3371
|
+
tempFilePath: true
|
|
3372
|
+
} },
|
|
3315
3373
|
clearStorage: true,
|
|
3316
3374
|
clearStorageSync: true,
|
|
3375
|
+
createAnimation: true,
|
|
3376
|
+
createCanvasContext: true,
|
|
3377
|
+
createIntersectionObserver: true,
|
|
3378
|
+
createVideoContext: true,
|
|
3317
3379
|
createSelectorQuery: true,
|
|
3380
|
+
getImageInfo: { return: {
|
|
3381
|
+
errMsg: true,
|
|
3382
|
+
height: true,
|
|
3383
|
+
orientation: true,
|
|
3384
|
+
path: true,
|
|
3385
|
+
type: true,
|
|
3386
|
+
width: true
|
|
3387
|
+
} },
|
|
3388
|
+
getFileInfo: { return: {
|
|
3389
|
+
digest: true,
|
|
3390
|
+
errMsg: true,
|
|
3391
|
+
size: true
|
|
3392
|
+
} },
|
|
3393
|
+
openDocument: { return: { errMsg: true } },
|
|
3394
|
+
getVideoInfo: { return: {
|
|
3395
|
+
bitrate: true,
|
|
3396
|
+
duration: true,
|
|
3397
|
+
errMsg: true,
|
|
3398
|
+
fps: true,
|
|
3399
|
+
height: true,
|
|
3400
|
+
orientation: true,
|
|
3401
|
+
size: true,
|
|
3402
|
+
type: true,
|
|
3403
|
+
width: true
|
|
3404
|
+
} },
|
|
3318
3405
|
getFileSystemManager: true,
|
|
3319
3406
|
getSavedFileInfo: true,
|
|
3320
3407
|
getSavedFileList: true,
|
|
@@ -3352,6 +3439,10 @@ function createHeadlessWx(driver) {
|
|
|
3352
3439
|
},
|
|
3353
3440
|
scene: true
|
|
3354
3441
|
} },
|
|
3442
|
+
getClipboardData: { return: {
|
|
3443
|
+
data: true,
|
|
3444
|
+
errMsg: true
|
|
3445
|
+
} },
|
|
3355
3446
|
getMenuButtonBoundingClientRect: { return: {
|
|
3356
3447
|
bottom: true,
|
|
3357
3448
|
height: true,
|
|
@@ -3426,14 +3517,18 @@ function createHeadlessWx(driver) {
|
|
|
3426
3517
|
offNetworkStatusChange: true,
|
|
3427
3518
|
onNetworkStatusChange: true,
|
|
3428
3519
|
pageScrollTo: true,
|
|
3520
|
+
previewImage: true,
|
|
3429
3521
|
reLaunch: true,
|
|
3430
3522
|
redirectTo: true,
|
|
3523
|
+
saveImageToPhotosAlbum: true,
|
|
3524
|
+
saveVideoToPhotosAlbum: true,
|
|
3431
3525
|
removeStorage: true,
|
|
3432
3526
|
removeStorageSync: true,
|
|
3433
3527
|
request: true,
|
|
3434
3528
|
saveFile: true,
|
|
3435
3529
|
setBackgroundColor: true,
|
|
3436
3530
|
setBackgroundTextStyle: true,
|
|
3531
|
+
setClipboardData: true,
|
|
3437
3532
|
setStorage: true,
|
|
3438
3533
|
setStorageSync: true,
|
|
3439
3534
|
setNavigationBarColor: true,
|
|
@@ -3450,6 +3545,7 @@ function createHeadlessWx(driver) {
|
|
|
3450
3545
|
showLoading: true,
|
|
3451
3546
|
showModal: true,
|
|
3452
3547
|
showToast: true,
|
|
3548
|
+
startPullDownRefresh: true,
|
|
3453
3549
|
stopPullDownRefresh: true,
|
|
3454
3550
|
switchTab: true,
|
|
3455
3551
|
uploadFile: true,
|
|
@@ -3459,11 +3555,18 @@ function createHeadlessWx(driver) {
|
|
|
3459
3555
|
};
|
|
3460
3556
|
return {
|
|
3461
3557
|
canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
}, option),
|
|
3558
|
+
canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
|
|
3559
|
+
chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
|
|
3560
|
+
chooseMessageFile: (option) => invokeWxApi(() => driver.chooseMessageFile(option ?? {}), option),
|
|
3561
|
+
chooseMedia: (option) => invokeWxApi(() => driver.chooseMedia(option ?? {}), option),
|
|
3562
|
+
chooseVideo: (option) => invokeWxApi(() => driver.chooseVideo(option ?? {}), option),
|
|
3563
|
+
compressImage: (option) => invokeWxApi(() => driver.compressImage(option), option),
|
|
3564
|
+
clearStorage: (option) => invokeWxApi(() => ({ errMsg: (driver.clearStorageSync(), "clearStorage:ok") }), option),
|
|
3466
3565
|
clearStorageSync: () => driver.clearStorageSync(),
|
|
3566
|
+
createAnimation: (option) => driver.createAnimation(option),
|
|
3567
|
+
createCanvasContext: (canvasId, component) => driver.createCanvasContext(canvasId, component),
|
|
3568
|
+
createIntersectionObserver: (component, options) => driver.createIntersectionObserver(component, options),
|
|
3569
|
+
createVideoContext: (videoId, component) => driver.createVideoContext(videoId, component),
|
|
3467
3570
|
createSelectorQuery: () => {
|
|
3468
3571
|
const requests = [];
|
|
3469
3572
|
let scope;
|
|
@@ -3522,12 +3625,16 @@ function createHeadlessWx(driver) {
|
|
|
3522
3625
|
return query;
|
|
3523
3626
|
},
|
|
3524
3627
|
getEnterOptionsSync: () => driver.getEnterOptionsSync(),
|
|
3628
|
+
getFileInfo: (option) => invokeWxApi(() => driver.getFileInfo(option), option),
|
|
3629
|
+
getImageInfo: (option) => invokeWxApi(() => driver.getImageInfo(option), option),
|
|
3630
|
+
getVideoInfo: (option) => invokeWxApi(() => driver.getVideoInfo(option), option),
|
|
3525
3631
|
getFileSystemManager: () => driver.getFileSystemManager(),
|
|
3526
3632
|
getSavedFileInfo: (option) => invokeWxApi(() => driver.getSavedFileInfo(option), option),
|
|
3527
3633
|
getSavedFileList: (option) => invokeWxApi(() => driver.getSavedFileList(option), option),
|
|
3528
3634
|
getAppBaseInfo: (option) => invokeWxApi(() => driver.getAppBaseInfoSync(), option),
|
|
3529
3635
|
getAppBaseInfoSync: () => driver.getAppBaseInfoSync(),
|
|
3530
3636
|
getLaunchOptionsSync: () => driver.getLaunchOptionsSync(),
|
|
3637
|
+
getClipboardData: (option) => invokeWxApi(() => driver.getClipboardData(), option),
|
|
3531
3638
|
getMenuButtonBoundingClientRect: () => driver.getMenuButtonBoundingClientRect(),
|
|
3532
3639
|
getNetworkType: (option) => invokeWxApi(() => driver.getNetworkType(), option),
|
|
3533
3640
|
getStorageInfo: (option) => invokeWxApi(() => driver.getStorageInfoSync(), option),
|
|
@@ -3553,9 +3660,11 @@ function createHeadlessWx(driver) {
|
|
|
3553
3660
|
nextTick: (callback) => driver.nextTick(callback),
|
|
3554
3661
|
offNetworkStatusChange: (callback) => driver.offNetworkStatusChange(callback),
|
|
3555
3662
|
onNetworkStatusChange: (callback) => driver.onNetworkStatusChange(callback),
|
|
3663
|
+
openDocument: (option) => invokeWxApi(() => driver.openDocument(option), option),
|
|
3556
3664
|
pageScrollTo: (option) => invokeWxApi(() => {
|
|
3557
3665
|
driver.pageScrollTo(option);
|
|
3558
3666
|
}, option),
|
|
3667
|
+
previewImage: (option) => invokeWxApi(() => driver.previewImage(option), option),
|
|
3559
3668
|
reLaunch: (option) => invokeWxApi(() => {
|
|
3560
3669
|
driver.reLaunch(option);
|
|
3561
3670
|
}, option),
|
|
@@ -3563,6 +3672,8 @@ function createHeadlessWx(driver) {
|
|
|
3563
3672
|
driver.redirectTo(option);
|
|
3564
3673
|
}, option),
|
|
3565
3674
|
removeSavedFile: (option) => invokeWxApi(() => driver.removeSavedFile(option), option),
|
|
3675
|
+
saveImageToPhotosAlbum: (option) => invokeWxApi(() => driver.saveImageToPhotosAlbum(option), option),
|
|
3676
|
+
saveVideoToPhotosAlbum: (option) => invokeWxApi(() => driver.saveVideoToPhotosAlbum(option), option),
|
|
3566
3677
|
removeStorage: (option) => invokeWxApi(() => {
|
|
3567
3678
|
driver.removeStorageSync(option.key);
|
|
3568
3679
|
return { errMsg: "removeStorage:ok" };
|
|
@@ -3572,6 +3683,7 @@ function createHeadlessWx(driver) {
|
|
|
3572
3683
|
saveFile: (option) => invokeWxApi(() => driver.saveFile(option), option),
|
|
3573
3684
|
setBackgroundColor: (option) => invokeWxApi(() => driver.setBackgroundColor(option), option),
|
|
3574
3685
|
setBackgroundTextStyle: (option) => invokeWxApi(() => driver.setBackgroundTextStyle(option), option),
|
|
3686
|
+
setClipboardData: (option) => invokeWxApi(() => driver.setClipboardData(option), option),
|
|
3575
3687
|
setStorage: (option) => invokeWxApi(() => {
|
|
3576
3688
|
driver.setStorageSync(option.key, option.data);
|
|
3577
3689
|
return { errMsg: "setStorage:ok" };
|
|
@@ -3591,6 +3703,7 @@ function createHeadlessWx(driver) {
|
|
|
3591
3703
|
showLoading: (option) => invokeWxApi(() => driver.showLoading(option), option),
|
|
3592
3704
|
showModal: (option) => invokeWxApi(() => driver.showModal(option), option),
|
|
3593
3705
|
showToast: (option) => invokeWxApi(() => driver.showToast(option), option),
|
|
3706
|
+
startPullDownRefresh: (option) => invokeWxApi(() => driver.startPullDownRefresh(), option),
|
|
3594
3707
|
stopPullDownRefresh: () => driver.stopPullDownRefresh(),
|
|
3595
3708
|
switchTab: (option) => invokeWxApi(() => {
|
|
3596
3709
|
driver.switchTab(option);
|
|
@@ -3802,10 +3915,11 @@ function loadProject(projectPath) {
|
|
|
3802
3915
|
}
|
|
3803
3916
|
//#endregion
|
|
3804
3917
|
//#region ../../mpcore/packages/simulator/src/view/selectors.ts
|
|
3805
|
-
const WHITESPACE_RE = /\s+/;
|
|
3918
|
+
const WHITESPACE_RE$1 = /\s+/;
|
|
3806
3919
|
const DATA_ATTR_SELECTOR_RE$1 = /^\[data-([^=\]]+)="([^"]*)"\]$/;
|
|
3920
|
+
const COMPOUND_SELECTOR_PART_RE$1 = /#[\w-]+|\.[\w-]+|\[data-[^=\]]+="[^"]*"\]|[A-Za-z][\w-]*/g;
|
|
3807
3921
|
function getClassList(node) {
|
|
3808
|
-
return String(node.attribs?.class ?? "").split(WHITESPACE_RE).map((item) => item.trim()).filter(Boolean);
|
|
3922
|
+
return String(node.attribs?.class ?? "").split(WHITESPACE_RE$1).map((item) => item.trim()).filter(Boolean);
|
|
3809
3923
|
}
|
|
3810
3924
|
function matchesSimpleSelector(node, selector) {
|
|
3811
3925
|
if (node.type !== "tag") return false;
|
|
@@ -3819,6 +3933,15 @@ function matchesSimpleSelector(node, selector) {
|
|
|
3819
3933
|
}
|
|
3820
3934
|
return node.name === selector;
|
|
3821
3935
|
}
|
|
3936
|
+
function parseCompoundSelector$1(selector) {
|
|
3937
|
+
const parts = selector.match(COMPOUND_SELECTOR_PART_RE$1) ?? [];
|
|
3938
|
+
return parts.join("") === selector ? parts : [];
|
|
3939
|
+
}
|
|
3940
|
+
function matchesSelectorToken(node, selector) {
|
|
3941
|
+
const simpleSelectors = parseCompoundSelector$1(selector);
|
|
3942
|
+
if (simpleSelectors.length === 0) return false;
|
|
3943
|
+
return simpleSelectors.every((simpleSelector) => matchesSimpleSelector(node, simpleSelector));
|
|
3944
|
+
}
|
|
3822
3945
|
function collectDescendants(node, into) {
|
|
3823
3946
|
for (const child of node.children ?? []) {
|
|
3824
3947
|
into.push(child);
|
|
@@ -3826,7 +3949,7 @@ function collectDescendants(node, into) {
|
|
|
3826
3949
|
}
|
|
3827
3950
|
}
|
|
3828
3951
|
function querySelectorAll(root, selector) {
|
|
3829
|
-
const parts = selector.trim().split(WHITESPACE_RE).filter(Boolean);
|
|
3952
|
+
const parts = selector.trim().split(WHITESPACE_RE$1).filter(Boolean);
|
|
3830
3953
|
if (parts.length === 0) return [];
|
|
3831
3954
|
let current = [root];
|
|
3832
3955
|
for (const part of parts) {
|
|
@@ -3835,7 +3958,7 @@ function querySelectorAll(root, selector) {
|
|
|
3835
3958
|
const candidates = [];
|
|
3836
3959
|
if (part === "page" && node.type === "tag" && node.name === "page") candidates.push(node);
|
|
3837
3960
|
collectDescendants(node, candidates);
|
|
3838
|
-
for (const candidate of candidates) if (
|
|
3961
|
+
for (const candidate of candidates) if (matchesSelectorToken(candidate, part)) next.push(candidate);
|
|
3839
3962
|
}
|
|
3840
3963
|
current = next;
|
|
3841
3964
|
}
|
|
@@ -3843,22 +3966,22 @@ function querySelectorAll(root, selector) {
|
|
|
3843
3966
|
}
|
|
3844
3967
|
//#endregion
|
|
3845
3968
|
//#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
|
|
3846
|
-
const DATASET_NAME_RE$
|
|
3969
|
+
const DATASET_NAME_RE$2 = /-([a-z])/g;
|
|
3847
3970
|
function escapeText$1(text) {
|
|
3848
3971
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
3849
3972
|
}
|
|
3850
|
-
function toDatasetKey$
|
|
3851
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE$
|
|
3973
|
+
function toDatasetKey$2(attributeName) {
|
|
3974
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$2, (_match, char) => char.toUpperCase());
|
|
3852
3975
|
}
|
|
3853
|
-
function collectDataset$
|
|
3976
|
+
function collectDataset$2(node) {
|
|
3854
3977
|
const dataset = {};
|
|
3855
3978
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
3856
3979
|
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
3857
|
-
dataset[toDatasetKey$
|
|
3980
|
+
dataset[toDatasetKey$2(key)] = value;
|
|
3858
3981
|
}
|
|
3859
3982
|
return dataset;
|
|
3860
3983
|
}
|
|
3861
|
-
function resolveEventBinding(node, eventName) {
|
|
3984
|
+
function resolveEventBinding$1(node, eventName) {
|
|
3862
3985
|
const normalizedEventName = eventName.trim();
|
|
3863
3986
|
if (!normalizedEventName) return null;
|
|
3864
3987
|
const bindingAttrs = [
|
|
@@ -3873,8 +3996,8 @@ function resolveEventBinding(node, eventName) {
|
|
|
3873
3996
|
}
|
|
3874
3997
|
return null;
|
|
3875
3998
|
}
|
|
3876
|
-
function createEventPayload(node, eventName, event) {
|
|
3877
|
-
const dataset = collectDataset$
|
|
3999
|
+
function createEventPayload$1(node, eventName, event) {
|
|
4000
|
+
const dataset = collectDataset$2(node);
|
|
3878
4001
|
const nodeId = node.attribs?.id ?? "";
|
|
3879
4002
|
return {
|
|
3880
4003
|
bubbles: false,
|
|
@@ -3939,7 +4062,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
3939
4062
|
return this.node.attribs?.[name];
|
|
3940
4063
|
}
|
|
3941
4064
|
async dataset() {
|
|
3942
|
-
return collectDataset$
|
|
4065
|
+
return collectDataset$2(this.node);
|
|
3943
4066
|
}
|
|
3944
4067
|
async scope() {
|
|
3945
4068
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
@@ -3969,9 +4092,9 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
3969
4092
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
3970
4093
|
const normalizedEventName = eventName.trim();
|
|
3971
4094
|
if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
|
|
3972
|
-
const methodName = resolveEventBinding(this.node, normalizedEventName);
|
|
4095
|
+
const methodName = resolveEventBinding$1(this.node, normalizedEventName);
|
|
3973
4096
|
if (!methodName) throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
|
|
3974
|
-
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload(this.node, normalizedEventName, event));
|
|
4097
|
+
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload$1(this.node, normalizedEventName, event));
|
|
3975
4098
|
}
|
|
3976
4099
|
async tap(event = {}) {
|
|
3977
4100
|
return await this.trigger("tap", event);
|
|
@@ -4055,18 +4178,18 @@ function renderPageTree(project, page) {
|
|
|
4055
4178
|
}
|
|
4056
4179
|
//#endregion
|
|
4057
4180
|
//#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
|
|
4058
|
-
const DATASET_NAME_RE = /-([a-z])/g;
|
|
4181
|
+
const DATASET_NAME_RE$1 = /-([a-z])/g;
|
|
4059
4182
|
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
4060
4183
|
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
4061
|
-
const NUMERIC_LIKE_VALUE_RE = /-?\d+(?:\.\d+)?/;
|
|
4062
|
-
function toDatasetKey(attributeName) {
|
|
4063
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
4184
|
+
const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
|
|
4185
|
+
function toDatasetKey$1(attributeName) {
|
|
4186
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$1, (_match, char) => char.toUpperCase());
|
|
4064
4187
|
}
|
|
4065
|
-
function collectDataset(node) {
|
|
4188
|
+
function collectDataset$1(node) {
|
|
4066
4189
|
const dataset = {};
|
|
4067
4190
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
4068
4191
|
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
4069
|
-
dataset[toDatasetKey(key)] = value;
|
|
4192
|
+
dataset[toDatasetKey$1(key)] = value;
|
|
4070
4193
|
}
|
|
4071
4194
|
return dataset;
|
|
4072
4195
|
}
|
|
@@ -4095,7 +4218,7 @@ function findNodeByScopeId(root, scopeId) {
|
|
|
4095
4218
|
}
|
|
4096
4219
|
return null;
|
|
4097
4220
|
}
|
|
4098
|
-
function parseStyleDeclarations(styleValue) {
|
|
4221
|
+
function parseStyleDeclarations$1(styleValue) {
|
|
4099
4222
|
const declarations = {};
|
|
4100
4223
|
if (!styleValue) return declarations;
|
|
4101
4224
|
for (const declaration of styleValue.split(";")) {
|
|
@@ -4106,17 +4229,17 @@ function parseStyleDeclarations(styleValue) {
|
|
|
4106
4229
|
}
|
|
4107
4230
|
return declarations;
|
|
4108
4231
|
}
|
|
4109
|
-
function parseNumericLikeValue(value) {
|
|
4232
|
+
function parseNumericLikeValue$1(value) {
|
|
4110
4233
|
if (!value) return 0;
|
|
4111
|
-
const match = value.match(NUMERIC_LIKE_VALUE_RE);
|
|
4234
|
+
const match = value.match(NUMERIC_LIKE_VALUE_RE$1);
|
|
4112
4235
|
return match ? Number(match[0]) : 0;
|
|
4113
4236
|
}
|
|
4114
|
-
function resolveRect(node) {
|
|
4115
|
-
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4116
|
-
const left = parseNumericLikeValue(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4117
|
-
const top = parseNumericLikeValue(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4118
|
-
const width = parseNumericLikeValue(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4119
|
-
const height = parseNumericLikeValue(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4237
|
+
function resolveRect$1(node) {
|
|
4238
|
+
const style = parseStyleDeclarations$1(node.attribs?.style);
|
|
4239
|
+
const left = parseNumericLikeValue$1(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4240
|
+
const top = parseNumericLikeValue$1(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4241
|
+
const width = parseNumericLikeValue$1(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4242
|
+
const height = parseNumericLikeValue$1(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4120
4243
|
return {
|
|
4121
4244
|
bottom: top + height,
|
|
4122
4245
|
height,
|
|
@@ -4126,10 +4249,16 @@ function resolveRect(node) {
|
|
|
4126
4249
|
width
|
|
4127
4250
|
};
|
|
4128
4251
|
}
|
|
4252
|
+
function resolveSelectorScrollTop(root, selector) {
|
|
4253
|
+
const normalizedSelector = selector?.trim();
|
|
4254
|
+
if (!normalizedSelector) return null;
|
|
4255
|
+
const match = querySelectorAll(root, normalizedSelector)[0];
|
|
4256
|
+
return match ? resolveRect$1(match).top : null;
|
|
4257
|
+
}
|
|
4129
4258
|
function resolveScrollOffset(node) {
|
|
4130
4259
|
return {
|
|
4131
|
-
scrollLeft: parseNumericLikeValue(node.attribs?.["data-sim-scroll-left"]),
|
|
4132
|
-
scrollTop: parseNumericLikeValue(node.attribs?.["data-sim-scroll-top"])
|
|
4260
|
+
scrollLeft: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-left"]),
|
|
4261
|
+
scrollTop: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-top"])
|
|
4133
4262
|
};
|
|
4134
4263
|
}
|
|
4135
4264
|
function resolvePropertyValue(node, propertyName) {
|
|
@@ -4137,7 +4266,7 @@ function resolvePropertyValue(node, propertyName) {
|
|
|
4137
4266
|
if (!normalizedPropertyName) return;
|
|
4138
4267
|
if (normalizedPropertyName === "id") return node.attribs?.id ?? "";
|
|
4139
4268
|
if (normalizedPropertyName === "class") return node.attribs?.class ?? "";
|
|
4140
|
-
if (normalizedPropertyName === "dataset") return collectDataset(node);
|
|
4269
|
+
if (normalizedPropertyName === "dataset") return collectDataset$1(node);
|
|
4141
4270
|
return node.attribs?.[normalizedPropertyName];
|
|
4142
4271
|
}
|
|
4143
4272
|
function pickProperties(node, propertyNames) {
|
|
@@ -4146,18 +4275,18 @@ function pickProperties(node, propertyNames) {
|
|
|
4146
4275
|
return result;
|
|
4147
4276
|
}
|
|
4148
4277
|
function pickComputedStyle(node, propertyNames) {
|
|
4149
|
-
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4278
|
+
const style = parseStyleDeclarations$1(node.attribs?.style);
|
|
4150
4279
|
const result = {};
|
|
4151
4280
|
for (const propertyName of propertyNames) result[propertyName] = style[propertyName] ?? "";
|
|
4152
4281
|
return result;
|
|
4153
4282
|
}
|
|
4154
|
-
function resolveFieldsResult(node, fields,
|
|
4283
|
+
function resolveFieldsResult(node, fields, options) {
|
|
4155
4284
|
const result = {};
|
|
4156
4285
|
if (fields.id) result.id = node.attribs?.id ?? "";
|
|
4157
|
-
if (fields.dataset) result.dataset = collectDataset(node);
|
|
4286
|
+
if (fields.dataset) result.dataset = collectDataset$1(node);
|
|
4158
4287
|
if (fields.mark) result.mark = collectMark(node);
|
|
4159
4288
|
if (fields.rect || fields.size) {
|
|
4160
|
-
const rect = resolveRect(node);
|
|
4289
|
+
const rect = resolveRect$1(node);
|
|
4161
4290
|
if (fields.rect) Object.assign(result, rect);
|
|
4162
4291
|
if (fields.size) {
|
|
4163
4292
|
result.width = rect.width;
|
|
@@ -4167,7 +4296,7 @@ function resolveFieldsResult(node, fields, _options) {
|
|
|
4167
4296
|
if (fields.scrollOffset) Object.assign(result, resolveScrollOffset(node));
|
|
4168
4297
|
if (Array.isArray(fields.properties) && fields.properties.length > 0) Object.assign(result, pickProperties(node, fields.properties));
|
|
4169
4298
|
if (Array.isArray(fields.computedStyle) && fields.computedStyle.length > 0) Object.assign(result, pickComputedStyle(node, fields.computedStyle));
|
|
4170
|
-
if (fields.context) result.context = { type: "unsupported-context" };
|
|
4299
|
+
if (fields.context) result.context = options.resolveContext?.(node) ?? { type: "unsupported-context" };
|
|
4171
4300
|
if (fields.node) result.node = { type: node.name ?? "unknown" };
|
|
4172
4301
|
return result;
|
|
4173
4302
|
}
|
|
@@ -4210,6 +4339,749 @@ function resolveSelectorQueryScopeRoot(root, scopeId) {
|
|
|
4210
4339
|
return scopedNode ? createScopedRoot(scopedNode) : root;
|
|
4211
4340
|
}
|
|
4212
4341
|
//#endregion
|
|
4342
|
+
//#region ../../mpcore/packages/simulator/src/view/animation.ts
|
|
4343
|
+
const DEFAULT_STEP_OPTION = {
|
|
4344
|
+
delay: 0,
|
|
4345
|
+
duration: 400,
|
|
4346
|
+
timingFunction: "linear",
|
|
4347
|
+
transformOrigin: "50% 50% 0"
|
|
4348
|
+
};
|
|
4349
|
+
function normalizeStepOption(option) {
|
|
4350
|
+
return {
|
|
4351
|
+
delay: option?.delay ?? DEFAULT_STEP_OPTION.delay,
|
|
4352
|
+
duration: option?.duration ?? DEFAULT_STEP_OPTION.duration,
|
|
4353
|
+
timingFunction: option?.timingFunction ?? DEFAULT_STEP_OPTION.timingFunction,
|
|
4354
|
+
transformOrigin: option?.transformOrigin ?? DEFAULT_STEP_OPTION.transformOrigin
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
function normalizeLength(value) {
|
|
4358
|
+
if (typeof value === "number") return `${value}px`;
|
|
4359
|
+
return value ?? "0px";
|
|
4360
|
+
}
|
|
4361
|
+
function createAction(type, args) {
|
|
4362
|
+
return {
|
|
4363
|
+
args,
|
|
4364
|
+
type
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
function createHeadlessAnimation(defaultOption) {
|
|
4368
|
+
const baseOption = normalizeStepOption(defaultOption);
|
|
4369
|
+
let currentActions = [];
|
|
4370
|
+
let queue = [];
|
|
4371
|
+
let animation;
|
|
4372
|
+
const append = (type, args) => {
|
|
4373
|
+
currentActions.push(createAction(type, args));
|
|
4374
|
+
return animation;
|
|
4375
|
+
};
|
|
4376
|
+
animation = {
|
|
4377
|
+
backgroundColor(value) {
|
|
4378
|
+
return append("backgroundColor", [value]);
|
|
4379
|
+
},
|
|
4380
|
+
bottom(value) {
|
|
4381
|
+
return append("bottom", [normalizeLength(value)]);
|
|
4382
|
+
},
|
|
4383
|
+
export() {
|
|
4384
|
+
const exported = queue.map((item) => ({
|
|
4385
|
+
animates: item.animates.map((action) => ({
|
|
4386
|
+
args: [...action.args],
|
|
4387
|
+
type: action.type
|
|
4388
|
+
})),
|
|
4389
|
+
option: { ...item.option }
|
|
4390
|
+
}));
|
|
4391
|
+
queue = [];
|
|
4392
|
+
currentActions = [];
|
|
4393
|
+
return { actions: exported };
|
|
4394
|
+
},
|
|
4395
|
+
height(value) {
|
|
4396
|
+
return append("height", [normalizeLength(value)]);
|
|
4397
|
+
},
|
|
4398
|
+
left(value) {
|
|
4399
|
+
return append("left", [normalizeLength(value)]);
|
|
4400
|
+
},
|
|
4401
|
+
opacity(value) {
|
|
4402
|
+
return append("opacity", [value]);
|
|
4403
|
+
},
|
|
4404
|
+
right(value) {
|
|
4405
|
+
return append("right", [normalizeLength(value)]);
|
|
4406
|
+
},
|
|
4407
|
+
rotate(angle) {
|
|
4408
|
+
return append("rotate", [`${angle}deg`]);
|
|
4409
|
+
},
|
|
4410
|
+
scale(sx, sy) {
|
|
4411
|
+
return append("scale", [sx, sy ?? sx]);
|
|
4412
|
+
},
|
|
4413
|
+
step(option) {
|
|
4414
|
+
queue.push({
|
|
4415
|
+
animates: currentActions.map((action) => ({
|
|
4416
|
+
args: [...action.args],
|
|
4417
|
+
type: action.type
|
|
4418
|
+
})),
|
|
4419
|
+
option: {
|
|
4420
|
+
...baseOption,
|
|
4421
|
+
...option ?? {}
|
|
4422
|
+
}
|
|
4423
|
+
});
|
|
4424
|
+
currentActions = [];
|
|
4425
|
+
return animation;
|
|
4426
|
+
},
|
|
4427
|
+
top(value) {
|
|
4428
|
+
return append("top", [normalizeLength(value)]);
|
|
4429
|
+
},
|
|
4430
|
+
translate(tx, ty) {
|
|
4431
|
+
return append("translate", [normalizeLength(tx), normalizeLength(ty)]);
|
|
4432
|
+
},
|
|
4433
|
+
translate3d(tx, ty, tz) {
|
|
4434
|
+
return append("translate3d", [
|
|
4435
|
+
normalizeLength(tx),
|
|
4436
|
+
normalizeLength(ty),
|
|
4437
|
+
normalizeLength(tz)
|
|
4438
|
+
]);
|
|
4439
|
+
},
|
|
4440
|
+
translateX(translation) {
|
|
4441
|
+
return append("translateX", [normalizeLength(translation)]);
|
|
4442
|
+
},
|
|
4443
|
+
translateY(translation) {
|
|
4444
|
+
return append("translateY", [normalizeLength(translation)]);
|
|
4445
|
+
},
|
|
4446
|
+
translateZ(translation) {
|
|
4447
|
+
return append("translateZ", [normalizeLength(translation)]);
|
|
4448
|
+
},
|
|
4449
|
+
width(value) {
|
|
4450
|
+
return append("width", [normalizeLength(value)]);
|
|
4451
|
+
}
|
|
4452
|
+
};
|
|
4453
|
+
return animation;
|
|
4454
|
+
}
|
|
4455
|
+
//#endregion
|
|
4456
|
+
//#region ../../mpcore/packages/simulator/src/view/canvasContext.ts
|
|
4457
|
+
function findCanvasNode(root, canvasId) {
|
|
4458
|
+
if (root.type === "tag" && root.name === "canvas" && root.attribs?.["canvas-id"] === canvasId) return root;
|
|
4459
|
+
for (const child of root.children ?? []) {
|
|
4460
|
+
const match = findCanvasNode(child, canvasId);
|
|
4461
|
+
if (match) return match;
|
|
4462
|
+
}
|
|
4463
|
+
return null;
|
|
4464
|
+
}
|
|
4465
|
+
function cloneCall(call) {
|
|
4466
|
+
return {
|
|
4467
|
+
args: call.args.map((arg) => Array.isArray(arg) ? [...arg] : arg),
|
|
4468
|
+
type: call.type
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4471
|
+
function createHeadlessCanvasContext(driver, canvasId, scope) {
|
|
4472
|
+
const defaultState = {
|
|
4473
|
+
fillStyle: "#000000",
|
|
4474
|
+
fontSize: 16,
|
|
4475
|
+
globalAlpha: 1,
|
|
4476
|
+
lineCap: "butt",
|
|
4477
|
+
lineDash: [],
|
|
4478
|
+
lineDashOffset: 0,
|
|
4479
|
+
lineJoin: "miter",
|
|
4480
|
+
miterLimit: 10,
|
|
4481
|
+
lineWidth: 1,
|
|
4482
|
+
shadowBlur: 0,
|
|
4483
|
+
shadowColor: "#000000",
|
|
4484
|
+
shadowOffsetX: 0,
|
|
4485
|
+
shadowOffsetY: 0,
|
|
4486
|
+
strokeStyle: "#000000",
|
|
4487
|
+
textAlign: "start",
|
|
4488
|
+
textBaseline: "alphabetic"
|
|
4489
|
+
};
|
|
4490
|
+
let state = { ...defaultState };
|
|
4491
|
+
let stateStack = [];
|
|
4492
|
+
let drawCalls = [];
|
|
4493
|
+
let snapshot = {
|
|
4494
|
+
canvasId,
|
|
4495
|
+
drawCalls: [],
|
|
4496
|
+
fillStyle: state.fillStyle,
|
|
4497
|
+
fontSize: state.fontSize,
|
|
4498
|
+
globalAlpha: state.globalAlpha,
|
|
4499
|
+
lineCap: state.lineCap,
|
|
4500
|
+
lineDash: [...state.lineDash],
|
|
4501
|
+
lineDashOffset: state.lineDashOffset,
|
|
4502
|
+
lineJoin: state.lineJoin,
|
|
4503
|
+
miterLimit: state.miterLimit,
|
|
4504
|
+
lineWidth: state.lineWidth,
|
|
4505
|
+
reserve: false,
|
|
4506
|
+
shadowBlur: state.shadowBlur,
|
|
4507
|
+
shadowColor: state.shadowColor,
|
|
4508
|
+
shadowOffsetX: state.shadowOffsetX,
|
|
4509
|
+
shadowOffsetY: state.shadowOffsetY,
|
|
4510
|
+
strokeStyle: state.strokeStyle,
|
|
4511
|
+
textAlign: state.textAlign,
|
|
4512
|
+
textBaseline: state.textBaseline
|
|
4513
|
+
};
|
|
4514
|
+
const resolveCanvas = () => {
|
|
4515
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
4516
|
+
if (scopeResolution.kind === "missing") return null;
|
|
4517
|
+
const rendered = driver.renderCurrentPage();
|
|
4518
|
+
const root = scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root;
|
|
4519
|
+
return root ? findCanvasNode(root, canvasId) : null;
|
|
4520
|
+
};
|
|
4521
|
+
const ensureCanvas = () => {
|
|
4522
|
+
const node = resolveCanvas();
|
|
4523
|
+
if (!node) throw new Error(`Canvas with canvas-id "${canvasId}" was not found in headless runtime.`);
|
|
4524
|
+
return node;
|
|
4525
|
+
};
|
|
4526
|
+
const record = (type, args) => {
|
|
4527
|
+
ensureCanvas();
|
|
4528
|
+
drawCalls.push({
|
|
4529
|
+
args,
|
|
4530
|
+
type
|
|
4531
|
+
});
|
|
4532
|
+
};
|
|
4533
|
+
return {
|
|
4534
|
+
arc(x, y, r, sAngle, eAngle, counterclockwise) {
|
|
4535
|
+
record("arc", [
|
|
4536
|
+
x,
|
|
4537
|
+
y,
|
|
4538
|
+
r,
|
|
4539
|
+
sAngle,
|
|
4540
|
+
eAngle,
|
|
4541
|
+
Boolean(counterclockwise)
|
|
4542
|
+
]);
|
|
4543
|
+
},
|
|
4544
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
4545
|
+
record("arcTo", [
|
|
4546
|
+
x1,
|
|
4547
|
+
y1,
|
|
4548
|
+
x2,
|
|
4549
|
+
y2,
|
|
4550
|
+
radius
|
|
4551
|
+
]);
|
|
4552
|
+
},
|
|
4553
|
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
4554
|
+
record("bezierCurveTo", [
|
|
4555
|
+
cp1x,
|
|
4556
|
+
cp1y,
|
|
4557
|
+
cp2x,
|
|
4558
|
+
cp2y,
|
|
4559
|
+
x,
|
|
4560
|
+
y
|
|
4561
|
+
]);
|
|
4562
|
+
},
|
|
4563
|
+
__getSnapshot() {
|
|
4564
|
+
return {
|
|
4565
|
+
canvasId: snapshot.canvasId,
|
|
4566
|
+
drawCalls: snapshot.drawCalls.map(cloneCall),
|
|
4567
|
+
fillStyle: snapshot.fillStyle,
|
|
4568
|
+
fontSize: snapshot.fontSize,
|
|
4569
|
+
globalAlpha: snapshot.globalAlpha,
|
|
4570
|
+
lineCap: snapshot.lineCap,
|
|
4571
|
+
lineDash: [...snapshot.lineDash],
|
|
4572
|
+
lineDashOffset: snapshot.lineDashOffset,
|
|
4573
|
+
lineJoin: snapshot.lineJoin,
|
|
4574
|
+
miterLimit: snapshot.miterLimit,
|
|
4575
|
+
lineWidth: snapshot.lineWidth,
|
|
4576
|
+
reserve: snapshot.reserve,
|
|
4577
|
+
shadowBlur: snapshot.shadowBlur,
|
|
4578
|
+
shadowColor: snapshot.shadowColor,
|
|
4579
|
+
shadowOffsetX: snapshot.shadowOffsetX,
|
|
4580
|
+
shadowOffsetY: snapshot.shadowOffsetY,
|
|
4581
|
+
strokeStyle: snapshot.strokeStyle,
|
|
4582
|
+
textAlign: snapshot.textAlign,
|
|
4583
|
+
textBaseline: snapshot.textBaseline
|
|
4584
|
+
};
|
|
4585
|
+
},
|
|
4586
|
+
beginPath() {
|
|
4587
|
+
record("beginPath", []);
|
|
4588
|
+
},
|
|
4589
|
+
clearRect(x, y, width, height) {
|
|
4590
|
+
record("clearRect", [
|
|
4591
|
+
x,
|
|
4592
|
+
y,
|
|
4593
|
+
width,
|
|
4594
|
+
height
|
|
4595
|
+
]);
|
|
4596
|
+
},
|
|
4597
|
+
clip(fillRule) {
|
|
4598
|
+
record("clip", fillRule == null ? [] : [fillRule]);
|
|
4599
|
+
},
|
|
4600
|
+
closePath() {
|
|
4601
|
+
record("closePath", []);
|
|
4602
|
+
},
|
|
4603
|
+
draw(reserve, callback) {
|
|
4604
|
+
ensureCanvas();
|
|
4605
|
+
snapshot = {
|
|
4606
|
+
canvasId,
|
|
4607
|
+
drawCalls: reserve ? [...snapshot.drawCalls.map(cloneCall), ...drawCalls.map(cloneCall)] : drawCalls.map(cloneCall),
|
|
4608
|
+
fillStyle: state.fillStyle,
|
|
4609
|
+
fontSize: state.fontSize,
|
|
4610
|
+
globalAlpha: state.globalAlpha,
|
|
4611
|
+
lineCap: state.lineCap,
|
|
4612
|
+
lineDash: [...state.lineDash],
|
|
4613
|
+
lineDashOffset: state.lineDashOffset,
|
|
4614
|
+
lineJoin: state.lineJoin,
|
|
4615
|
+
miterLimit: state.miterLimit,
|
|
4616
|
+
lineWidth: state.lineWidth,
|
|
4617
|
+
reserve: Boolean(reserve),
|
|
4618
|
+
shadowBlur: state.shadowBlur,
|
|
4619
|
+
shadowColor: state.shadowColor,
|
|
4620
|
+
shadowOffsetX: state.shadowOffsetX,
|
|
4621
|
+
shadowOffsetY: state.shadowOffsetY,
|
|
4622
|
+
strokeStyle: state.strokeStyle,
|
|
4623
|
+
textAlign: state.textAlign,
|
|
4624
|
+
textBaseline: state.textBaseline
|
|
4625
|
+
};
|
|
4626
|
+
drawCalls = [];
|
|
4627
|
+
state = { ...defaultState };
|
|
4628
|
+
stateStack = [];
|
|
4629
|
+
callback?.();
|
|
4630
|
+
},
|
|
4631
|
+
drawImage(image, ...args) {
|
|
4632
|
+
record("drawImage", [image, ...args]);
|
|
4633
|
+
},
|
|
4634
|
+
fill(fillRule) {
|
|
4635
|
+
record("fill", fillRule == null ? [] : [fillRule]);
|
|
4636
|
+
},
|
|
4637
|
+
fillRect(x, y, width, height) {
|
|
4638
|
+
record("fillRect", [
|
|
4639
|
+
x,
|
|
4640
|
+
y,
|
|
4641
|
+
width,
|
|
4642
|
+
height
|
|
4643
|
+
]);
|
|
4644
|
+
},
|
|
4645
|
+
fillText(text, x, y, maxWidth) {
|
|
4646
|
+
record("fillText", maxWidth == null ? [
|
|
4647
|
+
text,
|
|
4648
|
+
x,
|
|
4649
|
+
y
|
|
4650
|
+
] : [
|
|
4651
|
+
text,
|
|
4652
|
+
x,
|
|
4653
|
+
y,
|
|
4654
|
+
maxWidth
|
|
4655
|
+
]);
|
|
4656
|
+
},
|
|
4657
|
+
lineTo(x, y) {
|
|
4658
|
+
record("lineTo", [x, y]);
|
|
4659
|
+
},
|
|
4660
|
+
measureText(text) {
|
|
4661
|
+
return { width: text.length * state.fontSize * .5 };
|
|
4662
|
+
},
|
|
4663
|
+
moveTo(x, y) {
|
|
4664
|
+
record("moveTo", [x, y]);
|
|
4665
|
+
},
|
|
4666
|
+
quadraticCurveTo(cpx, cpy, x, y) {
|
|
4667
|
+
record("quadraticCurveTo", [
|
|
4668
|
+
cpx,
|
|
4669
|
+
cpy,
|
|
4670
|
+
x,
|
|
4671
|
+
y
|
|
4672
|
+
]);
|
|
4673
|
+
},
|
|
4674
|
+
rect(x, y, width, height) {
|
|
4675
|
+
record("rect", [
|
|
4676
|
+
x,
|
|
4677
|
+
y,
|
|
4678
|
+
width,
|
|
4679
|
+
height
|
|
4680
|
+
]);
|
|
4681
|
+
},
|
|
4682
|
+
restore() {
|
|
4683
|
+
state = stateStack.pop() ?? { ...defaultState };
|
|
4684
|
+
record("restore", []);
|
|
4685
|
+
},
|
|
4686
|
+
rotate(rotate) {
|
|
4687
|
+
record("rotate", [rotate]);
|
|
4688
|
+
},
|
|
4689
|
+
save() {
|
|
4690
|
+
stateStack.push({ ...state });
|
|
4691
|
+
record("save", []);
|
|
4692
|
+
},
|
|
4693
|
+
scale(scaleWidth, scaleHeight) {
|
|
4694
|
+
record("scale", [scaleWidth, scaleHeight]);
|
|
4695
|
+
},
|
|
4696
|
+
setFillStyle(value) {
|
|
4697
|
+
state.fillStyle = String(value);
|
|
4698
|
+
record("setFillStyle", [value]);
|
|
4699
|
+
},
|
|
4700
|
+
setFontSize(fontSize) {
|
|
4701
|
+
state.fontSize = Number(fontSize);
|
|
4702
|
+
record("setFontSize", [fontSize]);
|
|
4703
|
+
},
|
|
4704
|
+
setGlobalAlpha(value) {
|
|
4705
|
+
state.globalAlpha = Number(value);
|
|
4706
|
+
record("setGlobalAlpha", [value]);
|
|
4707
|
+
},
|
|
4708
|
+
setLineCap(value) {
|
|
4709
|
+
state.lineCap = String(value);
|
|
4710
|
+
record("setLineCap", [value]);
|
|
4711
|
+
},
|
|
4712
|
+
setLineDash(pattern, offset) {
|
|
4713
|
+
state.lineDash = pattern.map((item) => Number(item));
|
|
4714
|
+
state.lineDashOffset = offset == null ? 0 : Number(offset);
|
|
4715
|
+
record("setLineDash", offset == null ? [pattern.map((item) => Number(item))] : [pattern.map((item) => Number(item)), Number(offset)]);
|
|
4716
|
+
},
|
|
4717
|
+
setLineJoin(value) {
|
|
4718
|
+
state.lineJoin = String(value);
|
|
4719
|
+
record("setLineJoin", [value]);
|
|
4720
|
+
},
|
|
4721
|
+
setMiterLimit(value) {
|
|
4722
|
+
state.miterLimit = Number(value);
|
|
4723
|
+
record("setMiterLimit", [value]);
|
|
4724
|
+
},
|
|
4725
|
+
setLineWidth(value) {
|
|
4726
|
+
state.lineWidth = Number(value);
|
|
4727
|
+
record("setLineWidth", [value]);
|
|
4728
|
+
},
|
|
4729
|
+
setShadow(offsetX, offsetY, blur, color) {
|
|
4730
|
+
state.shadowOffsetX = Number(offsetX);
|
|
4731
|
+
state.shadowOffsetY = Number(offsetY);
|
|
4732
|
+
state.shadowBlur = Number(blur);
|
|
4733
|
+
state.shadowColor = String(color);
|
|
4734
|
+
record("setShadow", [
|
|
4735
|
+
offsetX,
|
|
4736
|
+
offsetY,
|
|
4737
|
+
blur,
|
|
4738
|
+
color
|
|
4739
|
+
]);
|
|
4740
|
+
},
|
|
4741
|
+
setStrokeStyle(value) {
|
|
4742
|
+
state.strokeStyle = String(value);
|
|
4743
|
+
record("setStrokeStyle", [value]);
|
|
4744
|
+
},
|
|
4745
|
+
setTextAlign(value) {
|
|
4746
|
+
state.textAlign = String(value);
|
|
4747
|
+
record("setTextAlign", [value]);
|
|
4748
|
+
},
|
|
4749
|
+
setTextBaseline(value) {
|
|
4750
|
+
state.textBaseline = String(value);
|
|
4751
|
+
record("setTextBaseline", [value]);
|
|
4752
|
+
},
|
|
4753
|
+
stroke() {
|
|
4754
|
+
record("stroke", []);
|
|
4755
|
+
},
|
|
4756
|
+
strokeRect(x, y, width, height) {
|
|
4757
|
+
record("strokeRect", [
|
|
4758
|
+
x,
|
|
4759
|
+
y,
|
|
4760
|
+
width,
|
|
4761
|
+
height
|
|
4762
|
+
]);
|
|
4763
|
+
},
|
|
4764
|
+
strokeText(text, x, y, maxWidth) {
|
|
4765
|
+
record("strokeText", maxWidth == null ? [
|
|
4766
|
+
text,
|
|
4767
|
+
x,
|
|
4768
|
+
y
|
|
4769
|
+
] : [
|
|
4770
|
+
text,
|
|
4771
|
+
x,
|
|
4772
|
+
y,
|
|
4773
|
+
maxWidth
|
|
4774
|
+
]);
|
|
4775
|
+
},
|
|
4776
|
+
translate(x, y) {
|
|
4777
|
+
record("translate", [x, y]);
|
|
4778
|
+
}
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
//#endregion
|
|
4782
|
+
//#region ../../mpcore/packages/simulator/src/view/intersectionObserver.ts
|
|
4783
|
+
const NUMERIC_LIKE_VALUE_RE = /-?\d+(?:\.\d+)?/;
|
|
4784
|
+
function normalizeMargins(margins) {
|
|
4785
|
+
return {
|
|
4786
|
+
bottom: Number(margins?.bottom) || 0,
|
|
4787
|
+
left: Number(margins?.left) || 0,
|
|
4788
|
+
right: Number(margins?.right) || 0,
|
|
4789
|
+
top: Number(margins?.top) || 0
|
|
4790
|
+
};
|
|
4791
|
+
}
|
|
4792
|
+
function parseStyleDeclarations(styleValue) {
|
|
4793
|
+
const declarations = {};
|
|
4794
|
+
if (!styleValue) return declarations;
|
|
4795
|
+
for (const declaration of styleValue.split(";")) {
|
|
4796
|
+
const [rawProperty, ...rawValueParts] = declaration.split(":");
|
|
4797
|
+
const property = rawProperty?.trim();
|
|
4798
|
+
if (!property) continue;
|
|
4799
|
+
declarations[property] = rawValueParts.join(":").trim();
|
|
4800
|
+
}
|
|
4801
|
+
return declarations;
|
|
4802
|
+
}
|
|
4803
|
+
function parseNumericLikeValue(value) {
|
|
4804
|
+
if (!value) return 0;
|
|
4805
|
+
const match = value.match(NUMERIC_LIKE_VALUE_RE);
|
|
4806
|
+
return match ? Number(match[0]) : 0;
|
|
4807
|
+
}
|
|
4808
|
+
function resolveRect(node) {
|
|
4809
|
+
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4810
|
+
const left = parseNumericLikeValue(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4811
|
+
const top = parseNumericLikeValue(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4812
|
+
const width = parseNumericLikeValue(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4813
|
+
const height = parseNumericLikeValue(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4814
|
+
return {
|
|
4815
|
+
bottom: top + height,
|
|
4816
|
+
height,
|
|
4817
|
+
left,
|
|
4818
|
+
right: left + width,
|
|
4819
|
+
top,
|
|
4820
|
+
width
|
|
4821
|
+
};
|
|
4822
|
+
}
|
|
4823
|
+
function applyMargins(rect, margins) {
|
|
4824
|
+
const top = rect.top - (margins.top ?? 0);
|
|
4825
|
+
const left = rect.left - (margins.left ?? 0);
|
|
4826
|
+
const right = rect.right + (margins.right ?? 0);
|
|
4827
|
+
const bottom = rect.bottom + (margins.bottom ?? 0);
|
|
4828
|
+
return {
|
|
4829
|
+
bottom,
|
|
4830
|
+
height: Math.max(0, bottom - top),
|
|
4831
|
+
left,
|
|
4832
|
+
right,
|
|
4833
|
+
top,
|
|
4834
|
+
width: Math.max(0, right - left)
|
|
4835
|
+
};
|
|
4836
|
+
}
|
|
4837
|
+
function intersectRects(source, target) {
|
|
4838
|
+
const left = Math.max(source.left, target.left);
|
|
4839
|
+
const top = Math.max(source.top, target.top);
|
|
4840
|
+
const right = Math.min(source.right, target.right);
|
|
4841
|
+
const bottom = Math.min(source.bottom, target.bottom);
|
|
4842
|
+
if (right <= left || bottom <= top) return {
|
|
4843
|
+
bottom: top,
|
|
4844
|
+
height: 0,
|
|
4845
|
+
left,
|
|
4846
|
+
right: left,
|
|
4847
|
+
top,
|
|
4848
|
+
width: 0
|
|
4849
|
+
};
|
|
4850
|
+
return {
|
|
4851
|
+
bottom,
|
|
4852
|
+
height: bottom - top,
|
|
4853
|
+
left,
|
|
4854
|
+
right,
|
|
4855
|
+
top,
|
|
4856
|
+
width: right - left
|
|
4857
|
+
};
|
|
4858
|
+
}
|
|
4859
|
+
function resolveViewportRect(windowInfo) {
|
|
4860
|
+
return {
|
|
4861
|
+
bottom: windowInfo.windowHeight,
|
|
4862
|
+
height: windowInfo.windowHeight,
|
|
4863
|
+
left: 0,
|
|
4864
|
+
right: windowInfo.windowWidth,
|
|
4865
|
+
top: 0,
|
|
4866
|
+
width: windowInfo.windowWidth
|
|
4867
|
+
};
|
|
4868
|
+
}
|
|
4869
|
+
function createHeadlessIntersectionObserver(driver, scope, _options) {
|
|
4870
|
+
let active = true;
|
|
4871
|
+
let relativeTarget = {
|
|
4872
|
+
kind: "viewport",
|
|
4873
|
+
margins: normalizeMargins()
|
|
4874
|
+
};
|
|
4875
|
+
const resolveScopedRoot = () => {
|
|
4876
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
4877
|
+
if (scopeResolution.kind === "missing") return null;
|
|
4878
|
+
const rendered = driver.renderCurrentPage();
|
|
4879
|
+
return scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root;
|
|
4880
|
+
};
|
|
4881
|
+
const resolveTargetNode = (selector) => {
|
|
4882
|
+
const scopedRoot = resolveScopedRoot();
|
|
4883
|
+
if (!scopedRoot) return null;
|
|
4884
|
+
return querySelectorAll(scopedRoot, selector)[0] ?? null;
|
|
4885
|
+
};
|
|
4886
|
+
const resolveRelativeRect = () => {
|
|
4887
|
+
if (relativeTarget.kind === "viewport") return applyMargins(resolveViewportRect(driver.getWindowInfo()), relativeTarget.margins);
|
|
4888
|
+
const relativeNode = resolveTargetNode(relativeTarget.selector);
|
|
4889
|
+
if (!relativeNode) return null;
|
|
4890
|
+
return applyMargins(resolveRect(relativeNode), relativeTarget.margins);
|
|
4891
|
+
};
|
|
4892
|
+
return {
|
|
4893
|
+
disconnect() {
|
|
4894
|
+
active = false;
|
|
4895
|
+
},
|
|
4896
|
+
observe(selector, callback) {
|
|
4897
|
+
if (!active) return;
|
|
4898
|
+
const targetNode = resolveTargetNode(selector);
|
|
4899
|
+
const relativeRect = resolveRelativeRect();
|
|
4900
|
+
if (!targetNode || !relativeRect) return;
|
|
4901
|
+
const boundingClientRect = resolveRect(targetNode);
|
|
4902
|
+
const intersectionRect = intersectRects(boundingClientRect, relativeRect);
|
|
4903
|
+
const targetArea = boundingClientRect.width * boundingClientRect.height;
|
|
4904
|
+
const intersectionArea = intersectionRect.width * intersectionRect.height;
|
|
4905
|
+
callback({
|
|
4906
|
+
boundingClientRect,
|
|
4907
|
+
id: targetNode.attribs?.id ?? "",
|
|
4908
|
+
intersectionRatio: targetArea > 0 ? intersectionArea / targetArea : 0,
|
|
4909
|
+
intersectionRect,
|
|
4910
|
+
relativeRect
|
|
4911
|
+
});
|
|
4912
|
+
},
|
|
4913
|
+
relativeTo(selector, margins) {
|
|
4914
|
+
relativeTarget = {
|
|
4915
|
+
kind: "selector",
|
|
4916
|
+
margins: normalizeMargins(margins),
|
|
4917
|
+
selector
|
|
4918
|
+
};
|
|
4919
|
+
return this;
|
|
4920
|
+
},
|
|
4921
|
+
relativeToViewport(margins) {
|
|
4922
|
+
relativeTarget = {
|
|
4923
|
+
kind: "viewport",
|
|
4924
|
+
margins: normalizeMargins(margins)
|
|
4925
|
+
};
|
|
4926
|
+
return this;
|
|
4927
|
+
}
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4930
|
+
//#endregion
|
|
4931
|
+
//#region ../../mpcore/packages/simulator/src/view/mediaQueryObserver.ts
|
|
4932
|
+
function resolveOrientation(windowInfo) {
|
|
4933
|
+
return windowInfo.windowWidth >= windowInfo.windowHeight ? "landscape" : "portrait";
|
|
4934
|
+
}
|
|
4935
|
+
function resolveMatches(descriptor, windowInfo) {
|
|
4936
|
+
if (descriptor.width != null && windowInfo.windowWidth !== descriptor.width) return false;
|
|
4937
|
+
if (descriptor.minWidth != null && windowInfo.windowWidth < descriptor.minWidth) return false;
|
|
4938
|
+
if (descriptor.maxWidth != null && windowInfo.windowWidth > descriptor.maxWidth) return false;
|
|
4939
|
+
if (descriptor.height != null && windowInfo.windowHeight !== descriptor.height) return false;
|
|
4940
|
+
if (descriptor.minHeight != null && windowInfo.windowHeight < descriptor.minHeight) return false;
|
|
4941
|
+
if (descriptor.maxHeight != null && windowInfo.windowHeight > descriptor.maxHeight) return false;
|
|
4942
|
+
if (descriptor.orientation != null && resolveOrientation(windowInfo) !== descriptor.orientation) return false;
|
|
4943
|
+
return true;
|
|
4944
|
+
}
|
|
4945
|
+
function createHeadlessMediaQueryObserver(driver, onDisconnect) {
|
|
4946
|
+
let active = true;
|
|
4947
|
+
let callback;
|
|
4948
|
+
let descriptor;
|
|
4949
|
+
let hasObserved = false;
|
|
4950
|
+
let lastMatches;
|
|
4951
|
+
const emit = (force) => {
|
|
4952
|
+
if (!active || !hasObserved || !callback || !descriptor) return;
|
|
4953
|
+
const matches = resolveMatches(descriptor, driver.getWindowInfo());
|
|
4954
|
+
if (!force && lastMatches === matches) return;
|
|
4955
|
+
lastMatches = matches;
|
|
4956
|
+
callback({ matches });
|
|
4957
|
+
};
|
|
4958
|
+
const observer = {
|
|
4959
|
+
disconnect() {
|
|
4960
|
+
if (!active) return;
|
|
4961
|
+
active = false;
|
|
4962
|
+
hasObserved = false;
|
|
4963
|
+
callback = void 0;
|
|
4964
|
+
descriptor = void 0;
|
|
4965
|
+
onDisconnect?.();
|
|
4966
|
+
},
|
|
4967
|
+
observe(nextDescriptor, nextCallback) {
|
|
4968
|
+
if (!active) return;
|
|
4969
|
+
descriptor = { ...nextDescriptor };
|
|
4970
|
+
callback = nextCallback;
|
|
4971
|
+
hasObserved = true;
|
|
4972
|
+
emit(true);
|
|
4973
|
+
}
|
|
4974
|
+
};
|
|
4975
|
+
return {
|
|
4976
|
+
disconnect: observer.disconnect,
|
|
4977
|
+
notify() {
|
|
4978
|
+
emit(false);
|
|
4979
|
+
},
|
|
4980
|
+
observer
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4983
|
+
//#endregion
|
|
4984
|
+
//#region ../../mpcore/packages/simulator/src/view/videoContext.ts
|
|
4985
|
+
const DATASET_NAME_RE = /-([a-z])/g;
|
|
4986
|
+
function toDatasetKey(attributeName) {
|
|
4987
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
4988
|
+
}
|
|
4989
|
+
function collectDataset(node) {
|
|
4990
|
+
const dataset = {};
|
|
4991
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
4992
|
+
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
4993
|
+
dataset[toDatasetKey(key)] = value;
|
|
4994
|
+
}
|
|
4995
|
+
return dataset;
|
|
4996
|
+
}
|
|
4997
|
+
function resolveEventBinding(node, eventName) {
|
|
4998
|
+
const normalizedEventName = eventName.trim();
|
|
4999
|
+
if (!normalizedEventName) return null;
|
|
5000
|
+
const bindingAttrs = [
|
|
5001
|
+
`bind${normalizedEventName}`,
|
|
5002
|
+
`bind:${normalizedEventName}`,
|
|
5003
|
+
`catch${normalizedEventName}`,
|
|
5004
|
+
`catch:${normalizedEventName}`
|
|
5005
|
+
];
|
|
5006
|
+
for (const attributeName of bindingAttrs) {
|
|
5007
|
+
const methodName = node.attribs?.[attributeName]?.trim();
|
|
5008
|
+
if (methodName) return methodName;
|
|
5009
|
+
}
|
|
5010
|
+
return null;
|
|
5011
|
+
}
|
|
5012
|
+
function createEventPayload(node, eventName, detail) {
|
|
5013
|
+
const dataset = collectDataset(node);
|
|
5014
|
+
const nodeId = node.attribs?.id ?? "";
|
|
5015
|
+
return {
|
|
5016
|
+
bubbles: false,
|
|
5017
|
+
capturePhase: false,
|
|
5018
|
+
composed: false,
|
|
5019
|
+
currentTarget: {
|
|
5020
|
+
dataset,
|
|
5021
|
+
id: nodeId
|
|
5022
|
+
},
|
|
5023
|
+
detail,
|
|
5024
|
+
mark: void 0,
|
|
5025
|
+
target: {
|
|
5026
|
+
dataset,
|
|
5027
|
+
id: nodeId
|
|
5028
|
+
},
|
|
5029
|
+
type: eventName
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
5032
|
+
function createHeadlessVideoContext(driver, videoId, scope) {
|
|
5033
|
+
const state = {
|
|
5034
|
+
currentTime: 0,
|
|
5035
|
+
fullScreen: false,
|
|
5036
|
+
paused: true
|
|
5037
|
+
};
|
|
5038
|
+
const resolveNode = () => {
|
|
5039
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
5040
|
+
if (scopeResolution.kind === "missing") return null;
|
|
5041
|
+
const rendered = driver.renderCurrentPage();
|
|
5042
|
+
return querySelectorAll(scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root, `#${videoId}`)[0] ?? null;
|
|
5043
|
+
};
|
|
5044
|
+
const dispatch = (eventName, detail) => {
|
|
5045
|
+
const node = resolveNode();
|
|
5046
|
+
if (!node) return;
|
|
5047
|
+
const methodName = resolveEventBinding(node, eventName);
|
|
5048
|
+
if (!methodName) return;
|
|
5049
|
+
driver.callScopeMethod(node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload(node, eventName, detail));
|
|
5050
|
+
};
|
|
5051
|
+
return {
|
|
5052
|
+
exitFullScreen() {
|
|
5053
|
+
state.fullScreen = false;
|
|
5054
|
+
dispatch("fullscreenchange", {
|
|
5055
|
+
currentTime: state.currentTime,
|
|
5056
|
+
fullScreen: false
|
|
5057
|
+
});
|
|
5058
|
+
},
|
|
5059
|
+
pause() {
|
|
5060
|
+
state.paused = true;
|
|
5061
|
+
dispatch("pause", { currentTime: state.currentTime });
|
|
5062
|
+
},
|
|
5063
|
+
play() {
|
|
5064
|
+
state.paused = false;
|
|
5065
|
+
dispatch("play", { currentTime: state.currentTime });
|
|
5066
|
+
},
|
|
5067
|
+
requestFullScreen() {
|
|
5068
|
+
state.fullScreen = true;
|
|
5069
|
+
dispatch("fullscreenchange", {
|
|
5070
|
+
currentTime: state.currentTime,
|
|
5071
|
+
fullScreen: true
|
|
5072
|
+
});
|
|
5073
|
+
},
|
|
5074
|
+
seek(position) {
|
|
5075
|
+
state.currentTime = Number.isFinite(position) ? Number(position) : 0;
|
|
5076
|
+
},
|
|
5077
|
+
stop() {
|
|
5078
|
+
state.currentTime = 0;
|
|
5079
|
+
state.paused = true;
|
|
5080
|
+
dispatch("pause", { currentTime: state.currentTime });
|
|
5081
|
+
}
|
|
5082
|
+
};
|
|
5083
|
+
}
|
|
5084
|
+
//#endregion
|
|
4213
5085
|
//#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
|
|
4214
5086
|
function createRequireNotFoundError(request, importer) {
|
|
4215
5087
|
return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
|
|
@@ -4387,9 +5259,228 @@ const DEFAULT_MODAL_CONFIRM_TEXT = "确定";
|
|
|
4387
5259
|
const TRAILING_SLASH_RE = /\/+$/;
|
|
4388
5260
|
const SAVED_FILE_PREFIXES = ["headless://saved/", "headless://wxfile/saved/"];
|
|
4389
5261
|
const textEncoder = new TextEncoder();
|
|
5262
|
+
const IMAGE_EXTENSION_RE = /\.([^.?#/]+)(?:[?#].*)?$/;
|
|
5263
|
+
const LEADING_DOT_RE = /^\.+/;
|
|
5264
|
+
const IMAGE_FILE_EXTENSIONS = new Set([
|
|
5265
|
+
"bmp",
|
|
5266
|
+
"gif",
|
|
5267
|
+
"heic",
|
|
5268
|
+
"jpeg",
|
|
5269
|
+
"jpg",
|
|
5270
|
+
"png",
|
|
5271
|
+
"webp"
|
|
5272
|
+
]);
|
|
5273
|
+
const VIDEO_FILE_EXTENSIONS = new Set([
|
|
5274
|
+
"avi",
|
|
5275
|
+
"m4v",
|
|
5276
|
+
"mov",
|
|
5277
|
+
"mp4",
|
|
5278
|
+
"mpeg",
|
|
5279
|
+
"mpg",
|
|
5280
|
+
"webm"
|
|
5281
|
+
]);
|
|
4390
5282
|
function byteLength(input) {
|
|
4391
5283
|
return textEncoder.encode(input).byteLength;
|
|
4392
5284
|
}
|
|
5285
|
+
function rotateLeft(value, bits) {
|
|
5286
|
+
return (value << bits | value >>> 32 - bits) >>> 0;
|
|
5287
|
+
}
|
|
5288
|
+
function toHexWordLittleEndian(value) {
|
|
5289
|
+
let output = "";
|
|
5290
|
+
for (let index = 0; index < 4; index += 1) output += (value >>> index * 8 & 255).toString(16).padStart(2, "0");
|
|
5291
|
+
return output;
|
|
5292
|
+
}
|
|
5293
|
+
function toHexWordBigEndian(value) {
|
|
5294
|
+
return value.toString(16).padStart(8, "0");
|
|
5295
|
+
}
|
|
5296
|
+
function computeMd5Digest(input) {
|
|
5297
|
+
const bytes = Array.from(textEncoder.encode(input));
|
|
5298
|
+
const bitLength = bytes.length * 8;
|
|
5299
|
+
const lowBits = bitLength >>> 0;
|
|
5300
|
+
const highBits = Math.floor(bitLength / 4294967296) >>> 0;
|
|
5301
|
+
bytes.push(128);
|
|
5302
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
5303
|
+
for (let index = 0; index < 4; index += 1) bytes.push(lowBits >>> index * 8 & 255);
|
|
5304
|
+
for (let index = 0; index < 4; index += 1) bytes.push(highBits >>> index * 8 & 255);
|
|
5305
|
+
const shifts = [
|
|
5306
|
+
7,
|
|
5307
|
+
12,
|
|
5308
|
+
17,
|
|
5309
|
+
22,
|
|
5310
|
+
7,
|
|
5311
|
+
12,
|
|
5312
|
+
17,
|
|
5313
|
+
22,
|
|
5314
|
+
7,
|
|
5315
|
+
12,
|
|
5316
|
+
17,
|
|
5317
|
+
22,
|
|
5318
|
+
7,
|
|
5319
|
+
12,
|
|
5320
|
+
17,
|
|
5321
|
+
22,
|
|
5322
|
+
5,
|
|
5323
|
+
9,
|
|
5324
|
+
14,
|
|
5325
|
+
20,
|
|
5326
|
+
5,
|
|
5327
|
+
9,
|
|
5328
|
+
14,
|
|
5329
|
+
20,
|
|
5330
|
+
5,
|
|
5331
|
+
9,
|
|
5332
|
+
14,
|
|
5333
|
+
20,
|
|
5334
|
+
5,
|
|
5335
|
+
9,
|
|
5336
|
+
14,
|
|
5337
|
+
20,
|
|
5338
|
+
4,
|
|
5339
|
+
11,
|
|
5340
|
+
16,
|
|
5341
|
+
23,
|
|
5342
|
+
4,
|
|
5343
|
+
11,
|
|
5344
|
+
16,
|
|
5345
|
+
23,
|
|
5346
|
+
4,
|
|
5347
|
+
11,
|
|
5348
|
+
16,
|
|
5349
|
+
23,
|
|
5350
|
+
4,
|
|
5351
|
+
11,
|
|
5352
|
+
16,
|
|
5353
|
+
23,
|
|
5354
|
+
6,
|
|
5355
|
+
10,
|
|
5356
|
+
15,
|
|
5357
|
+
21,
|
|
5358
|
+
6,
|
|
5359
|
+
10,
|
|
5360
|
+
15,
|
|
5361
|
+
21,
|
|
5362
|
+
6,
|
|
5363
|
+
10,
|
|
5364
|
+
15,
|
|
5365
|
+
21,
|
|
5366
|
+
6,
|
|
5367
|
+
10,
|
|
5368
|
+
15,
|
|
5369
|
+
21
|
|
5370
|
+
];
|
|
5371
|
+
const table = Array.from({ length: 64 }, (_, index) => Math.floor(Math.abs(Math.sin(index + 1)) * 4294967296) >>> 0);
|
|
5372
|
+
let a0 = 1732584193;
|
|
5373
|
+
let b0 = 4023233417;
|
|
5374
|
+
let c0 = 2562383102;
|
|
5375
|
+
let d0 = 271733878;
|
|
5376
|
+
for (let chunkOffset = 0; chunkOffset < bytes.length; chunkOffset += 64) {
|
|
5377
|
+
const words = Array.from({ length: 16 }, (_, index) => {
|
|
5378
|
+
const wordOffset = chunkOffset + index * 4;
|
|
5379
|
+
return (bytes[wordOffset] | bytes[wordOffset + 1] << 8 | bytes[wordOffset + 2] << 16 | bytes[wordOffset + 3] << 24) >>> 0;
|
|
5380
|
+
});
|
|
5381
|
+
let a = a0;
|
|
5382
|
+
let b = b0;
|
|
5383
|
+
let c = c0;
|
|
5384
|
+
let d = d0;
|
|
5385
|
+
for (let index = 0; index < 64; index += 1) {
|
|
5386
|
+
let f = 0;
|
|
5387
|
+
let g = 0;
|
|
5388
|
+
if (index < 16) {
|
|
5389
|
+
f = (b & c | ~b & d) >>> 0;
|
|
5390
|
+
g = index;
|
|
5391
|
+
} else if (index < 32) {
|
|
5392
|
+
f = (d & b | ~d & c) >>> 0;
|
|
5393
|
+
g = (5 * index + 1) % 16;
|
|
5394
|
+
} else if (index < 48) {
|
|
5395
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5396
|
+
g = (3 * index + 5) % 16;
|
|
5397
|
+
} else {
|
|
5398
|
+
f = (c ^ (b | ~d)) >>> 0;
|
|
5399
|
+
g = 7 * index % 16;
|
|
5400
|
+
}
|
|
5401
|
+
const next = a + f + table[index] + words[g] >>> 0;
|
|
5402
|
+
a = d;
|
|
5403
|
+
d = c;
|
|
5404
|
+
c = b;
|
|
5405
|
+
b = b + rotateLeft(next, shifts[index]) >>> 0;
|
|
5406
|
+
}
|
|
5407
|
+
a0 = a0 + a >>> 0;
|
|
5408
|
+
b0 = b0 + b >>> 0;
|
|
5409
|
+
c0 = c0 + c >>> 0;
|
|
5410
|
+
d0 = d0 + d >>> 0;
|
|
5411
|
+
}
|
|
5412
|
+
return [
|
|
5413
|
+
toHexWordLittleEndian(a0),
|
|
5414
|
+
toHexWordLittleEndian(b0),
|
|
5415
|
+
toHexWordLittleEndian(c0),
|
|
5416
|
+
toHexWordLittleEndian(d0)
|
|
5417
|
+
].join("");
|
|
5418
|
+
}
|
|
5419
|
+
function computeSha1Digest(input) {
|
|
5420
|
+
const bytes = Array.from(textEncoder.encode(input));
|
|
5421
|
+
const bitLength = bytes.length * 8;
|
|
5422
|
+
const lowBits = bitLength >>> 0;
|
|
5423
|
+
const highBits = Math.floor(bitLength / 4294967296) >>> 0;
|
|
5424
|
+
bytes.push(128);
|
|
5425
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
5426
|
+
for (let index = 3; index >= 0; index -= 1) bytes.push(highBits >>> index * 8 & 255);
|
|
5427
|
+
for (let index = 3; index >= 0; index -= 1) bytes.push(lowBits >>> index * 8 & 255);
|
|
5428
|
+
let h0 = 1732584193;
|
|
5429
|
+
let h1 = 4023233417;
|
|
5430
|
+
let h2 = 2562383102;
|
|
5431
|
+
let h3 = 271733878;
|
|
5432
|
+
let h4 = 3285377520;
|
|
5433
|
+
for (let chunkOffset = 0; chunkOffset < bytes.length; chunkOffset += 64) {
|
|
5434
|
+
const words = Array.from({ length: 80 }, (_, index) => {
|
|
5435
|
+
if (index < 16) {
|
|
5436
|
+
const wordOffset = chunkOffset + index * 4;
|
|
5437
|
+
return (bytes[wordOffset] << 24 | bytes[wordOffset + 1] << 16 | bytes[wordOffset + 2] << 8 | bytes[wordOffset + 3]) >>> 0;
|
|
5438
|
+
}
|
|
5439
|
+
return 0;
|
|
5440
|
+
});
|
|
5441
|
+
for (let index = 16; index < 80; index += 1) words[index] = rotateLeft(words[index - 3] ^ words[index - 8] ^ words[index - 14] ^ words[index - 16], 1);
|
|
5442
|
+
let a = h0;
|
|
5443
|
+
let b = h1;
|
|
5444
|
+
let c = h2;
|
|
5445
|
+
let d = h3;
|
|
5446
|
+
let e = h4;
|
|
5447
|
+
for (let index = 0; index < 80; index += 1) {
|
|
5448
|
+
let f = 0;
|
|
5449
|
+
let k = 0;
|
|
5450
|
+
if (index < 20) {
|
|
5451
|
+
f = (b & c | ~b & d) >>> 0;
|
|
5452
|
+
k = 1518500249;
|
|
5453
|
+
} else if (index < 40) {
|
|
5454
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5455
|
+
k = 1859775393;
|
|
5456
|
+
} else if (index < 60) {
|
|
5457
|
+
f = (b & c | b & d | c & d) >>> 0;
|
|
5458
|
+
k = 2400959708;
|
|
5459
|
+
} else {
|
|
5460
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5461
|
+
k = 3395469782;
|
|
5462
|
+
}
|
|
5463
|
+
const next = rotateLeft(a, 5) + f + e + k + words[index] >>> 0;
|
|
5464
|
+
e = d;
|
|
5465
|
+
d = c;
|
|
5466
|
+
c = rotateLeft(b, 30);
|
|
5467
|
+
b = a;
|
|
5468
|
+
a = next;
|
|
5469
|
+
}
|
|
5470
|
+
h0 = h0 + a >>> 0;
|
|
5471
|
+
h1 = h1 + b >>> 0;
|
|
5472
|
+
h2 = h2 + c >>> 0;
|
|
5473
|
+
h3 = h3 + d >>> 0;
|
|
5474
|
+
h4 = h4 + e >>> 0;
|
|
5475
|
+
}
|
|
5476
|
+
return [
|
|
5477
|
+
toHexWordBigEndian(h0),
|
|
5478
|
+
toHexWordBigEndian(h1),
|
|
5479
|
+
toHexWordBigEndian(h2),
|
|
5480
|
+
toHexWordBigEndian(h3),
|
|
5481
|
+
toHexWordBigEndian(h4)
|
|
5482
|
+
].join("");
|
|
5483
|
+
}
|
|
4393
5484
|
function cloneShareMenuSnapshot(snapshot) {
|
|
4394
5485
|
return {
|
|
4395
5486
|
isUpdatableMessage: snapshot.isUpdatableMessage,
|
|
@@ -4398,6 +5489,118 @@ function cloneShareMenuSnapshot(snapshot) {
|
|
|
4398
5489
|
withShareTicket: snapshot.withShareTicket
|
|
4399
5490
|
};
|
|
4400
5491
|
}
|
|
5492
|
+
function normalizeImageType(type) {
|
|
5493
|
+
const normalized = (type ?? "").trim().toLowerCase();
|
|
5494
|
+
if (normalized === "jpg") return "jpeg";
|
|
5495
|
+
return normalized;
|
|
5496
|
+
}
|
|
5497
|
+
function normalizeFileExtension(extension) {
|
|
5498
|
+
return extension.trim().replace(LEADING_DOT_RE, "").toLowerCase();
|
|
5499
|
+
}
|
|
5500
|
+
function inferDocumentFileType(filePath) {
|
|
5501
|
+
const extensionMatch = filePath.match(IMAGE_EXTENSION_RE);
|
|
5502
|
+
return extensionMatch ? normalizeFileExtension(extensionMatch[1]) : "unknown";
|
|
5503
|
+
}
|
|
5504
|
+
function isImageFileExtension(extension) {
|
|
5505
|
+
return IMAGE_FILE_EXTENSIONS.has(normalizeFileExtension(extension));
|
|
5506
|
+
}
|
|
5507
|
+
function isVideoFileExtension(extension) {
|
|
5508
|
+
return VIDEO_FILE_EXTENSIONS.has(normalizeFileExtension(extension));
|
|
5509
|
+
}
|
|
5510
|
+
function resolveMessageFileExtensions(option) {
|
|
5511
|
+
const filteredExtensions = (Array.isArray(option.extension) ? option.extension.filter((item) => typeof item === "string").map(normalizeFileExtension).filter(Boolean) : []).filter((extension) => {
|
|
5512
|
+
if (option.type === "image") return isImageFileExtension(extension);
|
|
5513
|
+
if (option.type === "video") return isVideoFileExtension(extension);
|
|
5514
|
+
if (option.type === "file") return !isImageFileExtension(extension) && !isVideoFileExtension(extension);
|
|
5515
|
+
return true;
|
|
5516
|
+
});
|
|
5517
|
+
if (filteredExtensions.length > 0) return filteredExtensions;
|
|
5518
|
+
if (option.type === "image") return ["png", "jpg"];
|
|
5519
|
+
if (option.type === "video") return ["mp4"];
|
|
5520
|
+
if (option.type === "file") return [
|
|
5521
|
+
"txt",
|
|
5522
|
+
"pdf",
|
|
5523
|
+
"doc"
|
|
5524
|
+
];
|
|
5525
|
+
return [
|
|
5526
|
+
"png",
|
|
5527
|
+
"pdf",
|
|
5528
|
+
"mp4"
|
|
5529
|
+
];
|
|
5530
|
+
}
|
|
5531
|
+
function inferImageType(filePath, fileContent) {
|
|
5532
|
+
const extensionMatch = filePath.match(IMAGE_EXTENSION_RE);
|
|
5533
|
+
if (extensionMatch) return normalizeImageType(extensionMatch[1]);
|
|
5534
|
+
try {
|
|
5535
|
+
return normalizeImageType(JSON.parse(fileContent)?.config?.fileType) || "png";
|
|
5536
|
+
} catch {
|
|
5537
|
+
return "unknown";
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
function inferImageSize(fileContent) {
|
|
5541
|
+
try {
|
|
5542
|
+
const config = JSON.parse(fileContent)?.config;
|
|
5543
|
+
const destHeight = config && "destHeight" in config ? config.destHeight : void 0;
|
|
5544
|
+
const destWidth = config && "destWidth" in config ? config.destWidth : void 0;
|
|
5545
|
+
return {
|
|
5546
|
+
height: destHeight ?? config?.height ?? 0,
|
|
5547
|
+
width: destWidth ?? config?.width ?? 0
|
|
5548
|
+
};
|
|
5549
|
+
} catch {
|
|
5550
|
+
return {
|
|
5551
|
+
height: 0,
|
|
5552
|
+
width: 0
|
|
5553
|
+
};
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
function buildImageTempFilePayload(fileType, width, height, metadata = {}) {
|
|
5557
|
+
return JSON.stringify({ config: {
|
|
5558
|
+
fileType,
|
|
5559
|
+
height,
|
|
5560
|
+
quality: metadata.quality,
|
|
5561
|
+
sizeType: metadata.sizeType ?? [],
|
|
5562
|
+
sourceType: metadata.sourceType ?? [],
|
|
5563
|
+
width
|
|
5564
|
+
} });
|
|
5565
|
+
}
|
|
5566
|
+
function buildVideoTempFilePayload(width, height, duration, metadata = {}) {
|
|
5567
|
+
return JSON.stringify({ config: {
|
|
5568
|
+
bitrate: metadata.bitrate ?? 1500,
|
|
5569
|
+
compressed: metadata.compressed ?? true,
|
|
5570
|
+
duration,
|
|
5571
|
+
fps: metadata.fps ?? 30,
|
|
5572
|
+
height,
|
|
5573
|
+
maxDuration: metadata.maxDuration ?? duration,
|
|
5574
|
+
sourceType: metadata.sourceType ?? [],
|
|
5575
|
+
width
|
|
5576
|
+
} });
|
|
5577
|
+
}
|
|
5578
|
+
function inferVideoInfo(filePath, fileContent) {
|
|
5579
|
+
const size = byteLength(fileContent);
|
|
5580
|
+
const type = filePath.match(IMAGE_EXTENSION_RE)?.[1]?.toLowerCase() ?? "mp4";
|
|
5581
|
+
try {
|
|
5582
|
+
const payload = JSON.parse(fileContent);
|
|
5583
|
+
return {
|
|
5584
|
+
bitrate: payload.config.bitrate ?? 1500,
|
|
5585
|
+
duration: payload.config.duration ?? 0,
|
|
5586
|
+
fps: payload.config.fps ?? 30,
|
|
5587
|
+
height: payload.config.height ?? 0,
|
|
5588
|
+
size,
|
|
5589
|
+
type,
|
|
5590
|
+
width: payload.config.width ?? 0
|
|
5591
|
+
};
|
|
5592
|
+
} catch {
|
|
5593
|
+
return {
|
|
5594
|
+
bitrate: 1500,
|
|
5595
|
+
duration: 0,
|
|
5596
|
+
fps: 30,
|
|
5597
|
+
height: 0,
|
|
5598
|
+
size,
|
|
5599
|
+
type,
|
|
5600
|
+
width: 0
|
|
5601
|
+
};
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
4401
5604
|
function createNetworkSnapshot(networkType) {
|
|
4402
5605
|
return {
|
|
4403
5606
|
isConnected: networkType !== "none",
|
|
@@ -4548,6 +5751,9 @@ function createHeadlessWxState() {
|
|
|
4548
5751
|
let fileId = 0;
|
|
4549
5752
|
let loading = null;
|
|
4550
5753
|
let networkType = "wifi";
|
|
5754
|
+
let previewImage = null;
|
|
5755
|
+
let openedDocument = null;
|
|
5756
|
+
let clipboardData = "";
|
|
4551
5757
|
let shareMenu = {
|
|
4552
5758
|
isUpdatableMessage: false,
|
|
4553
5759
|
menus: [],
|
|
@@ -4749,6 +5955,16 @@ function createHeadlessWxState() {
|
|
|
4749
5955
|
size: savedFile.size
|
|
4750
5956
|
};
|
|
4751
5957
|
};
|
|
5958
|
+
const getFileInfo = (option) => {
|
|
5959
|
+
const normalizedPath = normalizeFsPath(option.filePath);
|
|
5960
|
+
const fileContent = files.get(normalizedPath);
|
|
5961
|
+
if (fileContent == null) throw new Error(`getFileInfo:fail no such file or directory, stat '${normalizedPath}'`);
|
|
5962
|
+
return {
|
|
5963
|
+
digest: (option.digestAlgorithm === "sha1" ? "sha1" : "md5") === "sha1" ? computeSha1Digest(fileContent) : computeMd5Digest(fileContent),
|
|
5964
|
+
errMsg: "getFileInfo:ok",
|
|
5965
|
+
size: byteLength(fileContent)
|
|
5966
|
+
};
|
|
5967
|
+
};
|
|
4752
5968
|
const removeSavedFile = (filePath) => {
|
|
4753
5969
|
const normalizedPath = normalizeFsPath(filePath);
|
|
4754
5970
|
if (!savedFiles.has(normalizedPath)) throw new Error(`removeSavedFile:fail no such file or directory, unlink '${normalizedPath}'`);
|
|
@@ -4999,18 +6215,191 @@ function createHeadlessWxState() {
|
|
|
4999
6215
|
getSavedFileInfo(option) {
|
|
5000
6216
|
return getSavedFileInfo(option.filePath);
|
|
5001
6217
|
},
|
|
6218
|
+
getFileInfo(option) {
|
|
6219
|
+
return getFileInfo(option);
|
|
6220
|
+
},
|
|
5002
6221
|
getSavedFileList() {
|
|
5003
6222
|
return getSavedFileList();
|
|
5004
6223
|
},
|
|
5005
6224
|
getFileText(filePath) {
|
|
5006
6225
|
return files.get(filePath) ?? null;
|
|
5007
6226
|
},
|
|
6227
|
+
createTempFile(fileContent, preferredPath) {
|
|
6228
|
+
const tempFilePath = allocateFilePath("temp", preferredPath);
|
|
6229
|
+
ensureDirectoryTree(tempFilePath);
|
|
6230
|
+
files.set(tempFilePath, String(fileContent));
|
|
6231
|
+
return tempFilePath;
|
|
6232
|
+
},
|
|
6233
|
+
chooseImage(option) {
|
|
6234
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(9, Math.trunc(option.count ?? 1))) : 9;
|
|
6235
|
+
const sizeType = Array.isArray(option.sizeType) && option.sizeType.length > 0 ? option.sizeType.filter((item) => typeof item === "string") : ["original"];
|
|
6236
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6237
|
+
const fileType = sizeType.includes("compressed") ? "jpg" : "png";
|
|
6238
|
+
const tempFiles = Array.from({ length: count }, (_, index) => {
|
|
6239
|
+
const payload = buildImageTempFilePayload(fileType, 160 + index * 12, 120 + index * 8, {
|
|
6240
|
+
sizeType: [...sizeType],
|
|
6241
|
+
sourceType: [...sourceType]
|
|
6242
|
+
});
|
|
6243
|
+
return {
|
|
6244
|
+
path: this.createTempFile(payload, `headless://wxfile/temp/chosen-image-${String(index + 1).padStart(2, "0")}.${fileType}`),
|
|
6245
|
+
size: payload.length
|
|
6246
|
+
};
|
|
6247
|
+
});
|
|
6248
|
+
return {
|
|
6249
|
+
errMsg: "chooseImage:ok",
|
|
6250
|
+
tempFilePaths: tempFiles.map((item) => item.path),
|
|
6251
|
+
tempFiles
|
|
6252
|
+
};
|
|
6253
|
+
},
|
|
6254
|
+
chooseMessageFile(option) {
|
|
6255
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(100, Math.trunc(option.count ?? 1))) : 1;
|
|
6256
|
+
const extensions = resolveMessageFileExtensions(option);
|
|
6257
|
+
return {
|
|
6258
|
+
errMsg: "chooseMessageFile:ok",
|
|
6259
|
+
tempFiles: Array.from({ length: count }, (_, index) => {
|
|
6260
|
+
const extension = extensions[index % extensions.length];
|
|
6261
|
+
const sequence = String(index + 1).padStart(2, "0");
|
|
6262
|
+
const fileName = `message-file-${sequence}.${extension}`;
|
|
6263
|
+
let fileContent = `headless message file ${fileName}`;
|
|
6264
|
+
if (isImageFileExtension(extension)) fileContent = buildImageTempFilePayload(extension, 160 + index * 12, 120 + index * 8, { sourceType: ["message"] });
|
|
6265
|
+
else if (isVideoFileExtension(extension)) fileContent = buildVideoTempFilePayload(640, 360, 18 + index, {
|
|
6266
|
+
compressed: true,
|
|
6267
|
+
sourceType: ["message"]
|
|
6268
|
+
});
|
|
6269
|
+
return {
|
|
6270
|
+
name: fileName,
|
|
6271
|
+
path: this.createTempFile(fileContent, `headless://wxfile/temp/chosen-message-file-${sequence}.${extension}`),
|
|
6272
|
+
size: byteLength(fileContent),
|
|
6273
|
+
time: 171e10 + index * 1e3,
|
|
6274
|
+
type: extension
|
|
6275
|
+
};
|
|
6276
|
+
})
|
|
6277
|
+
};
|
|
6278
|
+
},
|
|
6279
|
+
chooseMedia(option) {
|
|
6280
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(9, Math.trunc(option.count ?? 1))) : 1;
|
|
6281
|
+
const mediaTypes = Array.isArray(option.mediaType) && option.mediaType.length > 0 ? option.mediaType.filter((item) => item === "image" || item === "video") : ["image"];
|
|
6282
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6283
|
+
const sizeType = Array.isArray(option.sizeType) && option.sizeType.length > 0 ? option.sizeType.filter((item) => typeof item === "string") : ["compressed"];
|
|
6284
|
+
const maxDuration = Number.isFinite(option.maxDuration) ? Math.max(1, Math.trunc(option.maxDuration ?? 60)) : 60;
|
|
6285
|
+
return {
|
|
6286
|
+
errMsg: "chooseMedia:ok",
|
|
6287
|
+
tempFiles: Array.from({ length: count }, (_, index) => {
|
|
6288
|
+
if (mediaTypes[index % mediaTypes.length] === "video") {
|
|
6289
|
+
const duration = Math.min(maxDuration, 18 + index);
|
|
6290
|
+
const width = 640;
|
|
6291
|
+
const height = 360;
|
|
6292
|
+
const payload = buildVideoTempFilePayload(width, height, duration, {
|
|
6293
|
+
compressed: sizeType.includes("compressed"),
|
|
6294
|
+
maxDuration,
|
|
6295
|
+
sourceType: [...sourceType]
|
|
6296
|
+
});
|
|
6297
|
+
const tempFilePath = this.createTempFile(payload, `headless://wxfile/temp/chosen-media-video-${String(index + 1).padStart(2, "0")}.mp4`);
|
|
6298
|
+
const thumbPayload = buildImageTempFilePayload("jpg", 160, 90, {
|
|
6299
|
+
sizeType: ["compressed"],
|
|
6300
|
+
sourceType: [...sourceType]
|
|
6301
|
+
});
|
|
6302
|
+
const thumbTempFilePath = this.createTempFile(thumbPayload, `headless://wxfile/temp/chosen-media-video-thumb-${String(index + 1).padStart(2, "0")}.jpg`);
|
|
6303
|
+
return {
|
|
6304
|
+
duration,
|
|
6305
|
+
fileType: "video",
|
|
6306
|
+
height,
|
|
6307
|
+
size: payload.length,
|
|
6308
|
+
tempFilePath,
|
|
6309
|
+
thumbTempFilePath,
|
|
6310
|
+
width
|
|
6311
|
+
};
|
|
6312
|
+
}
|
|
6313
|
+
const width = 160 + index * 12;
|
|
6314
|
+
const height = 120 + index * 8;
|
|
6315
|
+
const fileType = sizeType.includes("compressed") ? "jpg" : "png";
|
|
6316
|
+
const payload = buildImageTempFilePayload(fileType, width, height, {
|
|
6317
|
+
sizeType: [...sizeType],
|
|
6318
|
+
sourceType: [...sourceType]
|
|
6319
|
+
});
|
|
6320
|
+
return {
|
|
6321
|
+
fileType: "image",
|
|
6322
|
+
height,
|
|
6323
|
+
size: payload.length,
|
|
6324
|
+
tempFilePath: this.createTempFile(payload, `headless://wxfile/temp/chosen-media-image-${String(index + 1).padStart(2, "0")}.${fileType}`),
|
|
6325
|
+
width
|
|
6326
|
+
};
|
|
6327
|
+
}),
|
|
6328
|
+
type: mediaTypes.length > 1 ? "mix" : mediaTypes[0] ?? "image"
|
|
6329
|
+
};
|
|
6330
|
+
},
|
|
6331
|
+
chooseVideo(option) {
|
|
6332
|
+
const compressed = option.compressed !== false;
|
|
6333
|
+
const maxDuration = Number.isFinite(option.maxDuration) ? Math.max(1, Math.trunc(option.maxDuration ?? 60)) : 60;
|
|
6334
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6335
|
+
const payload = JSON.stringify({ config: {
|
|
6336
|
+
compressed,
|
|
6337
|
+
duration: Math.min(maxDuration, compressed ? 18 : 36),
|
|
6338
|
+
height: compressed ? 360 : 720,
|
|
6339
|
+
maxDuration,
|
|
6340
|
+
sourceType: [...sourceType],
|
|
6341
|
+
width: compressed ? 640 : 1280
|
|
6342
|
+
} });
|
|
6343
|
+
const tempFilePath = this.createTempFile(payload, "headless://wxfile/temp/chosen-video-01.mp4");
|
|
6344
|
+
return {
|
|
6345
|
+
duration: Math.min(maxDuration, compressed ? 18 : 36),
|
|
6346
|
+
errMsg: "chooseVideo:ok",
|
|
6347
|
+
height: compressed ? 360 : 720,
|
|
6348
|
+
size: payload.length,
|
|
6349
|
+
tempFilePath,
|
|
6350
|
+
width: compressed ? 640 : 1280
|
|
6351
|
+
};
|
|
6352
|
+
},
|
|
6353
|
+
compressImage(option) {
|
|
6354
|
+
const fileContent = files.get(option.src);
|
|
6355
|
+
if (fileContent == null) throw new Error(`compressImage:fail file not found: ${option.src}`);
|
|
6356
|
+
const sourceSize = inferImageSize(fileContent);
|
|
6357
|
+
const sourceType = inferImageType(option.src, fileContent);
|
|
6358
|
+
const width = Number.isFinite(option.compressedWidth) ? Math.max(1, Math.trunc(option.compressedWidth ?? sourceSize.width)) : Math.max(1, sourceSize.width);
|
|
6359
|
+
const height = Number.isFinite(option.compressedHeight) ? Math.max(1, Math.trunc(option.compressedHeight ?? sourceSize.height)) : Math.max(1, sourceSize.height);
|
|
6360
|
+
const normalizedType = sourceType === "unknown" ? "jpg" : sourceType;
|
|
6361
|
+
const fileExtension = normalizedType === "jpeg" ? "jpg" : normalizedType;
|
|
6362
|
+
const payload = buildImageTempFilePayload(normalizedType, width, height, { quality: Number.isFinite(option.quality) ? Number(option.quality) : void 0 });
|
|
6363
|
+
return {
|
|
6364
|
+
errMsg: "compressImage:ok",
|
|
6365
|
+
tempFilePath: this.createTempFile(payload, `headless://wxfile/temp/compressed-image-${String(fileId + 1).padStart(2, "0")}.${fileExtension}`)
|
|
6366
|
+
};
|
|
6367
|
+
},
|
|
5008
6368
|
getNetworkType() {
|
|
5009
6369
|
return {
|
|
5010
6370
|
errMsg: "getNetworkType:ok",
|
|
5011
6371
|
networkType
|
|
5012
6372
|
};
|
|
5013
6373
|
},
|
|
6374
|
+
getImageInfo(option) {
|
|
6375
|
+
const fileContent = files.get(option.src);
|
|
6376
|
+
if (fileContent == null) throw new Error(`getImageInfo:fail file not found: ${option.src}`);
|
|
6377
|
+
const size = inferImageSize(fileContent);
|
|
6378
|
+
return {
|
|
6379
|
+
errMsg: "getImageInfo:ok",
|
|
6380
|
+
height: size.height,
|
|
6381
|
+
orientation: "up",
|
|
6382
|
+
path: option.src,
|
|
6383
|
+
type: inferImageType(option.src, fileContent),
|
|
6384
|
+
width: size.width
|
|
6385
|
+
};
|
|
6386
|
+
},
|
|
6387
|
+
getVideoInfo(option) {
|
|
6388
|
+
const fileContent = files.get(option.src);
|
|
6389
|
+
if (fileContent == null) throw new Error(`getVideoInfo:fail file not found: ${option.src}`);
|
|
6390
|
+
const info = inferVideoInfo(option.src, fileContent);
|
|
6391
|
+
return {
|
|
6392
|
+
bitrate: info.bitrate,
|
|
6393
|
+
duration: info.duration,
|
|
6394
|
+
errMsg: "getVideoInfo:ok",
|
|
6395
|
+
fps: info.fps,
|
|
6396
|
+
height: info.height,
|
|
6397
|
+
orientation: "up",
|
|
6398
|
+
size: info.size,
|
|
6399
|
+
type: info.type,
|
|
6400
|
+
width: info.width
|
|
6401
|
+
};
|
|
6402
|
+
},
|
|
5014
6403
|
getModalLogs() {
|
|
5015
6404
|
return modalLogs.map((entry) => ({
|
|
5016
6405
|
...entry,
|
|
@@ -5045,6 +6434,15 @@ function createHeadlessWxState() {
|
|
|
5045
6434
|
getStorageSnapshot() {
|
|
5046
6435
|
return Object.fromEntries(Array.from(storage.entries(), ([key, value]) => [key, cloneValue(value)]));
|
|
5047
6436
|
},
|
|
6437
|
+
getClipboardData() {
|
|
6438
|
+
return {
|
|
6439
|
+
data: clipboardData,
|
|
6440
|
+
errMsg: "getClipboardData:ok"
|
|
6441
|
+
};
|
|
6442
|
+
},
|
|
6443
|
+
getClipboardSnapshot() {
|
|
6444
|
+
return { data: clipboardData };
|
|
6445
|
+
},
|
|
5048
6446
|
getStorageSync(key) {
|
|
5049
6447
|
return cloneValue(storage.get(key));
|
|
5050
6448
|
},
|
|
@@ -5054,6 +6452,21 @@ function createHeadlessWxState() {
|
|
|
5054
6452
|
getLoading() {
|
|
5055
6453
|
return loading ? { ...loading } : null;
|
|
5056
6454
|
},
|
|
6455
|
+
getPreviewImage() {
|
|
6456
|
+
return previewImage ? {
|
|
6457
|
+
current: previewImage.current,
|
|
6458
|
+
urls: [...previewImage.urls],
|
|
6459
|
+
visible: previewImage.visible
|
|
6460
|
+
} : null;
|
|
6461
|
+
},
|
|
6462
|
+
getOpenedDocument() {
|
|
6463
|
+
return openedDocument ? {
|
|
6464
|
+
filePath: openedDocument.filePath,
|
|
6465
|
+
fileType: openedDocument.fileType,
|
|
6466
|
+
showMenu: openedDocument.showMenu,
|
|
6467
|
+
visible: openedDocument.visible
|
|
6468
|
+
} : null;
|
|
6469
|
+
},
|
|
5057
6470
|
hideShareMenu() {
|
|
5058
6471
|
shareMenu = {
|
|
5059
6472
|
...shareMenu,
|
|
@@ -5123,6 +6536,27 @@ function createHeadlessWxState() {
|
|
|
5123
6536
|
onNetworkStatusChange(callback) {
|
|
5124
6537
|
networkStatusChangeCallbacks.add(callback);
|
|
5125
6538
|
},
|
|
6539
|
+
openDocument(option) {
|
|
6540
|
+
const normalizedPath = normalizeFsPath(option.filePath);
|
|
6541
|
+
if (!files.has(normalizedPath)) throw new Error(`openDocument:fail no such file or directory, open '${normalizedPath}'`);
|
|
6542
|
+
openedDocument = {
|
|
6543
|
+
filePath: normalizedPath,
|
|
6544
|
+
fileType: typeof option.fileType === "string" && option.fileType.trim() !== "" ? normalizeFileExtension(option.fileType) : inferDocumentFileType(normalizedPath),
|
|
6545
|
+
showMenu: option.showMenu !== false,
|
|
6546
|
+
visible: true
|
|
6547
|
+
};
|
|
6548
|
+
return { errMsg: "openDocument:ok" };
|
|
6549
|
+
},
|
|
6550
|
+
previewImage(option) {
|
|
6551
|
+
const urls = Array.isArray(option.urls) ? option.urls.filter((item) => typeof item === "string" && item.trim() !== "") : [];
|
|
6552
|
+
if (urls.length === 0) throw new Error("previewImage:fail invalid urls");
|
|
6553
|
+
previewImage = {
|
|
6554
|
+
current: typeof option.current === "string" && option.current.trim() !== "" ? option.current : urls[0],
|
|
6555
|
+
urls: [...urls],
|
|
6556
|
+
visible: true
|
|
6557
|
+
};
|
|
6558
|
+
return { errMsg: "previewImage:ok" };
|
|
6559
|
+
},
|
|
5126
6560
|
removeStorageSync(key) {
|
|
5127
6561
|
storage.delete(key);
|
|
5128
6562
|
},
|
|
@@ -5170,6 +6604,14 @@ function createHeadlessWxState() {
|
|
|
5170
6604
|
removeSavedFile(option) {
|
|
5171
6605
|
return removeSavedFile(option.filePath);
|
|
5172
6606
|
},
|
|
6607
|
+
saveImageToPhotosAlbum(option) {
|
|
6608
|
+
if (files.get(option.filePath) == null) throw new Error(`saveImageToPhotosAlbum:fail file not found: ${option.filePath}`);
|
|
6609
|
+
return { errMsg: "saveImageToPhotosAlbum:ok" };
|
|
6610
|
+
},
|
|
6611
|
+
saveVideoToPhotosAlbum(option) {
|
|
6612
|
+
if (files.get(option.filePath) == null) throw new Error(`saveVideoToPhotosAlbum:fail file not found: ${option.filePath}`);
|
|
6613
|
+
return { errMsg: "saveVideoToPhotosAlbum:ok" };
|
|
6614
|
+
},
|
|
5173
6615
|
saveFile(option) {
|
|
5174
6616
|
const fileContent = files.get(option.tempFilePath);
|
|
5175
6617
|
if (fileContent == null) throw new Error(`saveFile:fail tempFilePath not found: ${option.tempFilePath}`);
|
|
@@ -5185,6 +6627,10 @@ function createHeadlessWxState() {
|
|
|
5185
6627
|
setStorageSync(key, value) {
|
|
5186
6628
|
storage.set(key, cloneValue(value));
|
|
5187
6629
|
},
|
|
6630
|
+
setClipboardData(option) {
|
|
6631
|
+
clipboardData = String(option.data);
|
|
6632
|
+
return { errMsg: "setClipboardData:ok" };
|
|
6633
|
+
},
|
|
5188
6634
|
setFile(filePath, fileContent) {
|
|
5189
6635
|
const normalizedPath = normalizeFsPath(filePath);
|
|
5190
6636
|
ensureDirectoryTree(normalizedPath);
|
|
@@ -5330,7 +6776,9 @@ function createHeadlessWxState() {
|
|
|
5330
6776
|
//#region ../../mpcore/packages/simulator/src/runtime/session.ts
|
|
5331
6777
|
const LEADING_SLASH_RE$1 = /^\/+/;
|
|
5332
6778
|
const PAGE_STACK_LIMIT = 10;
|
|
6779
|
+
const WHITESPACE_RE = /\s+/;
|
|
5333
6780
|
const DATA_ATTR_SELECTOR_RE = /^\[data-([^=\]]+)="([^"]*)"\]$/;
|
|
6781
|
+
const COMPOUND_SELECTOR_PART_RE = /#[\w-]+|\.[\w-]+|\[data-[^=\]]+="[^"]*"\]|[A-Za-z][\w-]*/g;
|
|
5334
6782
|
const DATASET_KEY_RE = /-([a-z])/g;
|
|
5335
6783
|
function stripLeadingSlash(route) {
|
|
5336
6784
|
return route.replace(LEADING_SLASH_RE$1, "");
|
|
@@ -5351,6 +6799,28 @@ function parseNavigationUrl(url) {
|
|
|
5351
6799
|
query: normalizeQuery(queryString)
|
|
5352
6800
|
};
|
|
5353
6801
|
}
|
|
6802
|
+
function parseCompoundSelector(selector) {
|
|
6803
|
+
const parts = selector.match(COMPOUND_SELECTOR_PART_RE) ?? [];
|
|
6804
|
+
return parts.join("") === selector ? parts : [];
|
|
6805
|
+
}
|
|
6806
|
+
function matchesComponentSelector(scope, selector) {
|
|
6807
|
+
const parts = parseCompoundSelector(selector);
|
|
6808
|
+
if (parts.length === 0) return false;
|
|
6809
|
+
return parts.every((part) => {
|
|
6810
|
+
if (part.startsWith("#")) return scope.id === part.slice(1);
|
|
6811
|
+
if (part.startsWith(".")) return scope.classList?.includes(part.slice(1)) ?? false;
|
|
6812
|
+
const dataAttrMatch = part.match(DATA_ATTR_SELECTOR_RE);
|
|
6813
|
+
if (dataAttrMatch) {
|
|
6814
|
+
const [, key, value] = dataAttrMatch;
|
|
6815
|
+
const datasetKey = key.replace(DATASET_KEY_RE, (_match, char) => char.toUpperCase());
|
|
6816
|
+
return scope.dataset?.[datasetKey] === value;
|
|
6817
|
+
}
|
|
6818
|
+
return scope.alias === part;
|
|
6819
|
+
});
|
|
6820
|
+
}
|
|
6821
|
+
function normalizeSelectorParts(selector) {
|
|
6822
|
+
return selector.trim().split(WHITESPACE_RE).filter(Boolean);
|
|
6823
|
+
}
|
|
5354
6824
|
function createAppLaunchOptions(pathname, query) {
|
|
5355
6825
|
return {
|
|
5356
6826
|
path: stripLeadingSlash(pathname),
|
|
@@ -5392,6 +6862,8 @@ var HeadlessSession = class {
|
|
|
5392
6862
|
tabBarState = /* @__PURE__ */ new Map();
|
|
5393
6863
|
tabBarVisible = false;
|
|
5394
6864
|
systemInfo = createDefaultSystemInfo();
|
|
6865
|
+
mediaQueryObservers = /* @__PURE__ */ new Set();
|
|
6866
|
+
canvasContexts = /* @__PURE__ */ new Map();
|
|
5395
6867
|
enterOptions = createAppLaunchOptions("", {});
|
|
5396
6868
|
launchOptions = createAppLaunchOptions("", {});
|
|
5397
6869
|
wxState = createHeadlessWxState();
|
|
@@ -5424,7 +6896,20 @@ var HeadlessSession = class {
|
|
|
5424
6896
|
});
|
|
5425
6897
|
this.tabBarVisible = this.tabBarRoutes.size > 0;
|
|
5426
6898
|
this.moduleLoader = createModuleLoader(this.registries, () => this.pages.slice(), () => this.getApp(), {
|
|
6899
|
+
chooseImage: (option) => this.wxState.chooseImage(option ?? {}),
|
|
6900
|
+
chooseMessageFile: (option) => this.wxState.chooseMessageFile(option ?? {}),
|
|
6901
|
+
chooseMedia: (option) => this.wxState.chooseMedia(option ?? {}),
|
|
6902
|
+
chooseVideo: (option) => this.wxState.chooseVideo(option ?? {}),
|
|
6903
|
+
compressImage: (option) => this.wxState.compressImage(option),
|
|
6904
|
+
createAnimation: (option) => this.createAnimation(option),
|
|
6905
|
+
createCanvasContext: (canvasId, scope) => this.createCanvasContext(canvasId, scope),
|
|
6906
|
+
canvasToTempFilePath: (option) => this.canvasToTempFilePath(option),
|
|
6907
|
+
createIntersectionObserver: (scope, options) => this.createIntersectionObserver(scope, options),
|
|
6908
|
+
createVideoContext: (videoId, scope) => this.createVideoContext(videoId, scope),
|
|
5427
6909
|
executeSelectorQuery: (requests, scope) => this.executeSelectorQuery(requests, scope),
|
|
6910
|
+
getFileInfo: (option) => this.wxState.getFileInfo(option),
|
|
6911
|
+
getImageInfo: (option) => this.wxState.getImageInfo(option),
|
|
6912
|
+
getVideoInfo: (option) => this.wxState.getVideoInfo(option),
|
|
5428
6913
|
getFileSystemManager: () => this.wxState.getFileSystemManager(),
|
|
5429
6914
|
getSavedFileInfo: (option) => this.wxState.getSavedFileInfo(option),
|
|
5430
6915
|
getSavedFileList: () => this.wxState.getSavedFileList(),
|
|
@@ -5445,11 +6930,13 @@ var HeadlessSession = class {
|
|
|
5445
6930
|
extraData: { ...this.launchOptions.referrerInfo.extraData }
|
|
5446
6931
|
}
|
|
5447
6932
|
}),
|
|
6933
|
+
getClipboardData: () => this.wxState.getClipboardData(),
|
|
5448
6934
|
getMenuButtonBoundingClientRect: () => deriveMenuButtonBoundingClientRect(this.systemInfo),
|
|
5449
6935
|
getNetworkType: () => this.wxState.getNetworkType(),
|
|
5450
6936
|
navigateBack: (option) => this.navigateBack(option?.delta),
|
|
5451
6937
|
navigateTo: (option) => this.navigateTo(option.url),
|
|
5452
6938
|
pageScrollTo: (option) => this.pageScrollTo(option),
|
|
6939
|
+
openDocument: (option) => this.wxState.openDocument(option),
|
|
5453
6940
|
clearStorageSync: () => this.wxState.clearStorageSync(),
|
|
5454
6941
|
getStorageInfoSync: () => this.wxState.getStorageInfoSync(),
|
|
5455
6942
|
getStorageSync: (key) => this.wxState.getStorageSync(key),
|
|
@@ -5461,14 +6948,18 @@ var HeadlessSession = class {
|
|
|
5461
6948
|
reLaunch: (option) => this.reLaunch(option.url),
|
|
5462
6949
|
redirectTo: (option) => this.redirectTo(option.url),
|
|
5463
6950
|
removeSavedFile: (option) => this.wxState.removeSavedFile(option),
|
|
6951
|
+
saveImageToPhotosAlbum: (option) => this.wxState.saveImageToPhotosAlbum(option),
|
|
6952
|
+
saveVideoToPhotosAlbum: (option) => this.wxState.saveVideoToPhotosAlbum(option),
|
|
5464
6953
|
nextTick: (callback) => queueMicrotask(() => callback?.()),
|
|
5465
6954
|
offNetworkStatusChange: (callback) => this.wxState.offNetworkStatusChange(callback),
|
|
5466
6955
|
onNetworkStatusChange: (callback) => this.wxState.onNetworkStatusChange(callback),
|
|
5467
6956
|
removeStorageSync: (key) => this.wxState.removeStorageSync(key),
|
|
6957
|
+
previewImage: (option) => this.wxState.previewImage(option),
|
|
5468
6958
|
request: (option) => this.wxState.request(option),
|
|
5469
6959
|
saveFile: (option) => this.wxState.saveFile(option),
|
|
5470
6960
|
setBackgroundColor: (option) => this.setBackgroundColor(option),
|
|
5471
6961
|
setBackgroundTextStyle: (option) => this.setBackgroundTextStyle(option.textStyle),
|
|
6962
|
+
setClipboardData: (option) => this.wxState.setClipboardData(option),
|
|
5472
6963
|
setStorageSync: (key, value) => this.wxState.setStorageSync(key, value),
|
|
5473
6964
|
setNavigationBarColor: (option) => this.setNavigationBarColor(option),
|
|
5474
6965
|
setNavigationBarTitle: (option) => this.setNavigationBarTitle(option.title),
|
|
@@ -5484,6 +6975,7 @@ var HeadlessSession = class {
|
|
|
5484
6975
|
showLoading: (option) => this.wxState.showLoading(option),
|
|
5485
6976
|
showModal: (option) => this.wxState.showModal(option),
|
|
5486
6977
|
showToast: (option) => this.wxState.showToast(option),
|
|
6978
|
+
startPullDownRefresh: () => this.startPullDownRefresh(),
|
|
5487
6979
|
stopPullDownRefresh: () => this.stopPullDownRefresh(),
|
|
5488
6980
|
switchTab: (option) => this.switchTab(option.url),
|
|
5489
6981
|
uploadFile: (option) => this.wxState.uploadFile(option),
|
|
@@ -5542,6 +7034,8 @@ var HeadlessSession = class {
|
|
|
5542
7034
|
moduleLoader: this.moduleLoader,
|
|
5543
7035
|
project: this.project,
|
|
5544
7036
|
session: {
|
|
7037
|
+
createIntersectionObserver: (scope, options) => this.createIntersectionObserver(scope, options),
|
|
7038
|
+
createMediaQueryObserver: (scope) => this.createMediaQueryObserver(scope),
|
|
5545
7039
|
selectAllComponentsWithin: (scopeId, selector) => this.selectAllComponentsWithin(scopeId, selector),
|
|
5546
7040
|
selectComponentWithin: (scopeId, selector) => this.selectComponentWithin(scopeId, selector),
|
|
5547
7041
|
selectOwnerComponent: (scopeId) => this.selectOwnerComponent(scopeId)
|
|
@@ -5609,6 +7103,7 @@ var HeadlessSession = class {
|
|
|
5609
7103
|
}
|
|
5610
7104
|
const instance = this.componentCache.get(normalizedScopeId);
|
|
5611
7105
|
if (!instance) throw new Error(`Unknown scope "${normalizedScopeId}" in headless runtime.`);
|
|
7106
|
+
instance.__lastInteractionEvent__ = void 0;
|
|
5612
7107
|
const method = instance[methodName];
|
|
5613
7108
|
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${normalizedScopeId}".`);
|
|
5614
7109
|
return method.apply(instance, args);
|
|
@@ -5658,6 +7153,15 @@ var HeadlessSession = class {
|
|
|
5658
7153
|
getStorageInfo() {
|
|
5659
7154
|
return this.wxState.getStorageInfoSync();
|
|
5660
7155
|
}
|
|
7156
|
+
getClipboardData() {
|
|
7157
|
+
return this.wxState.getClipboardSnapshot();
|
|
7158
|
+
}
|
|
7159
|
+
getPreviewImage() {
|
|
7160
|
+
return this.wxState.getPreviewImage();
|
|
7161
|
+
}
|
|
7162
|
+
getOpenedDocument() {
|
|
7163
|
+
return this.wxState.getOpenedDocument();
|
|
7164
|
+
}
|
|
5661
7165
|
getPullDownRefreshState() {
|
|
5662
7166
|
return { ...this.pullDownRefreshState };
|
|
5663
7167
|
}
|
|
@@ -5931,7 +7435,8 @@ var HeadlessSession = class {
|
|
|
5931
7435
|
}
|
|
5932
7436
|
pageScrollTo(option) {
|
|
5933
7437
|
const current = this.requireCurrentPage("wx.pageScrollTo()");
|
|
5934
|
-
|
|
7438
|
+
const selectorScrollTop = resolveSelectorScrollTop(this.renderCurrentPage().root, option.selector);
|
|
7439
|
+
current.__scrollTop__ = Number(selectorScrollTop ?? option.scrollTop ?? 0);
|
|
5935
7440
|
current.onPageScroll?.({ scrollTop: current.__scrollTop__ });
|
|
5936
7441
|
}
|
|
5937
7442
|
triggerPullDownRefresh() {
|
|
@@ -5943,6 +7448,10 @@ var HeadlessSession = class {
|
|
|
5943
7448
|
current.onPullDownRefresh?.();
|
|
5944
7449
|
return current;
|
|
5945
7450
|
}
|
|
7451
|
+
startPullDownRefresh() {
|
|
7452
|
+
this.triggerPullDownRefresh();
|
|
7453
|
+
return { errMsg: "startPullDownRefresh:ok" };
|
|
7454
|
+
}
|
|
5946
7455
|
triggerReachBottom() {
|
|
5947
7456
|
const current = this.requireCurrentPage("triggerReachBottom()");
|
|
5948
7457
|
current.onReachBottom?.();
|
|
@@ -5953,6 +7462,7 @@ var HeadlessSession = class {
|
|
|
5953
7462
|
applyResizeToSystemInfo(this.systemInfo, options);
|
|
5954
7463
|
current.onResize?.(options);
|
|
5955
7464
|
this.runPageComponentLifetime(current.route, "resize", options);
|
|
7465
|
+
this.notifyMediaQueryObservers();
|
|
5956
7466
|
return current;
|
|
5957
7467
|
}
|
|
5958
7468
|
triggerRouteDone(options = {}) {
|
|
@@ -5973,6 +7483,11 @@ var HeadlessSession = class {
|
|
|
5973
7483
|
const scopeId = scope && scope !== current ? this.getComponentScopeId(scope) : null;
|
|
5974
7484
|
return executeSelectorQueryRequests(requests, {
|
|
5975
7485
|
page: current,
|
|
7486
|
+
resolveContext: (node) => {
|
|
7487
|
+
if (node.name !== "canvas") return { type: "unsupported-context" };
|
|
7488
|
+
const canvasId = node.attribs?.["canvas-id"];
|
|
7489
|
+
return typeof canvasId === "string" && canvasId ? this.createCanvasContext(canvasId, scope) : { type: "unsupported-context" };
|
|
7490
|
+
},
|
|
5976
7491
|
root: resolveSelectorQueryScopeRoot(rendered.root, scopeId),
|
|
5977
7492
|
windowInfo: this.getWindowInfo()
|
|
5978
7493
|
});
|
|
@@ -5986,6 +7501,8 @@ var HeadlessSession = class {
|
|
|
5986
7501
|
background: resolveBackgroundSnapshot(this.project.appConfig, pageConfig),
|
|
5987
7502
|
navigationBar: resolveNavigationBarSnapshot(this.project.appConfig, pageConfig)
|
|
5988
7503
|
});
|
|
7504
|
+
pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
|
|
7505
|
+
pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
|
|
5989
7506
|
pageInstance.selectComponent = (selector) => this.selectComponent(selector);
|
|
5990
7507
|
pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector);
|
|
5991
7508
|
pageInstance.onLoad?.(target.query);
|
|
@@ -6035,10 +7552,12 @@ var HeadlessSession = class {
|
|
|
6035
7552
|
this.tabPages.clear();
|
|
6036
7553
|
this.componentCache.clear();
|
|
6037
7554
|
this.componentScopes.clear();
|
|
7555
|
+
this.clearMediaQueryObservers();
|
|
6038
7556
|
this.currentPageInstance = null;
|
|
6039
7557
|
}
|
|
6040
7558
|
unloadPage(page) {
|
|
6041
7559
|
page.onUnload?.();
|
|
7560
|
+
this.clearMediaQueryObservers(page);
|
|
6042
7561
|
this.detachPageComponents(page.route);
|
|
6043
7562
|
this.tabPages.delete(stripLeadingSlash(page.route));
|
|
6044
7563
|
}
|
|
@@ -6049,21 +7568,21 @@ var HeadlessSession = class {
|
|
|
6049
7568
|
return current;
|
|
6050
7569
|
}
|
|
6051
7570
|
selectComponentsWithin(rootScopeId, selector) {
|
|
6052
|
-
const
|
|
6053
|
-
if (
|
|
7571
|
+
const selectorParts = normalizeSelectorParts(selector);
|
|
7572
|
+
if (selectorParts.length === 0) return [];
|
|
6054
7573
|
return [...this.componentScopes.entries()].filter(([candidateScopeId, scope]) => {
|
|
6055
7574
|
if (!candidateScopeId.includes("/")) return false;
|
|
6056
7575
|
if (rootScopeId && candidateScopeId === rootScopeId) return false;
|
|
6057
7576
|
if (rootScopeId && !candidateScopeId.startsWith(`${rootScopeId}/`)) return false;
|
|
6058
|
-
|
|
6059
|
-
if (
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
7577
|
+
const lastPart = selectorParts.at(-1);
|
|
7578
|
+
if (!lastPart || !matchesComponentSelector(scope, lastPart)) return false;
|
|
7579
|
+
let currentScope = scope.ownerScopeId ? this.componentScopes.get(scope.ownerScopeId) : void 0;
|
|
7580
|
+
for (let partIndex = selectorParts.length - 2; partIndex >= 0; partIndex -= 1) {
|
|
7581
|
+
while (currentScope && !matchesComponentSelector(currentScope, selectorParts[partIndex])) currentScope = currentScope.ownerScopeId ? this.componentScopes.get(currentScope.ownerScopeId) : void 0;
|
|
7582
|
+
if (!currentScope) return false;
|
|
7583
|
+
currentScope = currentScope.ownerScopeId ? this.componentScopes.get(currentScope.ownerScopeId) : void 0;
|
|
6065
7584
|
}
|
|
6066
|
-
return
|
|
7585
|
+
return true;
|
|
6067
7586
|
}).map(([candidateScopeId]) => this.componentCache.get(candidateScopeId)).filter(Boolean);
|
|
6068
7587
|
}
|
|
6069
7588
|
detachPageComponents(route) {
|
|
@@ -6075,6 +7594,18 @@ var HeadlessSession = class {
|
|
|
6075
7594
|
this.componentScopes.delete(scopeId);
|
|
6076
7595
|
}
|
|
6077
7596
|
}
|
|
7597
|
+
notifyMediaQueryObservers() {
|
|
7598
|
+
for (const entry of [...this.mediaQueryObservers]) {
|
|
7599
|
+
if (entry.ownerPage !== this.currentPageInstance) continue;
|
|
7600
|
+
entry.notify();
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
clearMediaQueryObservers(ownerPage) {
|
|
7604
|
+
for (const entry of [...this.mediaQueryObservers]) {
|
|
7605
|
+
if (ownerPage && entry.ownerPage !== ownerPage) continue;
|
|
7606
|
+
entry.disconnect();
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
6078
7609
|
runPageComponentLifetime(route, lifetimeName, payload) {
|
|
6079
7610
|
const prefix = `page:${stripLeadingSlash(route)}`;
|
|
6080
7611
|
for (const [scopeId, instance] of this.componentCache.entries()) {
|
|
@@ -6086,6 +7617,100 @@ var HeadlessSession = class {
|
|
|
6086
7617
|
for (const [scopeId, instance] of this.componentCache.entries()) if (instance === component) return scopeId;
|
|
6087
7618
|
return null;
|
|
6088
7619
|
}
|
|
7620
|
+
createVideoContext(videoId, scope) {
|
|
7621
|
+
return createHeadlessVideoContext({
|
|
7622
|
+
callScopeMethod: (scopeId, methodName, event) => this.callScopeMethod(scopeId, methodName, event),
|
|
7623
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7624
|
+
resolveScope: (value) => {
|
|
7625
|
+
const current = this.currentPageInstance;
|
|
7626
|
+
if (!value || value === current) return { kind: "page" };
|
|
7627
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7628
|
+
if (!scopeId) return { kind: "missing" };
|
|
7629
|
+
return {
|
|
7630
|
+
kind: "component",
|
|
7631
|
+
scopeId
|
|
7632
|
+
};
|
|
7633
|
+
}
|
|
7634
|
+
}, videoId, scope);
|
|
7635
|
+
}
|
|
7636
|
+
createAnimation(option) {
|
|
7637
|
+
return createHeadlessAnimation(option);
|
|
7638
|
+
}
|
|
7639
|
+
resolveCanvasScopeKey(scope) {
|
|
7640
|
+
const current = this.currentPageInstance;
|
|
7641
|
+
if (!scope || scope === current) return "page";
|
|
7642
|
+
const scopeId = this.getScopeIdForComponent(scope);
|
|
7643
|
+
return scopeId ? `component:${scopeId}` : "missing";
|
|
7644
|
+
}
|
|
7645
|
+
createCanvasContext(canvasId, scope) {
|
|
7646
|
+
const context = createHeadlessCanvasContext({
|
|
7647
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7648
|
+
resolveScope: (value) => {
|
|
7649
|
+
const current = this.currentPageInstance;
|
|
7650
|
+
if (!value || value === current) return { kind: "page" };
|
|
7651
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7652
|
+
if (!scopeId) return { kind: "missing" };
|
|
7653
|
+
return {
|
|
7654
|
+
kind: "component",
|
|
7655
|
+
scopeId
|
|
7656
|
+
};
|
|
7657
|
+
}
|
|
7658
|
+
}, canvasId, scope);
|
|
7659
|
+
this.canvasContexts.set(`${this.resolveCanvasScopeKey(scope)}:${canvasId}`, context);
|
|
7660
|
+
return context;
|
|
7661
|
+
}
|
|
7662
|
+
canvasToTempFilePath(option) {
|
|
7663
|
+
const context = this.canvasContexts.get(`${this.resolveCanvasScopeKey(option.component)}:${option.canvasId}`);
|
|
7664
|
+
if (!context) throw new Error(`canvasToTempFilePath:fail canvas "${option.canvasId}" has not been drawn in headless runtime.`);
|
|
7665
|
+
return {
|
|
7666
|
+
errMsg: "canvasToTempFilePath:ok",
|
|
7667
|
+
tempFilePath: this.wxState.createTempFile(JSON.stringify({
|
|
7668
|
+
canvasId: option.canvasId,
|
|
7669
|
+
config: {
|
|
7670
|
+
destHeight: option.destHeight,
|
|
7671
|
+
destWidth: option.destWidth,
|
|
7672
|
+
fileType: option.fileType,
|
|
7673
|
+
height: option.height,
|
|
7674
|
+
quality: option.quality,
|
|
7675
|
+
width: option.width,
|
|
7676
|
+
x: option.x,
|
|
7677
|
+
y: option.y
|
|
7678
|
+
},
|
|
7679
|
+
snapshot: context.__getSnapshot()
|
|
7680
|
+
}))
|
|
7681
|
+
};
|
|
7682
|
+
}
|
|
7683
|
+
createIntersectionObserver(scope, options) {
|
|
7684
|
+
return createHeadlessIntersectionObserver({
|
|
7685
|
+
getWindowInfo: () => this.getWindowInfo(),
|
|
7686
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7687
|
+
resolveScope: (value) => {
|
|
7688
|
+
const current = this.currentPageInstance;
|
|
7689
|
+
if (!value || value === current) return { kind: "page" };
|
|
7690
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7691
|
+
if (!scopeId) return { kind: "missing" };
|
|
7692
|
+
return {
|
|
7693
|
+
kind: "component",
|
|
7694
|
+
scopeId
|
|
7695
|
+
};
|
|
7696
|
+
}
|
|
7697
|
+
}, scope, options);
|
|
7698
|
+
}
|
|
7699
|
+
createMediaQueryObserver(scope) {
|
|
7700
|
+
const current = this.requireCurrentPage("createMediaQueryObserver()");
|
|
7701
|
+
const ownerPage = scope === current || !scope ? current : this.currentPageInstance ?? current;
|
|
7702
|
+
let entry;
|
|
7703
|
+
const controller = createHeadlessMediaQueryObserver({ getWindowInfo: () => this.getWindowInfo() }, () => {
|
|
7704
|
+
this.mediaQueryObservers.delete(entry);
|
|
7705
|
+
});
|
|
7706
|
+
entry = {
|
|
7707
|
+
disconnect: controller.disconnect,
|
|
7708
|
+
notify: controller.notify,
|
|
7709
|
+
ownerPage
|
|
7710
|
+
};
|
|
7711
|
+
this.mediaQueryObservers.add(entry);
|
|
7712
|
+
return controller.observer;
|
|
7713
|
+
}
|
|
6089
7714
|
};
|
|
6090
7715
|
function createHeadlessSession(options) {
|
|
6091
7716
|
return new HeadlessSession(options);
|
|
@@ -6227,7 +7852,7 @@ var HeadlessTestingPageHandle = class {
|
|
|
6227
7852
|
}
|
|
6228
7853
|
};
|
|
6229
7854
|
//#endregion
|
|
6230
|
-
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle.ts
|
|
7855
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/shared.ts
|
|
6231
7856
|
const LEADING_SLASH_RE = /^\/+/;
|
|
6232
7857
|
const DEFAULT_WAIT_TIMEOUT = 1e3;
|
|
6233
7858
|
const DEFAULT_WAIT_INTERVAL = 10;
|
|
@@ -6238,6 +7863,28 @@ function resolvePageScopeId(scopeId) {
|
|
|
6238
7863
|
const pagePathIndex = scopeId.indexOf("/page/");
|
|
6239
7864
|
return pagePathIndex >= 0 ? scopeId.slice(0, pagePathIndex) : scopeId;
|
|
6240
7865
|
}
|
|
7866
|
+
function normalizeNonEmptyInput(value, label) {
|
|
7867
|
+
const normalizedValue = value.trim();
|
|
7868
|
+
if (!normalizedValue) throw new Error(`${label} must be a non-empty string in headless testing runtime.`);
|
|
7869
|
+
return normalizedValue;
|
|
7870
|
+
}
|
|
7871
|
+
async function waitForDelay(ms = 0) {
|
|
7872
|
+
if (ms <= 0) return;
|
|
7873
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
7874
|
+
}
|
|
7875
|
+
async function pollUntil(check, errorMessage, options = {}) {
|
|
7876
|
+
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? DEFAULT_WAIT_TIMEOUT)) : DEFAULT_WAIT_TIMEOUT;
|
|
7877
|
+
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? DEFAULT_WAIT_INTERVAL)) : DEFAULT_WAIT_INTERVAL;
|
|
7878
|
+
const deadline = Date.now() + timeout;
|
|
7879
|
+
while (true) {
|
|
7880
|
+
const result = await check();
|
|
7881
|
+
if (result != null) return result;
|
|
7882
|
+
if (Date.now() >= deadline) throw new Error(errorMessage);
|
|
7883
|
+
await waitForDelay(interval);
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
//#endregion
|
|
7887
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/scope.ts
|
|
6241
7888
|
var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
6242
7889
|
constructor(scopeId, project, session) {
|
|
6243
7890
|
this.scopeId = scopeId;
|
|
@@ -6249,8 +7896,7 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
6249
7896
|
return scopeId ? new HeadlessTestingScopeHandle(scopeId, this.project, this.session) : null;
|
|
6250
7897
|
}
|
|
6251
7898
|
async callMethod(methodName, ...args) {
|
|
6252
|
-
const normalizedMethodName = methodName
|
|
6253
|
-
if (!normalizedMethodName) throw new Error("Method name must be a non-empty string in headless testing runtime.");
|
|
7899
|
+
const normalizedMethodName = normalizeNonEmptyInput(methodName, "Method name");
|
|
6254
7900
|
return this.session.callScopeMethodDirect(this.scopeId, normalizedMethodName, ...args);
|
|
6255
7901
|
}
|
|
6256
7902
|
async snapshot() {
|
|
@@ -6267,75 +7913,37 @@ var HeadlessTestingScopeHandle = class HeadlessTestingScopeHandle {
|
|
|
6267
7913
|
return this.createScopeHandle(this.session.selectOwnerComponent(this.scopeId));
|
|
6268
7914
|
}
|
|
6269
7915
|
async waitForOwnerComponent(options = {}) {
|
|
6270
|
-
|
|
6271
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
6272
|
-
const deadline = Date.now() + timeout;
|
|
6273
|
-
while (true) {
|
|
6274
|
-
const owner = await this.ownerComponent();
|
|
6275
|
-
if (owner) return owner;
|
|
6276
|
-
if (Date.now() >= deadline) throw new Error("Timed out waiting for owner component in headless testing runtime.");
|
|
6277
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
6278
|
-
}
|
|
7916
|
+
return await pollUntil(async () => await this.ownerComponent(), "Timed out waiting for owner component in headless testing runtime.", options);
|
|
6279
7917
|
}
|
|
6280
7918
|
async selectComponent(selector) {
|
|
6281
|
-
const normalizedSelector = selector
|
|
6282
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7919
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6283
7920
|
const component = this.session.selectComponentWithin(this.scopeId, normalizedSelector);
|
|
6284
7921
|
return this.createScopeHandle(component);
|
|
6285
7922
|
}
|
|
6286
7923
|
async selectAllComponents(selector) {
|
|
6287
|
-
const normalizedSelector = selector
|
|
6288
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7924
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6289
7925
|
return this.session.selectAllComponentsWithin(this.scopeId, normalizedSelector).map((component) => this.createScopeHandle(component)).filter((handle) => Boolean(handle));
|
|
6290
7926
|
}
|
|
6291
7927
|
async waitForComponent(selector, options = {}) {
|
|
6292
|
-
const normalizedSelector = selector
|
|
6293
|
-
|
|
6294
|
-
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? 1e3)) : 1e3;
|
|
6295
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
6296
|
-
const deadline = Date.now() + timeout;
|
|
6297
|
-
while (true) {
|
|
6298
|
-
const component = await this.selectComponent(normalizedSelector);
|
|
6299
|
-
if (component) return component;
|
|
6300
|
-
if (Date.now() >= deadline) throw new Error(`Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`);
|
|
6301
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
6302
|
-
}
|
|
7928
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7929
|
+
return await pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
6303
7930
|
}
|
|
6304
7931
|
async waitForComponents(selector, count = 1, options = {}) {
|
|
6305
|
-
const normalizedSelector = selector
|
|
6306
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7932
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6307
7933
|
const normalizedCount = Number.isFinite(count) ? Math.max(1, Math.trunc(count)) : 1;
|
|
6308
|
-
|
|
6309
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? 10)) : 10;
|
|
6310
|
-
const deadline = Date.now() + timeout;
|
|
6311
|
-
while (true) {
|
|
7934
|
+
return await pollUntil(async () => {
|
|
6312
7935
|
const components = await this.selectAllComponents(normalizedSelector);
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
6316
|
-
}
|
|
7936
|
+
return components.length >= normalizedCount ? components : null;
|
|
7937
|
+
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
6317
7938
|
}
|
|
6318
7939
|
};
|
|
7940
|
+
//#endregion
|
|
7941
|
+
//#region ../../mpcore/packages/simulator/src/testing/sessionHandle/index.ts
|
|
6319
7942
|
var HeadlessTestingSessionHandle = class {
|
|
6320
7943
|
constructor(project, session) {
|
|
6321
7944
|
this.project = project;
|
|
6322
7945
|
this.session = session;
|
|
6323
7946
|
}
|
|
6324
|
-
async waitFor(ms = 0) {
|
|
6325
|
-
if (ms <= 0) return;
|
|
6326
|
-
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
6327
|
-
}
|
|
6328
|
-
async pollUntil(check, errorMessage, options = {}) {
|
|
6329
|
-
const timeout = Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout ?? DEFAULT_WAIT_TIMEOUT)) : DEFAULT_WAIT_TIMEOUT;
|
|
6330
|
-
const interval = Number.isFinite(options.interval) ? Math.max(1, Math.trunc(options.interval ?? DEFAULT_WAIT_INTERVAL)) : DEFAULT_WAIT_INTERVAL;
|
|
6331
|
-
const deadline = Date.now() + timeout;
|
|
6332
|
-
while (true) {
|
|
6333
|
-
const result = await check();
|
|
6334
|
-
if (result != null) return result;
|
|
6335
|
-
if (Date.now() >= deadline) throw new Error(errorMessage);
|
|
6336
|
-
await this.waitFor(interval);
|
|
6337
|
-
}
|
|
6338
|
-
}
|
|
6339
7947
|
async close() {}
|
|
6340
7948
|
async currentPage() {
|
|
6341
7949
|
const current = this.session.getCurrentPages().at(-1);
|
|
@@ -6345,32 +7953,27 @@ var HeadlessTestingSessionHandle = class {
|
|
|
6345
7953
|
return this.session.getCurrentPages().map((page) => new HeadlessTestingPageHandle(this.project, page, this.session));
|
|
6346
7954
|
}
|
|
6347
7955
|
async scopeSnapshot(scopeId) {
|
|
6348
|
-
const normalizedScopeId = scopeId
|
|
6349
|
-
if (!normalizedScopeId) throw new Error("Scope id must be a non-empty string in headless testing runtime.");
|
|
7956
|
+
const normalizedScopeId = normalizeNonEmptyInput(scopeId, "Scope id");
|
|
6350
7957
|
return this.session.getScopeSnapshot(normalizedScopeId);
|
|
6351
7958
|
}
|
|
6352
7959
|
async selectComponent(selector) {
|
|
6353
|
-
const normalizedSelector = selector
|
|
6354
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7960
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6355
7961
|
const component = this.session.selectComponent(normalizedSelector);
|
|
6356
7962
|
const scopeId = this.session.getScopeIdForComponent(component);
|
|
6357
7963
|
return scopeId ? new HeadlessTestingScopeHandle(scopeId, this.project, this.session) : null;
|
|
6358
7964
|
}
|
|
6359
7965
|
async selectAllComponents(selector) {
|
|
6360
|
-
const normalizedSelector = selector
|
|
6361
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7966
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6362
7967
|
return this.session.selectAllComponents(normalizedSelector).map((component) => this.session.getScopeIdForComponent(component)).filter((scopeId) => Boolean(scopeId)).map((scopeId) => new HeadlessTestingScopeHandle(scopeId, this.project, this.session));
|
|
6363
7968
|
}
|
|
6364
7969
|
async waitForComponent(selector, options = {}) {
|
|
6365
|
-
const normalizedSelector = selector
|
|
6366
|
-
|
|
6367
|
-
return await this.pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
7970
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
7971
|
+
return await pollUntil(async () => await this.selectComponent(normalizedSelector), `Timed out waiting for component "${normalizedSelector}" in headless testing runtime.`, options);
|
|
6368
7972
|
}
|
|
6369
7973
|
async waitForComponents(selector, count = 1, options = {}) {
|
|
6370
|
-
const normalizedSelector = selector
|
|
6371
|
-
if (!normalizedSelector) throw new Error("Selector must be a non-empty string in headless testing runtime.");
|
|
7974
|
+
const normalizedSelector = normalizeNonEmptyInput(selector, "Selector");
|
|
6372
7975
|
const normalizedCount = Number.isFinite(count) ? Math.max(1, Math.trunc(count)) : 1;
|
|
6373
|
-
return await
|
|
7976
|
+
return await pollUntil(async () => {
|
|
6374
7977
|
const components = await this.selectAllComponents(normalizedSelector);
|
|
6375
7978
|
return components.length >= normalizedCount ? components : null;
|
|
6376
7979
|
}, `Timed out waiting for ${normalizedCount} component(s) matching "${normalizedSelector}" in headless testing runtime.`, options);
|
|
@@ -6382,7 +7985,7 @@ var HeadlessTestingSessionHandle = class {
|
|
|
6382
7985
|
async waitForCurrentPage(route, options = {}) {
|
|
6383
7986
|
const normalizedRoute = route?.trim();
|
|
6384
7987
|
if (normalizedRoute != null && !normalizedRoute) throw new Error("Route must be a non-empty string in headless testing runtime.");
|
|
6385
|
-
return await
|
|
7988
|
+
return await pollUntil(async () => {
|
|
6386
7989
|
const currentPageInstance = this.session.getCurrentPages().at(-1);
|
|
6387
7990
|
if (!currentPageInstance) return null;
|
|
6388
7991
|
const current = new HeadlessTestingPageHandle(this.project, currentPageInstance, this.session);
|