@skrillex1224/playwright-toolkit 2.1.111 → 2.1.113

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 CHANGED
@@ -208,7 +208,7 @@ const cookies = Utils.parseCookies('key=value; key2=value2', '.example.com');
208
208
  await page.context().addCookies(cookies);
209
209
 
210
210
  // 全页面滚动截图 (自动检测所有滚动元素,强制展开后截图)
211
- const base64Image = await Utils.fullPageScreenshot(page);
211
+ const base64Image = await Share.captureScreen(page);
212
212
  // 返回 base64 编码的 PNG 图片
213
213
  ```
214
214
 
package/dist/index.cjs CHANGED
@@ -3025,36 +3025,121 @@ var Logger = {
3025
3025
 
3026
3026
  // src/share.js
3027
3027
  var import_delay3 = __toESM(require("delay"), 1);
3028
+
3029
+ // src/internals/monitor.js
3030
+ var DEFAULT_DOM_SELECTORS = "html";
3031
+ var Monitor = {
3032
+ /**
3033
+ * 使用 Mutation.useMonitor 监控 DOM,并提取分享链接
3034
+ *
3035
+ * @param {import('playwright').Page} page
3036
+ * @param {Object} [options]
3037
+ * @param {string[]} [options.identities]
3038
+ * @param {string | string[]} [options.selectors]
3039
+ * @param {'added' | 'changed' | 'all'} [options.mode]
3040
+ * @param {(text: string, options?: { identities?: string[] }) => string[]} [options.parseLinks]
3041
+ * @param {(payload: { link: string; rawDom: string; mutationCount: number; html: string; text: string; mutationNodes: Array<{ html: string; text: string; mutationType: string }> }) => void} [options.onMatch]
3042
+ * @returns {Promise<{ stop: () => Promise<{ totalMutations: number }> }>}
3043
+ */
3044
+ async useShareLinkMonitor(page, options = {}) {
3045
+ const identities = Array.isArray(options.identities) ? options.identities : [];
3046
+ const selectors = options.selectors ?? DEFAULT_DOM_SELECTORS;
3047
+ const mode = options.mode ?? Mutation.Mode.Added;
3048
+ const parseLinks = typeof options.parseLinks === "function" ? options.parseLinks : Utils.parseLinks;
3049
+ const onMatch = typeof options.onMatch === "function" ? options.onMatch : null;
3050
+ let matched = false;
3051
+ const monitor = await Mutation.useMonitor(page, selectors, {
3052
+ mode,
3053
+ onMutation: (context = {}) => {
3054
+ if (matched) return;
3055
+ const html = String(context.html || "");
3056
+ const text = String(context.text || "");
3057
+ const rawDom = `${html}
3058
+ ${text}`;
3059
+ const [candidate] = parseLinks(rawDom, { identities }) || [];
3060
+ if (!candidate) return;
3061
+ matched = true;
3062
+ if (onMatch) {
3063
+ onMatch({
3064
+ link: candidate,
3065
+ rawDom,
3066
+ mutationCount: context.mutationCount || 0,
3067
+ html,
3068
+ text,
3069
+ mutationNodes: Array.isArray(context.mutationNodes) ? context.mutationNodes : []
3070
+ });
3071
+ }
3072
+ }
3073
+ });
3074
+ return {
3075
+ stop: async () => {
3076
+ return await monitor.stop();
3077
+ }
3078
+ };
3079
+ }
3080
+ };
3081
+
3082
+ // src/share.js
3028
3083
  var DEFAULT_TIMEOUT_AFTER_ACTION_MS = 10 * 1e3;
3029
3084
  var DEFAULT_PAYLOAD_SNAPSHOT_MAX_LEN = 500;
3030
3085
  var Share = {
3031
3086
  /**
3032
- * 捕获分享链接(仅通过接口响应文本匹配,不依赖 DOM / 剪切板)
3087
+ * 捕获分享链接(接口响应 + DOM 监听 双通道)
3033
3088
  *
3034
3089
  * @param {import('playwright').Page} page
3035
3090
  * @param {Object} [options]
3036
3091
  * @param {string[]} [options.identities]
3037
3092
  * @param {number} [options.timeoutAfterActionMs]
3038
3093
  * @param {number} [options.payloadSnapshotMaxLen]
3094
+ * @param {boolean} [options.enableResponse=true]
3095
+ * @param {boolean} [options.enableDom=true]
3096
+ * @param {string | string[]} [options.domSelectors='html']
3097
+ * @param {'added' | 'changed' | 'all'} [options.domMode='added']
3039
3098
  * @param {(response: import('playwright').Response) => boolean | Promise<boolean>} [options.responseFilter]
3040
3099
  * @param {(text: string, options?: { identities?: string[] }) => string[]} [options.parseLinks]
3041
3100
  * @param {() => Promise<void>} [options.performActions]
3042
- * @returns {Promise<{ link: string | null; payloadText: string; payloadSnapshot: string; source: 'response' | 'none' }>}
3101
+ * @returns {Promise<{ link: string | null; payloadText: string; payloadSnapshot: string; source: 'response' | 'dom' | 'none' }>}
3043
3102
  */
3044
3103
  async captureLink(page, options = {}) {
3045
3104
  const identities = Array.isArray(options.identities) ? options.identities : [];
3046
3105
  const timeoutAfterActionMs = options.timeoutAfterActionMs ?? DEFAULT_TIMEOUT_AFTER_ACTION_MS;
3047
3106
  const payloadSnapshotMaxLen = options.payloadSnapshotMaxLen ?? DEFAULT_PAYLOAD_SNAPSHOT_MAX_LEN;
3107
+ const enableResponse = options.enableResponse !== false;
3108
+ const enableDom = options.enableDom !== false;
3109
+ const domSelectors = options.domSelectors ?? "html";
3110
+ const domMode = options.domMode ?? Mutation.Mode.All;
3048
3111
  const responseFilter = typeof options.responseFilter === "function" ? options.responseFilter : null;
3049
3112
  const parseLinks = typeof options.parseLinks === "function" ? options.parseLinks : Utils.parseLinks;
3050
3113
  const performActions = typeof options.performActions === "function" ? options.performActions : async () => {
3051
3114
  };
3115
+ if (!enableResponse && !enableDom) {
3116
+ throw new Error("Share.captureLink requires at least one channel: response or dom");
3117
+ }
3052
3118
  let link = null;
3053
3119
  let payloadText = "";
3120
+ let source = "none";
3054
3121
  let resolveMatched = null;
3122
+ let domMonitor = null;
3055
3123
  const matchedPromise = new Promise((resolve) => {
3056
3124
  resolveMatched = resolve;
3057
3125
  });
3126
+ const finalizeMatch = (candidate, matchedSource, payload) => {
3127
+ if (link || !candidate) return false;
3128
+ link = candidate;
3129
+ source = matchedSource;
3130
+ payloadText = String(payload || "");
3131
+ if (resolveMatched) resolveMatched(candidate);
3132
+ return true;
3133
+ };
3134
+ const stopDomMonitor = async () => {
3135
+ if (!domMonitor) return;
3136
+ const monitor = domMonitor;
3137
+ domMonitor = null;
3138
+ try {
3139
+ await monitor.stop();
3140
+ } catch {
3141
+ }
3142
+ };
3058
3143
  const onResponse = async (response) => {
3059
3144
  if (link) return;
3060
3145
  try {
@@ -3066,14 +3151,31 @@ var Share = {
3066
3151
  if (!text) return;
3067
3152
  const [candidate] = parseLinks(text, { identities }) || [];
3068
3153
  if (!candidate) return;
3069
- link = candidate;
3070
- payloadText = String(text || "");
3071
- if (resolveMatched) resolveMatched(candidate);
3154
+ const matched = finalizeMatch(candidate, "response", text);
3155
+ if (!matched) return;
3072
3156
  page.off("response", onResponse);
3157
+ void stopDomMonitor();
3073
3158
  } catch {
3074
3159
  }
3075
3160
  };
3076
- page.on("response", onResponse);
3161
+ if (enableDom) {
3162
+ domMonitor = await Monitor.useShareLinkMonitor(page, {
3163
+ identities,
3164
+ selectors: domSelectors,
3165
+ mode: domMode,
3166
+ parseLinks,
3167
+ onMatch: ({ link: domLink, rawDom }) => {
3168
+ const matched = finalizeMatch(domLink, "dom", rawDom);
3169
+ if (!matched) return;
3170
+ if (enableResponse) {
3171
+ page.off("response", onResponse);
3172
+ }
3173
+ }
3174
+ });
3175
+ }
3176
+ if (enableResponse) {
3177
+ page.on("response", onResponse);
3178
+ }
3077
3179
  try {
3078
3180
  await performActions();
3079
3181
  if (!link && timeoutAfterActionMs > 0) {
@@ -3087,10 +3189,13 @@ var Share = {
3087
3189
  link,
3088
3190
  payloadText,
3089
3191
  payloadSnapshot,
3090
- source: link ? "response" : "none"
3192
+ source
3091
3193
  };
3092
3194
  } finally {
3093
- page.off("response", onResponse);
3195
+ if (enableResponse) {
3196
+ page.off("response", onResponse);
3197
+ }
3198
+ await stopDomMonitor();
3094
3199
  }
3095
3200
  },
3096
3201
  /**