opensteer 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +158 -165
  2. package/dist/{chunk-UM2Q4JD2.js → chunk-7D45QUZ3.js} +5 -7
  3. package/dist/chunk-7D45QUZ3.js.map +1 -0
  4. package/dist/{chunk-GREXSYNC.js → chunk-7LQL5YUR.js} +578 -224
  5. package/dist/chunk-7LQL5YUR.js.map +1 -0
  6. package/dist/{chunk-2TIVULZY.js → chunk-GSCQQKZZ.js} +53 -9
  7. package/dist/chunk-GSCQQKZZ.js.map +1 -0
  8. package/dist/{chunk-BMPUL66S.js → chunk-T5P2QGZ3.js} +58 -53
  9. package/dist/chunk-T5P2QGZ3.js.map +1 -0
  10. package/dist/{chunk-FIMNKEG5.js → chunk-ZRF7WMS3.js} +4 -4
  11. package/dist/{chunk-FIMNKEG5.js.map → chunk-ZRF7WMS3.js.map} +1 -1
  12. package/dist/cli/bin.cjs +707 -278
  13. package/dist/cli/bin.cjs.map +1 -1
  14. package/dist/cli/bin.js +30 -34
  15. package/dist/cli/bin.js.map +1 -1
  16. package/dist/index.cjs +733 -473
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +25 -460
  19. package/dist/index.d.ts +25 -460
  20. package/dist/index.js +4 -5
  21. package/dist/local-view/serve-entry.cjs +106 -57
  22. package/dist/local-view/serve-entry.cjs.map +1 -1
  23. package/dist/local-view/serve-entry.js +2 -2
  24. package/dist/opensteer-T2JENADR.js +6 -0
  25. package/dist/{opensteer-IBDPRIEX.js.map → opensteer-T2JENADR.js.map} +1 -1
  26. package/dist/{session-control-IFE3IPS3.js → session-control-M3JD7ZKA.js} +4 -4
  27. package/dist/{session-control-IFE3IPS3.js.map → session-control-M3JD7ZKA.js.map} +1 -1
  28. package/package.json +6 -6
  29. package/skills/opensteer/SKILL.md +134 -95
  30. package/skills/recorder/SKILL.md +43 -48
  31. package/dist/chunk-2TIVULZY.js.map +0 -1
  32. package/dist/chunk-BMPUL66S.js.map +0 -1
  33. package/dist/chunk-GREXSYNC.js.map +0 -1
  34. package/dist/chunk-KCINASQC.js +0 -3
  35. package/dist/chunk-KCINASQC.js.map +0 -1
  36. package/dist/chunk-UM2Q4JD2.js.map +0 -1
  37. package/dist/opensteer-IBDPRIEX.js +0 -6
  38. package/skills/recorder/references/recorder-reference.md +0 -71
@@ -1,5 +1,5 @@
1
- import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, OpensteerBrowserManager, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext } from './chunk-2TIVULZY.js';
2
- import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, sha256Hex, resolveBrandUserDataDir, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-BMPUL66S.js';
1
+ import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext, OpensteerBrowserManager } from './chunk-GSCQQKZZ.js';
2
+ import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, resolveBrandUserDataDir, sha256Hex, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-T5P2QGZ3.js';
3
3
  import { selectAll } from 'css-select';
4
4
  import { createHash, randomUUID, pbkdf2Sync, createDecipheriv } from 'crypto';
5
5
  import { existsSync, readFileSync } from 'fs';
@@ -7,7 +7,7 @@ import path2, { join } from 'path';
7
7
  import { promisify } from 'util';
8
8
  import { gzip as gzip$1 } from 'zlib';
9
9
  import { execFile as execFile$1, spawn } from 'child_process';
10
- import { rm, mkdtemp, writeFile, readFile, copyFile } from 'fs/promises';
10
+ import { rm, mkdtemp, copyFile, writeFile, readFile } from 'fs/promises';
11
11
  import { tmpdir } from 'os';
12
12
  import { AsyncLocalStorage } from 'async_hooks';
13
13
  import sharp from 'sharp';
@@ -307,6 +307,7 @@ var defaultNavigationSettleObserver = {
307
307
  return false;
308
308
  }
309
309
  try {
310
+ const startedAt = Date.now();
310
311
  await input.engine.waitForPostLoadQuiet({
311
312
  pageRef: input.pageRef,
312
313
  timeoutMs: effectiveTimeout,
@@ -314,9 +315,13 @@ var defaultNavigationSettleObserver = {
314
315
  captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
315
316
  signal: input.signal
316
317
  });
318
+ const visualTimeout = Math.max(0, effectiveTimeout - (Date.now() - startedAt));
319
+ if (visualTimeout <= 0) {
320
+ return true;
321
+ }
317
322
  await input.engine.waitForVisualStability({
318
323
  pageRef: input.pageRef,
319
- timeoutMs: effectiveTimeout,
324
+ timeoutMs: visualTimeout,
320
325
  settleMs: profile.settleMs,
321
326
  scope: profile.scope
322
327
  });
@@ -3743,6 +3748,20 @@ var opensteerBrowserContextOptionsSchema = objectSchema(
3743
3748
  {
3744
3749
  title: "OpensteerStealthProfileInput"
3745
3750
  }
3751
+ ),
3752
+ humanize: oneOfSchema(
3753
+ [
3754
+ { type: "boolean" },
3755
+ objectSchema(
3756
+ {
3757
+ mouse: { type: "boolean" },
3758
+ keyboard: { type: "boolean" },
3759
+ scroll: { type: "boolean" }
3760
+ },
3761
+ { title: "OpensteerHumanizeOptions" }
3762
+ )
3763
+ ],
3764
+ { title: "OpensteerHumanize" }
3746
3765
  )
3747
3766
  },
3748
3767
  {
@@ -4135,10 +4154,10 @@ var opensteerDomScrollInputSchema = objectSchema(
4135
4154
  required: ["target", "direction", "amount"]
4136
4155
  }
4137
4156
  );
4138
- var opensteerExtractSchemaSchema = objectSchema(
4157
+ var opensteerExtractTemplateSchema = objectSchema(
4139
4158
  {},
4140
4159
  {
4141
- title: "OpensteerExtractSchema",
4160
+ title: "OpensteerExtractTemplate",
4142
4161
  additionalProperties: true
4143
4162
  }
4144
4163
  );
@@ -4146,13 +4165,13 @@ var opensteerDomExtractInputSchema = defineSchema({
4146
4165
  ...objectSchema(
4147
4166
  {
4148
4167
  persist: stringSchema(),
4149
- schema: opensteerExtractSchemaSchema
4168
+ template: opensteerExtractTemplateSchema
4150
4169
  },
4151
4170
  {
4152
4171
  title: "OpensteerDomExtractInput"
4153
4172
  }
4154
4173
  ),
4155
- anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["schema"] })]
4174
+ anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["template"] })]
4156
4175
  });
4157
4176
  var jsonValueSchema2 = recordSchema({}, { title: "JsonValueRecord" });
4158
4177
  var opensteerDomExtractOutputSchema = objectSchema(
@@ -8903,9 +8922,9 @@ function isPersistedObjectNode(node) {
8903
8922
  }
8904
8923
 
8905
8924
  // ../runtime-core/src/sdk/extraction.ts
8906
- function assertValidOpensteerExtractionSchemaRoot(schema) {
8907
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
8908
- throw new Error("Invalid extraction schema: expected a JSON object at the top level.");
8925
+ function assertValidOpensteerExtractionTemplateRoot(template) {
8926
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
8927
+ throw new Error("Invalid extraction template: expected a JSON object at the top level.");
8909
8928
  }
8910
8929
  }
8911
8930
  function isPersistedOpensteerExtractionValueNode2(value) {
@@ -8927,12 +8946,12 @@ function isPersistedOpensteerExtractionArrayNode2(value) {
8927
8946
  return "$array" in value;
8928
8947
  }
8929
8948
  async function compileOpensteerExtractionFieldTargets(options) {
8930
- assertValidOpensteerExtractionSchemaRoot(options.schema);
8949
+ assertValidOpensteerExtractionTemplateRoot(options.template);
8931
8950
  const fields = [];
8932
- await collectFieldTargetsFromSchemaObject({
8951
+ await collectFieldTargetsFromTemplateObject({
8933
8952
  dom: options.dom,
8934
8953
  pageRef: options.pageRef,
8935
- value: options.schema,
8954
+ value: options.template,
8936
8955
  path: "",
8937
8956
  fields,
8938
8957
  insideArray: false
@@ -8984,13 +9003,13 @@ function createOpensteerExtractionDescriptorStore(options) {
8984
9003
  }
8985
9004
  return new MemoryOpensteerExtractionDescriptorStore(namespace);
8986
9005
  }
8987
- async function collectFieldTargetsFromSchemaObject(options) {
9006
+ async function collectFieldTargetsFromTemplateObject(options) {
8988
9007
  for (const [key, childValue] of Object.entries(options.value)) {
8989
9008
  const normalizedKey = normalizeKey(key);
8990
9009
  if (!normalizedKey) {
8991
9010
  continue;
8992
9011
  }
8993
- await collectFieldTargetsFromSchemaValue({
9012
+ await collectFieldTargetsFromTemplateValue({
8994
9013
  dom: options.dom,
8995
9014
  pageRef: options.pageRef,
8996
9015
  value: childValue,
@@ -9000,8 +9019,8 @@ async function collectFieldTargetsFromSchemaObject(options) {
9000
9019
  });
9001
9020
  }
9002
9021
  }
9003
- async function collectFieldTargetsFromSchemaValue(options) {
9004
- const normalizedField = normalizeSchemaField(options.value);
9022
+ async function collectFieldTargetsFromTemplateValue(options) {
9023
+ const normalizedField = normalizeTemplateField(options.value);
9005
9024
  if (normalizedField !== null) {
9006
9025
  options.fields.push(
9007
9026
  await compileFieldTarget({
@@ -9016,12 +9035,12 @@ async function collectFieldTargetsFromSchemaValue(options) {
9016
9035
  if (Array.isArray(options.value)) {
9017
9036
  if (options.insideArray) {
9018
9037
  throw new Error(
9019
- `Nested arrays are not supported in extraction schema at "${labelForPath(options.path)}".`
9038
+ `Nested arrays are not supported in extraction template at "${labelForPath(options.path)}".`
9020
9039
  );
9021
9040
  }
9022
9041
  if (options.value.length === 0) {
9023
9042
  throw new Error(
9024
- `Extraction array "${labelForPath(options.path)}" must include at least one representative item.`
9043
+ `Extraction array "${labelForPath(options.path)}" must include at least one representative template item.`
9025
9044
  );
9026
9045
  }
9027
9046
  for (let index = 0; index < options.value.length; index += 1) {
@@ -9032,7 +9051,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9032
9051
  );
9033
9052
  }
9034
9053
  const fieldCountBeforeItem = options.fields.length;
9035
- await collectFieldTargetsFromSchemaObject({
9054
+ await collectFieldTargetsFromTemplateObject({
9036
9055
  dom: options.dom,
9037
9056
  pageRef: options.pageRef,
9038
9057
  value: itemValue,
@@ -9043,7 +9062,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9043
9062
  const itemFields = options.fields.slice(fieldCountBeforeItem);
9044
9063
  if (!itemFields.some((field) => !("source" in field))) {
9045
9064
  throw new Error(
9046
- `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element- or selector-backed field.`
9065
+ `Extraction array "${labelForPath(options.path)}" item ${String(index)} must include at least one element number or selector field.`
9047
9066
  );
9048
9067
  }
9049
9068
  }
@@ -9051,10 +9070,10 @@ async function collectFieldTargetsFromSchemaValue(options) {
9051
9070
  }
9052
9071
  if (!options.value || typeof options.value !== "object") {
9053
9072
  throw new Error(
9054
- `Invalid extraction schema value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
9073
+ `Invalid extraction template value at "${labelForPath(options.path)}": expected an object, array, or field descriptor.`
9055
9074
  );
9056
9075
  }
9057
- await collectFieldTargetsFromSchemaObject({
9076
+ await collectFieldTargetsFromTemplateObject({
9058
9077
  dom: options.dom,
9059
9078
  pageRef: options.pageRef,
9060
9079
  value: options.value,
@@ -9086,7 +9105,7 @@ async function compileFieldTarget(options) {
9086
9105
  path: await resolveSelectorFieldPath({
9087
9106
  dom: options.dom,
9088
9107
  pageRef: options.pageRef,
9089
- selector: `[c="${String(options.field.element)}"]`
9108
+ selector: `[c="${String(options.field.c)}"]`
9090
9109
  }),
9091
9110
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
9092
9111
  };
@@ -9387,24 +9406,29 @@ function countNonNullLeaves(value) {
9387
9406
  }
9388
9407
  return Object.values(value).reduce((sum, item) => sum + countNonNullLeaves(item), 0);
9389
9408
  }
9390
- function normalizeSchemaField(value) {
9409
+ function normalizeTemplateField(value) {
9410
+ if (typeof value === "number") {
9411
+ return {
9412
+ c: normalizeExtractionCounter(value)
9413
+ };
9414
+ }
9391
9415
  if (!value || typeof value !== "object" || Array.isArray(value)) {
9392
9416
  return null;
9393
9417
  }
9394
9418
  const raw = value;
9395
- const hasElement = raw.element !== void 0;
9419
+ const hasCounter = raw.c !== void 0 || raw.element !== void 0;
9396
9420
  const hasSelector = raw.selector !== void 0;
9397
9421
  const hasSource = raw.source !== void 0;
9398
- const targetCount = Number(hasElement) + Number(hasSelector) + Number(hasSource);
9422
+ const targetCount = Number(hasCounter) + Number(hasSelector) + Number(hasSource);
9399
9423
  if (targetCount === 0) {
9400
9424
  return null;
9401
9425
  }
9402
9426
  if (targetCount !== 1) {
9403
9427
  throw new Error(
9404
- "Extraction field descriptors must specify exactly one of element, selector, or source."
9428
+ "Extraction field descriptors must specify exactly one of c/element, selector, or source."
9405
9429
  );
9406
9430
  }
9407
- const attribute = raw.attribute === void 0 ? void 0 : normalizeNonEmptyString("attribute", raw.attribute);
9431
+ const attribute = raw.attr !== void 0 ? normalizeNonEmptyString("attr", raw.attr) : raw.attribute === void 0 ? void 0 : normalizeNonEmptyString("attribute", raw.attribute);
9408
9432
  if (hasSource) {
9409
9433
  if (raw.source !== "current_url") {
9410
9434
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -9419,17 +9443,20 @@ function normalizeSchemaField(value) {
9419
9443
  ...attribute === void 0 ? {} : { attribute }
9420
9444
  };
9421
9445
  }
9422
- const element = Number(raw.element);
9423
- if (!Number.isInteger(element) || element < 1) {
9424
- throw new Error(
9425
- `Extraction field element must be a positive integer, received ${String(raw.element)}.`
9426
- );
9427
- }
9428
9446
  return {
9429
- element,
9447
+ c: normalizeExtractionCounter(raw.c ?? raw.element),
9430
9448
  ...attribute === void 0 ? {} : { attribute }
9431
9449
  };
9432
9450
  }
9451
+ function normalizeExtractionCounter(value) {
9452
+ const counter = Number(value);
9453
+ if (!Number.isInteger(counter) || counter < 1) {
9454
+ throw new Error(
9455
+ `Extraction element number must be a positive integer, received ${String(value)}.`
9456
+ );
9457
+ }
9458
+ return counter;
9459
+ }
9433
9460
  function normalizeNamespace(namespace) {
9434
9461
  const normalized = String(namespace ?? "default").trim();
9435
9462
  return normalized.length === 0 ? "default" : normalized;
@@ -9460,7 +9487,7 @@ function parseExtractionDescriptorRecord(record) {
9460
9487
  kind: "dom-extraction",
9461
9488
  persist: raw.persist,
9462
9489
  root,
9463
- ...typeof raw.schemaHash === "string" ? { schemaHash: raw.schemaHash } : {},
9490
+ ...typeof raw.templateHash === "string" ? { templateHash: raw.templateHash } : typeof raw.schemaHash === "string" ? { templateHash: raw.schemaHash } : {},
9464
9491
  ...typeof raw.sourceUrl === "string" ? { sourceUrl: raw.sourceUrl } : {}
9465
9492
  }
9466
9493
  };
@@ -9546,7 +9573,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
9546
9573
  kind: "dom-extraction",
9547
9574
  persist: input.persist,
9548
9575
  root: input.root,
9549
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9576
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9550
9577
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9551
9578
  };
9552
9579
  const key = persistKey(this.namespace, input.persist);
@@ -9595,7 +9622,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
9595
9622
  kind: "dom-extraction",
9596
9623
  persist: input.persist,
9597
9624
  root: input.root,
9598
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9625
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9599
9626
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9600
9627
  };
9601
9628
  const key = persistKey(this.namespace, input.persist);
@@ -10594,7 +10621,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10594
10621
 
10595
10622
  // ../runtime-core/package.json
10596
10623
  var package_default = {
10597
- version: "0.2.3"};
10624
+ version: "0.2.4"};
10598
10625
 
10599
10626
  // ../runtime-core/src/version.ts
10600
10627
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -11640,10 +11667,19 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
11640
11667
  // ../runtime-core/src/sdk/snapshot/cleaner.ts
11641
11668
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
11642
11669
  var TEXT_ATTR_MAX = 150;
11643
- var URL_ATTR_MAX = 500;
11644
- var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "srcset"]);
11670
+ var SRCSET_ATTR_MAX = 160;
11671
+ var MIDDLE_TRUNCATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "src"]);
11645
11672
  var TEXT_ATTRS = /* @__PURE__ */ new Set(["alt", "title", "aria-label", "placeholder", "value"]);
11646
- var TRUNCATION_SUFFIX = " [truncated]";
11673
+ var TRUNCATION_SUFFIX = "...";
11674
+ var MIDDLE_TRUNCATION_MARKER = "...";
11675
+ var MIDDLE_TRUNCATION_HEAD_MAX = 40;
11676
+ var MIDDLE_TRUNCATION_TAIL_MAX = 20;
11677
+ var SRCSET_CANDIDATE_HEAD_MAX = 36;
11678
+ var SRCSET_CANDIDATE_TAIL_MAX = 12;
11679
+ var SRCSET_COMPACT_CANDIDATE_HEAD_MAX = 20;
11680
+ var SRCSET_COMPACT_CANDIDATE_TAIL_MAX = 8;
11681
+ var SRCSET_FALLBACK_HEAD_MAX = 56;
11682
+ var SRCSET_FALLBACK_TAIL_MAX = 20;
11647
11683
  var NOISE_SELECTORS = [
11648
11684
  `[${OPENSTEER_HIDDEN_ATTR}]`,
11649
11685
  "[hidden]",
@@ -11706,19 +11742,260 @@ function truncateValue(value, max) {
11706
11742
  }
11707
11743
  return `${head}${TRUNCATION_SUFFIX}`;
11708
11744
  }
11745
+ function takeValueWithinSerializedLengthFromEnd(value, max) {
11746
+ let serializedLength = 0;
11747
+ const chars = [];
11748
+ for (let index = value.length - 1; index >= 0; index -= 1) {
11749
+ const char = value[index];
11750
+ let nextLength = 1;
11751
+ if (char === "&") {
11752
+ nextLength = 5;
11753
+ } else if (char === "<" || char === ">") {
11754
+ nextLength = 4;
11755
+ } else if (char === '"') {
11756
+ nextLength = 6;
11757
+ }
11758
+ if (serializedLength + nextLength > max) {
11759
+ break;
11760
+ }
11761
+ chars.push(char);
11762
+ serializedLength += nextLength;
11763
+ }
11764
+ return chars.reverse().join("");
11765
+ }
11766
+ function truncateValueInMiddle(value, headMax, tailMax, marker = MIDDLE_TRUNCATION_MARKER) {
11767
+ const markerLength = getSerializedLength(marker);
11768
+ const max = headMax + markerLength + tailMax;
11769
+ if (getSerializedLength(value) <= max) {
11770
+ return value;
11771
+ }
11772
+ const head = takeValueWithinSerializedLength(value, headMax).replace(/\s+$/u, "");
11773
+ const tail = takeValueWithinSerializedLengthFromEnd(value, tailMax).replace(/^\s+/u, "");
11774
+ if (head.length === 0) {
11775
+ return tail.length === 0 ? marker : `${marker}${tail}`;
11776
+ }
11777
+ if (tail.length === 0) {
11778
+ return `${head}${marker}`;
11779
+ }
11780
+ return `${head}${marker}${tail}`;
11781
+ }
11709
11782
  function getAttrLimit(attr) {
11710
- if (URL_ATTRS.has(attr)) {
11711
- return URL_ATTR_MAX;
11783
+ if (attr === "srcset") {
11784
+ return SRCSET_ATTR_MAX;
11712
11785
  }
11713
11786
  if (TEXT_ATTRS.has(attr)) {
11714
11787
  return TEXT_ATTR_MAX;
11715
11788
  }
11716
11789
  return void 0;
11717
11790
  }
11791
+ function shouldBoundAttr(attr) {
11792
+ return MIDDLE_TRUNCATED_URL_ATTRS.has(attr) || getAttrLimit(attr) !== void 0;
11793
+ }
11718
11794
  function setBoundedAttr(el, attr, value) {
11795
+ if (MIDDLE_TRUNCATED_URL_ATTRS.has(attr)) {
11796
+ el.attr(
11797
+ attr,
11798
+ truncateValueInMiddle(value, MIDDLE_TRUNCATION_HEAD_MAX, MIDDLE_TRUNCATION_TAIL_MAX)
11799
+ );
11800
+ return;
11801
+ }
11719
11802
  const limit = getAttrLimit(attr);
11803
+ if (attr === "srcset" && limit !== void 0) {
11804
+ el.attr(attr, truncateSrcsetValue(value, limit));
11805
+ return;
11806
+ }
11720
11807
  el.attr(attr, limit === void 0 ? value : truncateValue(value, limit));
11721
11808
  }
11809
+ function truncateSrcsetValue(value, max) {
11810
+ if (getSerializedLength(value) <= max) {
11811
+ return value;
11812
+ }
11813
+ const candidates = parseSrcsetCandidates2(value);
11814
+ if (candidates.length === 0) {
11815
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
11816
+ }
11817
+ for (const [headMax, tailMax, includeBest] of [
11818
+ [SRCSET_CANDIDATE_HEAD_MAX, SRCSET_CANDIDATE_TAIL_MAX, true],
11819
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, true],
11820
+ [SRCSET_COMPACT_CANDIDATE_HEAD_MAX, SRCSET_COMPACT_CANDIDATE_TAIL_MAX, false]
11821
+ ]) {
11822
+ const compact = buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest);
11823
+ if (getSerializedLength(compact) <= max) {
11824
+ return compact;
11825
+ }
11826
+ }
11827
+ return truncateValueInMiddle(value, SRCSET_FALLBACK_HEAD_MAX, SRCSET_FALLBACK_TAIL_MAX);
11828
+ }
11829
+ function buildTruncatedSrcsetValue(candidates, headMax, tailMax, includeBest) {
11830
+ const kept = getPreferredSrcsetCandidateIndices(candidates, includeBest);
11831
+ const parts = [];
11832
+ let previousIndex;
11833
+ for (const candidateIndex of kept) {
11834
+ if (previousIndex !== void 0 && candidateIndex - previousIndex > 1) {
11835
+ parts.push(MIDDLE_TRUNCATION_MARKER);
11836
+ }
11837
+ parts.push(formatSrcsetCandidate(candidates[candidateIndex], headMax, tailMax));
11838
+ previousIndex = candidateIndex;
11839
+ }
11840
+ return parts.join(", ");
11841
+ }
11842
+ function getPreferredSrcsetCandidateIndices(candidates, includeBest) {
11843
+ if (candidates.length === 0) {
11844
+ return [];
11845
+ }
11846
+ const kept = /* @__PURE__ */ new Set([0, candidates.length - 1]);
11847
+ if (includeBest) {
11848
+ kept.add(pickBestSrcsetCandidateIndex(candidates));
11849
+ }
11850
+ return [...kept].filter((index) => index >= 0 && index < candidates.length).sort((a, b) => a - b);
11851
+ }
11852
+ function pickBestSrcsetCandidateIndex(candidates) {
11853
+ let bestWidthIndex = -1;
11854
+ let bestWidth = -1;
11855
+ let bestDensityIndex = -1;
11856
+ let bestDensity = -1;
11857
+ for (let index = 0; index < candidates.length; index += 1) {
11858
+ const candidate = candidates[index];
11859
+ if (typeof candidate.width === "number" && Number.isFinite(candidate.width) && candidate.width > bestWidth) {
11860
+ bestWidth = candidate.width;
11861
+ bestWidthIndex = index;
11862
+ }
11863
+ if (typeof candidate.density === "number" && Number.isFinite(candidate.density) && candidate.density > bestDensity) {
11864
+ bestDensity = candidate.density;
11865
+ bestDensityIndex = index;
11866
+ }
11867
+ }
11868
+ if (bestWidthIndex >= 0) {
11869
+ return bestWidthIndex;
11870
+ }
11871
+ if (bestDensityIndex >= 0) {
11872
+ return bestDensityIndex;
11873
+ }
11874
+ return candidates.length - 1;
11875
+ }
11876
+ function formatSrcsetCandidate(candidate, headMax, tailMax) {
11877
+ const url = truncateValueInMiddle(candidate.url, headMax, tailMax);
11878
+ return candidate.descriptorText ? `${url} ${candidate.descriptorText}` : url;
11879
+ }
11880
+ function parseSrcsetCandidates2(raw) {
11881
+ const text = raw.trim();
11882
+ if (!text) {
11883
+ return [];
11884
+ }
11885
+ const out = [];
11886
+ let index = 0;
11887
+ while (index < text.length) {
11888
+ index = skipSrcsetSeparators(text, index);
11889
+ if (index >= text.length) {
11890
+ break;
11891
+ }
11892
+ const urlToken = readSrcsetUrlToken(text, index);
11893
+ index = urlToken.nextIndex;
11894
+ const url = urlToken.value.trim();
11895
+ if (!url) {
11896
+ continue;
11897
+ }
11898
+ index = skipSrcsetWhitespace(text, index);
11899
+ const descriptors = [];
11900
+ while (index < text.length && text[index] !== ",") {
11901
+ const descriptorToken = readSrcsetDescriptorToken(text, index);
11902
+ if (!descriptorToken.value) {
11903
+ index = descriptorToken.nextIndex;
11904
+ continue;
11905
+ }
11906
+ descriptors.push(descriptorToken.value);
11907
+ index = descriptorToken.nextIndex;
11908
+ index = skipSrcsetWhitespace(text, index);
11909
+ }
11910
+ if (index < text.length && text[index] === ",") {
11911
+ index += 1;
11912
+ }
11913
+ let width = null;
11914
+ let density = null;
11915
+ for (const descriptor of descriptors) {
11916
+ const token = descriptor.trim().toLowerCase();
11917
+ if (!token) {
11918
+ continue;
11919
+ }
11920
+ const widthMatch = token.match(/^(\d+)w$/);
11921
+ if (widthMatch) {
11922
+ const parsed = Number.parseInt(widthMatch[1], 10);
11923
+ if (Number.isFinite(parsed)) {
11924
+ width = parsed;
11925
+ }
11926
+ continue;
11927
+ }
11928
+ const densityMatch = token.match(/^(\d*\.?\d+)x$/);
11929
+ if (densityMatch) {
11930
+ const parsed = Number.parseFloat(densityMatch[1]);
11931
+ if (Number.isFinite(parsed)) {
11932
+ density = parsed;
11933
+ }
11934
+ }
11935
+ }
11936
+ out.push({
11937
+ url,
11938
+ descriptorText: descriptors.join(" "),
11939
+ width,
11940
+ density
11941
+ });
11942
+ }
11943
+ return out;
11944
+ }
11945
+ function skipSrcsetWhitespace(value, index) {
11946
+ let cursor = index;
11947
+ while (cursor < value.length && /\s/u.test(value[cursor])) {
11948
+ cursor += 1;
11949
+ }
11950
+ return cursor;
11951
+ }
11952
+ function skipSrcsetSeparators(value, index) {
11953
+ let cursor = skipSrcsetWhitespace(value, index);
11954
+ while (cursor < value.length && value[cursor] === ",") {
11955
+ cursor += 1;
11956
+ cursor = skipSrcsetWhitespace(value, cursor);
11957
+ }
11958
+ return cursor;
11959
+ }
11960
+ function readSrcsetUrlToken(value, index) {
11961
+ let cursor = index;
11962
+ let out = "";
11963
+ const isDataUrl = value.slice(index, index + 5).toLowerCase().startsWith("data:");
11964
+ while (cursor < value.length) {
11965
+ const char = value[cursor];
11966
+ if (/\s/u.test(char)) {
11967
+ break;
11968
+ }
11969
+ if (char === "," && !isDataUrl) {
11970
+ break;
11971
+ }
11972
+ out += char;
11973
+ cursor += 1;
11974
+ }
11975
+ if (isDataUrl && out.endsWith(",") && cursor < value.length) {
11976
+ out = out.slice(0, -1);
11977
+ }
11978
+ return {
11979
+ value: out,
11980
+ nextIndex: cursor
11981
+ };
11982
+ }
11983
+ function readSrcsetDescriptorToken(value, index) {
11984
+ let cursor = skipSrcsetWhitespace(value, index);
11985
+ let out = "";
11986
+ while (cursor < value.length) {
11987
+ const char = value[cursor];
11988
+ if (char === "," || /\s/u.test(char)) {
11989
+ break;
11990
+ }
11991
+ out += char;
11992
+ cursor += 1;
11993
+ }
11994
+ return {
11995
+ value: out.trim(),
11996
+ nextIndex: cursor
11997
+ };
11998
+ }
11722
11999
  function removeNoise($) {
11723
12000
  for (const tag of STRIP_TAGS) {
11724
12001
  $(tag).remove();
@@ -11743,38 +12020,68 @@ function markInlineSelfHiddenFallback($) {
11743
12020
  });
11744
12021
  }
11745
12022
  function pruneSelfHiddenNodes($) {
11746
- const nodes = [];
11747
- $(`[${OPENSTEER_SELF_HIDDEN_ATTR}]`).each(function collectSelfHiddenNodes() {
11748
- nodes.push($(this));
11749
- });
11750
- nodes.sort((left, right) => right.parents().length - left.parents().length);
11751
- for (const el of nodes) {
11752
- if (!el[0]) {
12023
+ for (const node of getElementsInReverseDocumentOrder($)) {
12024
+ if (node.attribs?.[OPENSTEER_SELF_HIDDEN_ATTR] === void 0) {
11753
12025
  continue;
11754
12026
  }
12027
+ const el = $(node);
11755
12028
  el.contents().each(function removeSelfHiddenText() {
11756
12029
  if (this.type === "text") {
11757
12030
  $(this).remove();
11758
12031
  }
11759
12032
  });
11760
- if (el.children().length === 0) {
12033
+ if (!hasElementChildren(node)) {
11761
12034
  el.remove();
11762
12035
  }
11763
12036
  }
11764
12037
  }
11765
- function hasDirectText($, el) {
11766
- return el.contents().filter(function hasDirectNodeText() {
11767
- return this.type === "text" && $(this).text().trim() !== "";
11768
- }).length > 0;
12038
+ function getChildNodes(node) {
12039
+ return node?.children ?? [];
11769
12040
  }
11770
- function hasTextDeep(el) {
11771
- return el.text().trim().length > 0;
12041
+ function isElementLikeNode(node) {
12042
+ return node?.type === "tag" || node?.type === "script" || node?.type === "style";
12043
+ }
12044
+ function hasDirectText(node) {
12045
+ if (!node) {
12046
+ return false;
12047
+ }
12048
+ for (const child of getChildNodes(node)) {
12049
+ if (child.type === "text" && (child.data || "").trim() !== "") {
12050
+ return true;
12051
+ }
12052
+ }
12053
+ return false;
12054
+ }
12055
+ function hasElementChildren(node) {
12056
+ if (!node) {
12057
+ return false;
12058
+ }
12059
+ for (const child of getChildNodes(node)) {
12060
+ if (isElementLikeNode(child)) {
12061
+ return true;
12062
+ }
12063
+ }
12064
+ return false;
12065
+ }
12066
+ function hasTextDeepNode(node) {
12067
+ if (!node) {
12068
+ return false;
12069
+ }
12070
+ if (node.type === "text") {
12071
+ return (node.data || "").trim() !== "";
12072
+ }
12073
+ for (const child of getChildNodes(node)) {
12074
+ if (hasTextDeepNode(child)) {
12075
+ return true;
12076
+ }
12077
+ }
12078
+ return false;
11772
12079
  }
11773
12080
  function hasActionLabel(attrs) {
11774
12081
  return typeof attrs["aria-label"] === "string" && attrs["aria-label"].trim() !== "" || typeof attrs["aria-labelledby"] === "string" && attrs["aria-labelledby"].trim() !== "" || typeof attrs["aria-describedby"] === "string" && attrs["aria-describedby"].trim() !== "" || typeof attrs.title === "string" && attrs.title.trim() !== "" || typeof attrs.placeholder === "string" && attrs.placeholder.trim() !== "" || typeof attrs.value === "string" && attrs.value.trim() !== "";
11775
12082
  }
11776
12083
  function unwrapActionNode($, el) {
11777
- if (hasTextDeep(el)) {
12084
+ if (hasTextDeepNode(el[0])) {
11778
12085
  if (el.prev().length > 0) {
11779
12086
  el.before(" ");
11780
12087
  }
@@ -11795,7 +12102,7 @@ function stripToAttrs(el, keep) {
11795
12102
  if (typeof value !== "string") {
11796
12103
  continue;
11797
12104
  }
11798
- if (getAttrLimit(attr) !== void 0) {
12105
+ if (shouldBoundAttr(attr)) {
11799
12106
  setBoundedAttr(el, attr, value);
11800
12107
  }
11801
12108
  }
@@ -11813,6 +12120,9 @@ function restoreBoundedAttr(el, attr, value) {
11813
12120
  function deduplicateImages(html) {
11814
12121
  const seen = /* @__PURE__ */ new Set();
11815
12122
  return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
12123
+ if (/\bc\s*=/.test(attrContent)) {
12124
+ return full;
12125
+ }
11816
12126
  const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
11817
12127
  const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
11818
12128
  let src = null;
@@ -11831,59 +12141,155 @@ function deduplicateImages(html) {
11831
12141
  return full;
11832
12142
  });
11833
12143
  }
11834
- function isPreservedImageElement($, el) {
11835
- const tag = (el[0]?.tagName || "").toLowerCase();
12144
+ function hasAttribute2(node, attr) {
12145
+ return node?.attribs?.[attr] !== void 0;
12146
+ }
12147
+ function hasPictureAncestor(node) {
12148
+ let current = node?.parent;
12149
+ while (current) {
12150
+ if (isElementLikeNode(current) && (current.tagName || "").toLowerCase() === "picture") {
12151
+ return true;
12152
+ }
12153
+ current = current.parent;
12154
+ }
12155
+ return false;
12156
+ }
12157
+ function pictureHasPreservedDescendant(node) {
12158
+ if (!node) {
12159
+ return false;
12160
+ }
12161
+ for (const child of getChildNodes(node)) {
12162
+ if (!isElementLikeNode(child)) {
12163
+ continue;
12164
+ }
12165
+ const tag = (child.tagName || "").toLowerCase();
12166
+ if (tag === "img") {
12167
+ return true;
12168
+ }
12169
+ if (tag === "source" && typeof child.attribs?.src === "string" && child.attribs.src.trim() !== "") {
12170
+ return true;
12171
+ }
12172
+ if (tag === "source" && typeof child.attribs?.srcset === "string" && child.attribs.srcset.trim() !== "") {
12173
+ return true;
12174
+ }
12175
+ if (pictureHasPreservedDescendant(child)) {
12176
+ return true;
12177
+ }
12178
+ }
12179
+ return false;
12180
+ }
12181
+ function isPreservedImageElement(node) {
12182
+ const tag = (node?.tagName || "").toLowerCase();
11836
12183
  if (tag === "img") {
11837
12184
  return true;
11838
12185
  }
11839
12186
  if (tag === "picture") {
11840
- const hasImg = el.find("img").length > 0;
11841
- const hasSource = el.find("source[src], source[srcset]").length > 0;
11842
- return hasImg || hasSource;
12187
+ return pictureHasPreservedDescendant(node);
11843
12188
  }
11844
12189
  if (tag === "source") {
11845
- const inPicture = el.parents("picture").length > 0;
11846
- const hasSrc = el.attr("src") != null && el.attr("src").trim() !== "" || el.attr("srcset") != null && el.attr("srcset").trim() !== "";
12190
+ const inPicture = hasPictureAncestor(node);
12191
+ const hasSrc = typeof node?.attribs?.src === "string" && node.attribs.src.trim() !== "" || typeof node?.attribs?.srcset === "string" && node.attribs.srcset.trim() !== "";
11847
12192
  return inPicture && hasSrc;
11848
12193
  }
11849
12194
  return false;
11850
12195
  }
12196
+ function getElementsInReverseDocumentOrder($) {
12197
+ return $.root().find("*").toArray().reverse().filter((node) => node.type === "tag");
12198
+ }
12199
+ function getNodeDepth(node) {
12200
+ let depth = 0;
12201
+ let current = node.parent;
12202
+ while (current) {
12203
+ depth++;
12204
+ current = current.parent;
12205
+ }
12206
+ return depth;
12207
+ }
12208
+ function getElementsByDepthDescending($) {
12209
+ const elements = $.root().find("*").toArray().filter((node) => node.type === "tag");
12210
+ const depths = /* @__PURE__ */ new Map();
12211
+ for (const el of elements) {
12212
+ depths.set(el, getNodeDepth(el));
12213
+ }
12214
+ return elements.sort((a, b) => (depths.get(b) ?? 0) - (depths.get(a) ?? 0));
12215
+ }
11851
12216
  function flattenExtractionTree($) {
11852
- const flatten = (root) => {
11853
- root.find("*").each(function flattenNode() {
11854
- const el = $(this);
11855
- const node = el[0];
11856
- if (!node) {
11857
- return;
11858
- }
11859
- const tag = (node.tagName || "").toLowerCase();
11860
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
11861
- return;
11862
- }
11863
- if (isPreservedImageElement($, el)) {
11864
- return;
11865
- }
11866
- if (tag === "a") {
11867
- el.children().each(function flattenAnchorChild() {
11868
- flatten($(this));
11869
- });
11870
- return;
11871
- }
11872
- const hasText = hasDirectText($, el);
11873
- if (hasText) {
11874
- return;
11875
- }
11876
- if (el.children().length === 0) {
11877
- el.remove();
11878
- return;
12217
+ for (const node of getElementsInReverseDocumentOrder($)) {
12218
+ const el = $(node);
12219
+ const tag = (node.tagName || "").toLowerCase();
12220
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag) || isPreservedImageElement(node)) {
12221
+ continue;
12222
+ }
12223
+ if (tag === "a" || hasDirectText(node)) {
12224
+ continue;
12225
+ }
12226
+ if (!hasElementChildren(node)) {
12227
+ el.remove();
12228
+ continue;
12229
+ }
12230
+ el.replaceWith(el.contents());
12231
+ }
12232
+ }
12233
+ function hasMarkedAncestor(el, attr) {
12234
+ let current = el[0]?.parent;
12235
+ while (current) {
12236
+ if (!isElementLikeNode(current)) {
12237
+ return false;
12238
+ }
12239
+ if (current.attribs?.[attr] !== void 0) {
12240
+ return true;
12241
+ }
12242
+ current = current.parent;
12243
+ }
12244
+ return false;
12245
+ }
12246
+ function isIndicatorImage(node) {
12247
+ return (node?.tagName || "").toLowerCase() === "img" && (hasAttribute2(node, "alt") || hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12248
+ }
12249
+ function isIndicatorPictureSource(node) {
12250
+ return (node?.tagName || "").toLowerCase() === "source" && hasPictureAncestor(node) && (hasAttribute2(node, "src") || hasAttribute2(node, "srcset"));
12251
+ }
12252
+ function isSemanticIndicator(node) {
12253
+ const tag = (node?.tagName || "").toLowerCase();
12254
+ if (tag === "svg") {
12255
+ return true;
12256
+ }
12257
+ return hasAttribute2(node, "aria-label") || hasAttribute2(node, "title") || hasAttribute2(node, "data-icon") || node?.attribs?.role === "img";
12258
+ }
12259
+ function findIndicatorDescendant(root) {
12260
+ if (!root) {
12261
+ return void 0;
12262
+ }
12263
+ let firstImage;
12264
+ let firstSource;
12265
+ let firstSemantic;
12266
+ const visit = (node) => {
12267
+ if (!isElementLikeNode(node)) {
12268
+ return false;
12269
+ }
12270
+ if (isIndicatorImage(node)) {
12271
+ firstImage = node;
12272
+ return true;
12273
+ }
12274
+ if (firstSource === void 0 && isIndicatorPictureSource(node)) {
12275
+ firstSource = node;
12276
+ }
12277
+ if (firstSemantic === void 0 && isSemanticIndicator(node)) {
12278
+ firstSemantic = node;
12279
+ }
12280
+ for (const child of getChildNodes(node)) {
12281
+ if (visit(child)) {
12282
+ return true;
11879
12283
  }
11880
- el.children().each(function flattenChild() {
11881
- flatten($(this));
11882
- });
11883
- el.replaceWith(el.contents());
11884
- });
12284
+ }
12285
+ return false;
11885
12286
  };
11886
- flatten($.root());
12287
+ for (const child of getChildNodes(root)) {
12288
+ if (visit(child)) {
12289
+ return firstImage;
12290
+ }
12291
+ }
12292
+ return firstImage ?? firstSource ?? firstSemantic;
11887
12293
  }
11888
12294
  function serializeForExtraction($, root) {
11889
12295
  const lines = [];
@@ -11952,7 +12358,7 @@ function serializeForExtraction($, root) {
11952
12358
  lines.push(`${" ".repeat(depth)}</${tagName}>`);
11953
12359
  }
11954
12360
  traverse(root, 0);
11955
- return lines.join("\n");
12361
+ return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
11956
12362
  }
11957
12363
  function isClickable($, el, context) {
11958
12364
  if (context.hasPreMarked) {
@@ -12044,7 +12450,11 @@ function cleanForExtraction(html) {
12044
12450
  }
12045
12451
  });
12046
12452
  flattenExtractionTree($clean);
12047
- return deduplicateImages(serializeForExtraction($clean, $clean.root()[0]));
12453
+ const root = $clean.root()[0];
12454
+ if (root === void 0) {
12455
+ return "";
12456
+ }
12457
+ return deduplicateImages(serializeForExtraction($clean, root));
12048
12458
  }
12049
12459
  function cleanForAction(html) {
12050
12460
  if (!html.trim()) {
@@ -12070,22 +12480,12 @@ function cleanForAction(html) {
12070
12480
  $(`[${clickableMark}]`).each(function markIndicators() {
12071
12481
  const el = $(this);
12072
12482
  const wrapperAttrs = el.attr() || {};
12073
- if (hasTextDeep(el) || hasActionLabel(wrapperAttrs)) {
12074
- return;
12075
- }
12076
- const imageIndicator = el.find("img[alt], img[src], img[srcset]").first();
12077
- if (imageIndicator.length) {
12078
- imageIndicator.attr(indicatorMark, "1");
12483
+ if (hasTextDeepNode(el[0]) || hasActionLabel(wrapperAttrs)) {
12079
12484
  return;
12080
12485
  }
12081
- const pictureSourceIndicator = el.find("picture source[src], picture source[srcset]").first();
12082
- if (pictureSourceIndicator.length) {
12083
- pictureSourceIndicator.attr(indicatorMark, "1");
12084
- return;
12085
- }
12086
- const semanticIndicator = el.find('[aria-label], [title], [data-icon], [role="img"], svg').first();
12087
- if (semanticIndicator.length) {
12088
- semanticIndicator.attr(indicatorMark, "1");
12486
+ const indicatorNode = findIndicatorDescendant(el[0]);
12487
+ if (indicatorNode !== void 0) {
12488
+ $(indicatorNode).attr(indicatorMark, "1");
12089
12489
  }
12090
12490
  });
12091
12491
  $(`[${clickableMark}]`).each(function removeEmptyClickable() {
@@ -12095,7 +12495,7 @@ function cleanForAction(html) {
12095
12495
  if (NATIVE_INTERACTIVE_TAGS.has(tag) || tag === "a") {
12096
12496
  return;
12097
12497
  }
12098
- if (el.children().length > 0 || hasDirectText($, el)) {
12498
+ if (hasElementChildren(node) || hasDirectText(node)) {
12099
12499
  return;
12100
12500
  }
12101
12501
  const wrapperAttrs = el.attr() || {};
@@ -12121,46 +12521,31 @@ function cleanForAction(html) {
12121
12521
  current = ancestor.parent();
12122
12522
  }
12123
12523
  });
12124
- let changed = true;
12125
- while (changed) {
12126
- changed = false;
12127
- const nodes = [];
12128
- $("*").each(function collectNodes() {
12129
- nodes.push($(this));
12130
- });
12131
- nodes.sort((left, right) => right.parents().length - left.parents().length);
12132
- for (const el of nodes) {
12133
- const node = el[0];
12134
- if (!node) {
12135
- continue;
12136
- }
12137
- const tag = (node.tagName || "").toLowerCase();
12138
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
12139
- continue;
12140
- }
12141
- if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12142
- continue;
12143
- }
12144
- const insideClickable = el.parents(`[${clickableMark}]`).length > 0;
12145
- const preserveBranch = el.attr(branchMark) !== void 0;
12146
- const hasContent = el.children().length > 0 || hasDirectText($, el);
12147
- if (insideClickable || preserveBranch) {
12148
- if (!hasContent) {
12149
- el.remove();
12150
- } else {
12151
- unwrapActionNode($, el);
12152
- }
12153
- changed = true;
12154
- continue;
12155
- }
12524
+ for (const node of getElementsByDepthDescending($)) {
12525
+ const el = $(node);
12526
+ const tag = (node.tagName || "").toLowerCase();
12527
+ if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
12528
+ continue;
12529
+ }
12530
+ if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12531
+ continue;
12532
+ }
12533
+ const insideClickable = hasMarkedAncestor(el, clickableMark);
12534
+ const preserveBranch = el.attr(branchMark) !== void 0;
12535
+ const hasContent = hasElementChildren(node) || hasDirectText(node);
12536
+ if (insideClickable || preserveBranch) {
12156
12537
  if (!hasContent) {
12157
12538
  el.remove();
12158
- changed = true;
12159
- continue;
12539
+ } else {
12540
+ unwrapActionNode($, el);
12160
12541
  }
12161
- unwrapActionNode($, el);
12162
- changed = true;
12542
+ continue;
12543
+ }
12544
+ if (!hasContent) {
12545
+ el.remove();
12546
+ continue;
12163
12547
  }
12548
+ unwrapActionNode($, el);
12164
12549
  }
12165
12550
  $.root().find("*").contents().each(function normalizeActionTextNodes() {
12166
12551
  if (this.type !== "text") {
@@ -12240,21 +12625,7 @@ function cleanForAction(html) {
12240
12625
  OPENSTEER_SPARSE_COUNTER_ATTR
12241
12626
  ]);
12242
12627
  if (clickable) {
12243
- for (const attr of [
12244
- "href",
12245
- "role",
12246
- "type",
12247
- "title",
12248
- "placeholder",
12249
- "value",
12250
- "aria-label",
12251
- "aria-labelledby",
12252
- "aria-describedby",
12253
- "aria-expanded",
12254
- "aria-pressed",
12255
- "aria-selected",
12256
- "aria-haspopup"
12257
- ]) {
12628
+ for (const attr of ["href", "role", "type", "title", "placeholder", "value", "aria-label"]) {
12258
12629
  keep.add(attr);
12259
12630
  }
12260
12631
  }
@@ -12874,9 +13245,9 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
12874
13245
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
12875
13246
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
12876
13247
  const attributes = [...authoredAttributes];
12877
- const subtreeHidden = hasAttribute2(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
12878
- const selfHidden = !subtreeHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
12879
- const interactive = !subtreeHidden && !selfHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
13248
+ const subtreeHidden = hasAttribute3(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
13249
+ const selfHidden = !subtreeHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
13250
+ const interactive = !subtreeHidden && !selfHidden && (hasAttribute3(snapshotAttributes, OPENSTEER_INTERACTIVE_ATTR) || isLikelyInteractive(tagName, node, authoredAttributes));
12880
13251
  if (interactive) {
12881
13252
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
12882
13253
  }
@@ -13194,7 +13565,7 @@ function parseOpacity(value) {
13194
13565
  const parsed = Number.parseFloat(value);
13195
13566
  return Number.isFinite(parsed) ? parsed : Number.NaN;
13196
13567
  }
13197
- function hasAttribute2(attributes, name) {
13568
+ function hasAttribute3(attributes, name) {
13198
13569
  const normalizedName = name.toLowerCase();
13199
13570
  return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
13200
13571
  }
@@ -14410,6 +14781,7 @@ function diffInteractionTraces(left, right) {
14410
14781
  // ../runtime-core/src/sdk/runtime.ts
14411
14782
  var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
14412
14783
  var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
14784
+ var PERSISTED_NETWORK_SETTLE_POLL_MS = 25;
14413
14785
  var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
14414
14786
  var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
14415
14787
  var REPLAY_PROBE_MIN_ATTEMPT_TIMEOUT_MS = 3e3;
@@ -15154,12 +15526,12 @@ var OpensteerSessionRuntime = class {
15154
15526
  async (timeout) => {
15155
15527
  let descriptor2;
15156
15528
  let data;
15157
- if (input.schema !== void 0) {
15158
- assertValidOpensteerExtractionSchemaRoot(input.schema);
15529
+ if (input.template !== void 0) {
15530
+ assertValidOpensteerExtractionTemplateRoot(input.template);
15159
15531
  const fieldTargets = await timeout.runStep(
15160
15532
  () => compileOpensteerExtractionFieldTargets({
15161
15533
  pageRef,
15162
- schema: input.schema,
15534
+ template: input.template,
15163
15535
  dom: this.requireDom()
15164
15536
  })
15165
15537
  );
@@ -15191,7 +15563,7 @@ var OpensteerSessionRuntime = class {
15191
15563
  () => descriptors.write({
15192
15564
  persist,
15193
15565
  root: payload,
15194
- schemaHash: canonicalJsonString(input.schema),
15566
+ templateHash: canonicalJsonString(input.template),
15195
15567
  sourceUrl: pageInfo.url
15196
15568
  })
15197
15569
  );
@@ -15251,7 +15623,7 @@ var OpensteerSessionRuntime = class {
15251
15623
  artifacts,
15252
15624
  data: {
15253
15625
  ...input.persist === void 0 ? {} : { persist: input.persist },
15254
- ...descriptor?.payload.schemaHash === void 0 ? {} : { schemaHash: descriptor.payload.schemaHash },
15626
+ ...descriptor?.payload.templateHash === void 0 ? {} : { templateHash: descriptor.payload.templateHash },
15255
15627
  data: output.data
15256
15628
  },
15257
15629
  context: buildRuntimeTraceContext({
@@ -17184,15 +17556,22 @@ var OpensteerSessionRuntime = class {
17184
17556
  return [];
17185
17557
  }
17186
17558
  const root = await this.ensureRoot();
17187
- const browserRecords = await this.readBrowserNetworkRecords(
17188
- {
17189
- includeBodies: true,
17190
- includeCurrentPageOnly: options.includeCurrentPageOnly,
17191
- ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
17192
- requestIds
17193
- },
17194
- signal
17195
- );
17559
+ let browserRecords = [];
17560
+ for (; ; ) {
17561
+ browserRecords = await this.readBrowserNetworkRecords(
17562
+ {
17563
+ includeBodies: true,
17564
+ includeCurrentPageOnly: options.includeCurrentPageOnly,
17565
+ ...options.pageRef === void 0 ? {} : { pageRef: options.pageRef },
17566
+ requestIds
17567
+ },
17568
+ signal
17569
+ );
17570
+ if (browserRecords.length === requestIds.length && browserRecords.every((record) => record.captureState !== "pending")) {
17571
+ break;
17572
+ }
17573
+ await delayWithSignal(PERSISTED_NETWORK_SETTLE_POLL_MS, signal);
17574
+ }
17196
17575
  return this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
17197
17576
  bodyWriteMode: "authoritative",
17198
17577
  redactSecretHeaders: false
@@ -22300,39 +22679,10 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
22300
22679
  super(
22301
22680
  buildSharedRuntimeOptions({
22302
22681
  name: publicWorkspace ?? "default",
22682
+ ...options.rootDir === void 0 ? {} : { rootDir: options.rootDir },
22303
22683
  rootPath,
22304
22684
  ...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
22305
- ...options.browser === void 0 ? {} : { browser: options.browser },
22306
- ...options.launch === void 0 ? {} : { launch: options.launch },
22307
- ...options.context === void 0 ? {} : { context: options.context },
22308
- engineName,
22309
- ...options.engine === void 0 ? {} : { engine: options.engine },
22310
- ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
22311
- ...options.policy === void 0 ? {} : { policy: options.policy },
22312
- ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
22313
- ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
22314
- cleanupRootOnClose,
22315
- ...options.observability === void 0 ? {} : { observability: options.observability },
22316
- ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
22317
- ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
22318
- })
22319
- );
22320
- }
22321
- };
22322
- var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
22323
- constructor(options) {
22324
- const rootPath = options.rootPath ?? path2.resolve(options.rootDir ?? process.cwd());
22325
- const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
22326
- const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
22327
- assertSupportedEngineOptions({
22328
- engineName,
22329
- ...options.browser === void 0 ? {} : { browser: options.browser },
22330
- ...options.context === void 0 ? {} : { context: options.context }
22331
- });
22332
- super(
22333
- buildSharedRuntimeOptions({
22334
- name: options.name,
22335
- rootPath,
22685
+ ...options.environment === void 0 ? {} : { environment: options.environment },
22336
22686
  ...options.browser === void 0 ? {} : { browser: options.browser },
22337
22687
  ...options.launch === void 0 ? {} : { launch: options.launch },
22338
22688
  ...options.context === void 0 ? {} : { context: options.context },
@@ -22353,9 +22703,11 @@ var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
22353
22703
  function buildSharedRuntimeOptions(input) {
22354
22704
  const ownership = resolveOwnership(input.browser);
22355
22705
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
22706
+ ...input.rootDir === void 0 ? {} : { rootDir: input.rootDir },
22356
22707
  rootPath: input.rootPath,
22357
22708
  ...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
22358
22709
  engineName: input.engineName,
22710
+ ...input.environment === void 0 ? {} : { environment: input.environment },
22359
22711
  ...(factoryOptions.browser ?? input.browser) === void 0 ? {} : { browser: factoryOptions.browser ?? input.browser },
22360
22712
  ...(factoryOptions.launch ?? input.launch) === void 0 ? {} : { launch: factoryOptions.launch ?? input.launch },
22361
22713
  ...(factoryOptions.context ?? input.context) === void 0 ? {} : { context: factoryOptions.context ?? input.context }
@@ -22416,9 +22768,10 @@ function resolveOpensteerRuntimeConfig(input = {}) {
22416
22768
  function createOpensteerSemanticRuntime(input = {}) {
22417
22769
  const runtimeOptions = input.runtimeOptions ?? {};
22418
22770
  const engine = input.engine ?? runtimeOptions.engineName ?? DEFAULT_OPENSTEER_ENGINE;
22771
+ const environment = input.environment ?? process.env;
22419
22772
  const config = resolveOpensteerRuntimeConfig({
22420
22773
  ...input.provider === void 0 ? {} : { provider: input.provider },
22421
- ...input.environment === void 0 ? {} : { environment: input.environment }
22774
+ environment
22422
22775
  });
22423
22776
  assertProviderSupportsEngine(config.provider.mode, engine);
22424
22777
  if (config.provider.mode === "cloud") {
@@ -22433,10 +22786,11 @@ function createOpensteerSemanticRuntime(input = {}) {
22433
22786
  }
22434
22787
  return new OpensteerRuntime({
22435
22788
  ...runtimeOptions,
22436
- engineName: engine
22789
+ engineName: engine,
22790
+ environment
22437
22791
  });
22438
22792
  }
22439
22793
 
22440
- export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22441
- //# sourceMappingURL=chunk-GREXSYNC.js.map
22442
- //# sourceMappingURL=chunk-GREXSYNC.js.map
22794
+ export { CloudSessionProxy, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OpensteerCloudClient, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createDomDescriptorStore, createDomRuntime, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorPersist, isCurrentUrlField, isValidCssAttributeKey, loadEnvironment, normalizeExtractedValue, normalizeOpensteerProviderMode, parseDomDescriptorRecord, parseExtractionDescriptorRecord, requireCloudAppBaseUrl, resolveCloudConfig, resolveDomActionBridge, resolveExtractedValueInContext, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath };
22795
+ //# sourceMappingURL=chunk-7LQL5YUR.js.map
22796
+ //# sourceMappingURL=chunk-7LQL5YUR.js.map