opensteer 0.9.4 → 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.
@@ -1,5 +1,5 @@
1
- import { resolveFilesystemWorkspacePath, createFilesystemOpensteerWorkspace, DEFAULT_OPENSTEER_ENGINE, assertSupportedEngineOptions, OpensteerBrowserManager, normalizeObservabilityConfig, manifestToExternalBinaryLocation, normalizeObservationContext } from './chunk-GSCQQKZZ.js';
2
- import { canonicalJsonString, toCanonicalJsonValue, readPersistedCloudSessionRecord, writePersistedSessionRecord, clearPersistedSessionRecord, sha256Hex, resolveBrandUserDataDir, getBrowserBrand, detectInstalledBrowserBrands, __require } from './chunk-T5P2QGZ3.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
  });
@@ -4149,10 +4154,10 @@ var opensteerDomScrollInputSchema = objectSchema(
4149
4154
  required: ["target", "direction", "amount"]
4150
4155
  }
4151
4156
  );
4152
- var opensteerExtractSchemaSchema = objectSchema(
4157
+ var opensteerExtractTemplateSchema = objectSchema(
4153
4158
  {},
4154
4159
  {
4155
- title: "OpensteerExtractSchema",
4160
+ title: "OpensteerExtractTemplate",
4156
4161
  additionalProperties: true
4157
4162
  }
4158
4163
  );
@@ -4160,13 +4165,13 @@ var opensteerDomExtractInputSchema = defineSchema({
4160
4165
  ...objectSchema(
4161
4166
  {
4162
4167
  persist: stringSchema(),
4163
- schema: opensteerExtractSchemaSchema
4168
+ template: opensteerExtractTemplateSchema
4164
4169
  },
4165
4170
  {
4166
4171
  title: "OpensteerDomExtractInput"
4167
4172
  }
4168
4173
  ),
4169
- anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["schema"] })]
4174
+ anyOf: [defineSchema({ required: ["persist"] }), defineSchema({ required: ["template"] })]
4170
4175
  });
4171
4176
  var jsonValueSchema2 = recordSchema({}, { title: "JsonValueRecord" });
4172
4177
  var opensteerDomExtractOutputSchema = objectSchema(
@@ -8917,9 +8922,9 @@ function isPersistedObjectNode(node) {
8917
8922
  }
8918
8923
 
8919
8924
  // ../runtime-core/src/sdk/extraction.ts
8920
- function assertValidOpensteerExtractionSchemaRoot(schema) {
8921
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
8922
- 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.");
8923
8928
  }
8924
8929
  }
8925
8930
  function isPersistedOpensteerExtractionValueNode2(value) {
@@ -8941,12 +8946,12 @@ function isPersistedOpensteerExtractionArrayNode2(value) {
8941
8946
  return "$array" in value;
8942
8947
  }
8943
8948
  async function compileOpensteerExtractionFieldTargets(options) {
8944
- assertValidOpensteerExtractionSchemaRoot(options.schema);
8949
+ assertValidOpensteerExtractionTemplateRoot(options.template);
8945
8950
  const fields = [];
8946
- await collectFieldTargetsFromSchemaObject({
8951
+ await collectFieldTargetsFromTemplateObject({
8947
8952
  dom: options.dom,
8948
8953
  pageRef: options.pageRef,
8949
- value: options.schema,
8954
+ value: options.template,
8950
8955
  path: "",
8951
8956
  fields,
8952
8957
  insideArray: false
@@ -8998,13 +9003,13 @@ function createOpensteerExtractionDescriptorStore(options) {
8998
9003
  }
8999
9004
  return new MemoryOpensteerExtractionDescriptorStore(namespace);
9000
9005
  }
9001
- async function collectFieldTargetsFromSchemaObject(options) {
9006
+ async function collectFieldTargetsFromTemplateObject(options) {
9002
9007
  for (const [key, childValue] of Object.entries(options.value)) {
9003
9008
  const normalizedKey = normalizeKey(key);
9004
9009
  if (!normalizedKey) {
9005
9010
  continue;
9006
9011
  }
9007
- await collectFieldTargetsFromSchemaValue({
9012
+ await collectFieldTargetsFromTemplateValue({
9008
9013
  dom: options.dom,
9009
9014
  pageRef: options.pageRef,
9010
9015
  value: childValue,
@@ -9014,8 +9019,8 @@ async function collectFieldTargetsFromSchemaObject(options) {
9014
9019
  });
9015
9020
  }
9016
9021
  }
9017
- async function collectFieldTargetsFromSchemaValue(options) {
9018
- const normalizedField = normalizeSchemaField(options.value);
9022
+ async function collectFieldTargetsFromTemplateValue(options) {
9023
+ const normalizedField = normalizeTemplateField(options.value);
9019
9024
  if (normalizedField !== null) {
9020
9025
  options.fields.push(
9021
9026
  await compileFieldTarget({
@@ -9030,12 +9035,12 @@ async function collectFieldTargetsFromSchemaValue(options) {
9030
9035
  if (Array.isArray(options.value)) {
9031
9036
  if (options.insideArray) {
9032
9037
  throw new Error(
9033
- `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)}".`
9034
9039
  );
9035
9040
  }
9036
9041
  if (options.value.length === 0) {
9037
9042
  throw new Error(
9038
- `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.`
9039
9044
  );
9040
9045
  }
9041
9046
  for (let index = 0; index < options.value.length; index += 1) {
@@ -9046,7 +9051,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9046
9051
  );
9047
9052
  }
9048
9053
  const fieldCountBeforeItem = options.fields.length;
9049
- await collectFieldTargetsFromSchemaObject({
9054
+ await collectFieldTargetsFromTemplateObject({
9050
9055
  dom: options.dom,
9051
9056
  pageRef: options.pageRef,
9052
9057
  value: itemValue,
@@ -9057,7 +9062,7 @@ async function collectFieldTargetsFromSchemaValue(options) {
9057
9062
  const itemFields = options.fields.slice(fieldCountBeforeItem);
9058
9063
  if (!itemFields.some((field) => !("source" in field))) {
9059
9064
  throw new Error(
9060
- `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.`
9061
9066
  );
9062
9067
  }
9063
9068
  }
@@ -9065,10 +9070,10 @@ async function collectFieldTargetsFromSchemaValue(options) {
9065
9070
  }
9066
9071
  if (!options.value || typeof options.value !== "object") {
9067
9072
  throw new Error(
9068
- `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.`
9069
9074
  );
9070
9075
  }
9071
- await collectFieldTargetsFromSchemaObject({
9076
+ await collectFieldTargetsFromTemplateObject({
9072
9077
  dom: options.dom,
9073
9078
  pageRef: options.pageRef,
9074
9079
  value: options.value,
@@ -9100,7 +9105,7 @@ async function compileFieldTarget(options) {
9100
9105
  path: await resolveSelectorFieldPath({
9101
9106
  dom: options.dom,
9102
9107
  pageRef: options.pageRef,
9103
- selector: `[c="${String(options.field.element)}"]`
9108
+ selector: `[c="${String(options.field.c)}"]`
9104
9109
  }),
9105
9110
  ...options.field.attribute === void 0 ? {} : { attribute: options.field.attribute }
9106
9111
  };
@@ -9401,24 +9406,29 @@ function countNonNullLeaves(value) {
9401
9406
  }
9402
9407
  return Object.values(value).reduce((sum, item) => sum + countNonNullLeaves(item), 0);
9403
9408
  }
9404
- function normalizeSchemaField(value) {
9409
+ function normalizeTemplateField(value) {
9410
+ if (typeof value === "number") {
9411
+ return {
9412
+ c: normalizeExtractionCounter(value)
9413
+ };
9414
+ }
9405
9415
  if (!value || typeof value !== "object" || Array.isArray(value)) {
9406
9416
  return null;
9407
9417
  }
9408
9418
  const raw = value;
9409
- const hasElement = raw.element !== void 0;
9419
+ const hasCounter = raw.c !== void 0 || raw.element !== void 0;
9410
9420
  const hasSelector = raw.selector !== void 0;
9411
9421
  const hasSource = raw.source !== void 0;
9412
- const targetCount = Number(hasElement) + Number(hasSelector) + Number(hasSource);
9422
+ const targetCount = Number(hasCounter) + Number(hasSelector) + Number(hasSource);
9413
9423
  if (targetCount === 0) {
9414
9424
  return null;
9415
9425
  }
9416
9426
  if (targetCount !== 1) {
9417
9427
  throw new Error(
9418
- "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."
9419
9429
  );
9420
9430
  }
9421
- 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);
9422
9432
  if (hasSource) {
9423
9433
  if (raw.source !== "current_url") {
9424
9434
  throw new Error(`Unsupported extraction source "${String(raw.source)}".`);
@@ -9433,17 +9443,20 @@ function normalizeSchemaField(value) {
9433
9443
  ...attribute === void 0 ? {} : { attribute }
9434
9444
  };
9435
9445
  }
9436
- const element = Number(raw.element);
9437
- if (!Number.isInteger(element) || element < 1) {
9438
- throw new Error(
9439
- `Extraction field element must be a positive integer, received ${String(raw.element)}.`
9440
- );
9441
- }
9442
9446
  return {
9443
- element,
9447
+ c: normalizeExtractionCounter(raw.c ?? raw.element),
9444
9448
  ...attribute === void 0 ? {} : { attribute }
9445
9449
  };
9446
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
+ }
9447
9460
  function normalizeNamespace(namespace) {
9448
9461
  const normalized = String(namespace ?? "default").trim();
9449
9462
  return normalized.length === 0 ? "default" : normalized;
@@ -9474,7 +9487,7 @@ function parseExtractionDescriptorRecord(record) {
9474
9487
  kind: "dom-extraction",
9475
9488
  persist: raw.persist,
9476
9489
  root,
9477
- ...typeof raw.schemaHash === "string" ? { schemaHash: raw.schemaHash } : {},
9490
+ ...typeof raw.templateHash === "string" ? { templateHash: raw.templateHash } : typeof raw.schemaHash === "string" ? { templateHash: raw.schemaHash } : {},
9478
9491
  ...typeof raw.sourceUrl === "string" ? { sourceUrl: raw.sourceUrl } : {}
9479
9492
  }
9480
9493
  };
@@ -9560,7 +9573,7 @@ var FilesystemOpensteerExtractionDescriptorStore = class {
9560
9573
  kind: "dom-extraction",
9561
9574
  persist: input.persist,
9562
9575
  root: input.root,
9563
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9576
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9564
9577
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9565
9578
  };
9566
9579
  const key = persistKey(this.namespace, input.persist);
@@ -9609,7 +9622,7 @@ var MemoryOpensteerExtractionDescriptorStore = class {
9609
9622
  kind: "dom-extraction",
9610
9623
  persist: input.persist,
9611
9624
  root: input.root,
9612
- ...input.schemaHash === void 0 ? {} : { schemaHash: input.schemaHash },
9625
+ ...input.templateHash === void 0 ? {} : { templateHash: input.templateHash },
9613
9626
  ...input.sourceUrl === void 0 ? {} : { sourceUrl: input.sourceUrl }
9614
9627
  };
9615
9628
  const key = persistKey(this.namespace, input.persist);
@@ -10608,7 +10621,7 @@ async function dispatchSemanticOperation(runtime, operation, input, options = {}
10608
10621
 
10609
10622
  // ../runtime-core/package.json
10610
10623
  var package_default = {
10611
- version: "0.2.3"};
10624
+ version: "0.2.4"};
10612
10625
 
10613
10626
  // ../runtime-core/src/version.ts
10614
10627
  var OPENSTEER_RUNTIME_CORE_VERSION = package_default.version;
@@ -11654,10 +11667,19 @@ var VOID_TAGS = /* @__PURE__ */ new Set([
11654
11667
  // ../runtime-core/src/sdk/snapshot/cleaner.ts
11655
11668
  var STRIP_TAGS = /* @__PURE__ */ new Set(["script", "style", "noscript", "meta", "link", "template"]);
11656
11669
  var TEXT_ATTR_MAX = 150;
11657
- var URL_ATTR_MAX = 500;
11658
- 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"]);
11659
11672
  var TEXT_ATTRS = /* @__PURE__ */ new Set(["alt", "title", "aria-label", "placeholder", "value"]);
11660
- 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;
11661
11683
  var NOISE_SELECTORS = [
11662
11684
  `[${OPENSTEER_HIDDEN_ATTR}]`,
11663
11685
  "[hidden]",
@@ -11720,19 +11742,260 @@ function truncateValue(value, max) {
11720
11742
  }
11721
11743
  return `${head}${TRUNCATION_SUFFIX}`;
11722
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
+ }
11723
11782
  function getAttrLimit(attr) {
11724
- if (URL_ATTRS.has(attr)) {
11725
- return URL_ATTR_MAX;
11783
+ if (attr === "srcset") {
11784
+ return SRCSET_ATTR_MAX;
11726
11785
  }
11727
11786
  if (TEXT_ATTRS.has(attr)) {
11728
11787
  return TEXT_ATTR_MAX;
11729
11788
  }
11730
11789
  return void 0;
11731
11790
  }
11791
+ function shouldBoundAttr(attr) {
11792
+ return MIDDLE_TRUNCATED_URL_ATTRS.has(attr) || getAttrLimit(attr) !== void 0;
11793
+ }
11732
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
+ }
11733
11802
  const limit = getAttrLimit(attr);
11803
+ if (attr === "srcset" && limit !== void 0) {
11804
+ el.attr(attr, truncateSrcsetValue(value, limit));
11805
+ return;
11806
+ }
11734
11807
  el.attr(attr, limit === void 0 ? value : truncateValue(value, limit));
11735
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
+ }
11736
11999
  function removeNoise($) {
11737
12000
  for (const tag of STRIP_TAGS) {
11738
12001
  $(tag).remove();
@@ -11757,38 +12020,68 @@ function markInlineSelfHiddenFallback($) {
11757
12020
  });
11758
12021
  }
11759
12022
  function pruneSelfHiddenNodes($) {
11760
- const nodes = [];
11761
- $(`[${OPENSTEER_SELF_HIDDEN_ATTR}]`).each(function collectSelfHiddenNodes() {
11762
- nodes.push($(this));
11763
- });
11764
- nodes.sort((left, right) => right.parents().length - left.parents().length);
11765
- for (const el of nodes) {
11766
- if (!el[0]) {
12023
+ for (const node of getElementsInReverseDocumentOrder($)) {
12024
+ if (node.attribs?.[OPENSTEER_SELF_HIDDEN_ATTR] === void 0) {
11767
12025
  continue;
11768
12026
  }
12027
+ const el = $(node);
11769
12028
  el.contents().each(function removeSelfHiddenText() {
11770
12029
  if (this.type === "text") {
11771
12030
  $(this).remove();
11772
12031
  }
11773
12032
  });
11774
- if (el.children().length === 0) {
12033
+ if (!hasElementChildren(node)) {
11775
12034
  el.remove();
11776
12035
  }
11777
12036
  }
11778
12037
  }
11779
- function hasDirectText($, el) {
11780
- return el.contents().filter(function hasDirectNodeText() {
11781
- return this.type === "text" && $(this).text().trim() !== "";
11782
- }).length > 0;
12038
+ function getChildNodes(node) {
12039
+ return node?.children ?? [];
11783
12040
  }
11784
- function hasTextDeep(el) {
11785
- 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;
11786
12079
  }
11787
12080
  function hasActionLabel(attrs) {
11788
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() !== "";
11789
12082
  }
11790
12083
  function unwrapActionNode($, el) {
11791
- if (hasTextDeep(el)) {
12084
+ if (hasTextDeepNode(el[0])) {
11792
12085
  if (el.prev().length > 0) {
11793
12086
  el.before(" ");
11794
12087
  }
@@ -11809,7 +12102,7 @@ function stripToAttrs(el, keep) {
11809
12102
  if (typeof value !== "string") {
11810
12103
  continue;
11811
12104
  }
11812
- if (getAttrLimit(attr) !== void 0) {
12105
+ if (shouldBoundAttr(attr)) {
11813
12106
  setBoundedAttr(el, attr, value);
11814
12107
  }
11815
12108
  }
@@ -11827,6 +12120,9 @@ function restoreBoundedAttr(el, attr, value) {
11827
12120
  function deduplicateImages(html) {
11828
12121
  const seen = /* @__PURE__ */ new Set();
11829
12122
  return html.replace(/<img\b([^>]*)>/gi, (full, attrContent) => {
12123
+ if (/\bc\s*=/.test(attrContent)) {
12124
+ return full;
12125
+ }
11830
12126
  const srcMatch = attrContent.match(/\bsrc\s*=\s*(["']?)(.*?)\1/);
11831
12127
  const srcsetMatch = attrContent.match(/\bsrcset\s*=\s*(["'])(.*?)\1/);
11832
12128
  let src = null;
@@ -11845,59 +12141,155 @@ function deduplicateImages(html) {
11845
12141
  return full;
11846
12142
  });
11847
12143
  }
11848
- function isPreservedImageElement($, el) {
11849
- 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();
11850
12183
  if (tag === "img") {
11851
12184
  return true;
11852
12185
  }
11853
12186
  if (tag === "picture") {
11854
- const hasImg = el.find("img").length > 0;
11855
- const hasSource = el.find("source[src], source[srcset]").length > 0;
11856
- return hasImg || hasSource;
12187
+ return pictureHasPreservedDescendant(node);
11857
12188
  }
11858
12189
  if (tag === "source") {
11859
- const inPicture = el.parents("picture").length > 0;
11860
- 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() !== "";
11861
12192
  return inPicture && hasSrc;
11862
12193
  }
11863
12194
  return false;
11864
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
+ }
11865
12216
  function flattenExtractionTree($) {
11866
- const flatten = (root) => {
11867
- root.find("*").each(function flattenNode() {
11868
- const el = $(this);
11869
- const node = el[0];
11870
- if (!node) {
11871
- return;
11872
- }
11873
- const tag = (node.tagName || "").toLowerCase();
11874
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
11875
- return;
11876
- }
11877
- if (isPreservedImageElement($, el)) {
11878
- return;
11879
- }
11880
- if (tag === "a") {
11881
- el.children().each(function flattenAnchorChild() {
11882
- flatten($(this));
11883
- });
11884
- return;
11885
- }
11886
- const hasText = hasDirectText($, el);
11887
- if (hasText) {
11888
- return;
11889
- }
11890
- if (el.children().length === 0) {
11891
- el.remove();
11892
- 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;
11893
12283
  }
11894
- el.children().each(function flattenChild() {
11895
- flatten($(this));
11896
- });
11897
- el.replaceWith(el.contents());
11898
- });
12284
+ }
12285
+ return false;
11899
12286
  };
11900
- flatten($.root());
12287
+ for (const child of getChildNodes(root)) {
12288
+ if (visit(child)) {
12289
+ return firstImage;
12290
+ }
12291
+ }
12292
+ return firstImage ?? firstSource ?? firstSemantic;
11901
12293
  }
11902
12294
  function serializeForExtraction($, root) {
11903
12295
  const lines = [];
@@ -11966,7 +12358,7 @@ function serializeForExtraction($, root) {
11966
12358
  lines.push(`${" ".repeat(depth)}</${tagName}>`);
11967
12359
  }
11968
12360
  traverse(root, 0);
11969
- return lines.join("\n");
12361
+ return lines.map((l) => l.trim()).filter((l) => l.length > 0).join("");
11970
12362
  }
11971
12363
  function isClickable($, el, context) {
11972
12364
  if (context.hasPreMarked) {
@@ -12058,7 +12450,11 @@ function cleanForExtraction(html) {
12058
12450
  }
12059
12451
  });
12060
12452
  flattenExtractionTree($clean);
12061
- 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));
12062
12458
  }
12063
12459
  function cleanForAction(html) {
12064
12460
  if (!html.trim()) {
@@ -12084,22 +12480,12 @@ function cleanForAction(html) {
12084
12480
  $(`[${clickableMark}]`).each(function markIndicators() {
12085
12481
  const el = $(this);
12086
12482
  const wrapperAttrs = el.attr() || {};
12087
- if (hasTextDeep(el) || hasActionLabel(wrapperAttrs)) {
12483
+ if (hasTextDeepNode(el[0]) || hasActionLabel(wrapperAttrs)) {
12088
12484
  return;
12089
12485
  }
12090
- const imageIndicator = el.find("img[alt], img[src], img[srcset]").first();
12091
- if (imageIndicator.length) {
12092
- imageIndicator.attr(indicatorMark, "1");
12093
- return;
12094
- }
12095
- const pictureSourceIndicator = el.find("picture source[src], picture source[srcset]").first();
12096
- if (pictureSourceIndicator.length) {
12097
- pictureSourceIndicator.attr(indicatorMark, "1");
12098
- return;
12099
- }
12100
- const semanticIndicator = el.find('[aria-label], [title], [data-icon], [role="img"], svg').first();
12101
- if (semanticIndicator.length) {
12102
- semanticIndicator.attr(indicatorMark, "1");
12486
+ const indicatorNode = findIndicatorDescendant(el[0]);
12487
+ if (indicatorNode !== void 0) {
12488
+ $(indicatorNode).attr(indicatorMark, "1");
12103
12489
  }
12104
12490
  });
12105
12491
  $(`[${clickableMark}]`).each(function removeEmptyClickable() {
@@ -12109,7 +12495,7 @@ function cleanForAction(html) {
12109
12495
  if (NATIVE_INTERACTIVE_TAGS.has(tag) || tag === "a") {
12110
12496
  return;
12111
12497
  }
12112
- if (el.children().length > 0 || hasDirectText($, el)) {
12498
+ if (hasElementChildren(node) || hasDirectText(node)) {
12113
12499
  return;
12114
12500
  }
12115
12501
  const wrapperAttrs = el.attr() || {};
@@ -12135,46 +12521,31 @@ function cleanForAction(html) {
12135
12521
  current = ancestor.parent();
12136
12522
  }
12137
12523
  });
12138
- let changed = true;
12139
- while (changed) {
12140
- changed = false;
12141
- const nodes = [];
12142
- $("*").each(function collectNodes() {
12143
- nodes.push($(this));
12144
- });
12145
- nodes.sort((left, right) => right.parents().length - left.parents().length);
12146
- for (const el of nodes) {
12147
- const node = el[0];
12148
- if (!node) {
12149
- continue;
12150
- }
12151
- const tag = (node.tagName || "").toLowerCase();
12152
- if (ROOT_TAGS.has(tag) || isBoundaryTag(tag)) {
12153
- continue;
12154
- }
12155
- if (el.attr(clickableMark) !== void 0 || el.attr(indicatorMark) !== void 0) {
12156
- continue;
12157
- }
12158
- const insideClickable = el.parents(`[${clickableMark}]`).length > 0;
12159
- const preserveBranch = el.attr(branchMark) !== void 0;
12160
- const hasContent = el.children().length > 0 || hasDirectText($, el);
12161
- if (insideClickable || preserveBranch) {
12162
- if (!hasContent) {
12163
- el.remove();
12164
- } else {
12165
- unwrapActionNode($, el);
12166
- }
12167
- changed = true;
12168
- continue;
12169
- }
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) {
12170
12537
  if (!hasContent) {
12171
12538
  el.remove();
12172
- changed = true;
12173
- continue;
12539
+ } else {
12540
+ unwrapActionNode($, el);
12174
12541
  }
12175
- unwrapActionNode($, el);
12176
- changed = true;
12542
+ continue;
12177
12543
  }
12544
+ if (!hasContent) {
12545
+ el.remove();
12546
+ continue;
12547
+ }
12548
+ unwrapActionNode($, el);
12178
12549
  }
12179
12550
  $.root().find("*").contents().each(function normalizeActionTextNodes() {
12180
12551
  if (this.type !== "text") {
@@ -12254,21 +12625,7 @@ function cleanForAction(html) {
12254
12625
  OPENSTEER_SPARSE_COUNTER_ATTR
12255
12626
  ]);
12256
12627
  if (clickable) {
12257
- for (const attr of [
12258
- "href",
12259
- "role",
12260
- "type",
12261
- "title",
12262
- "placeholder",
12263
- "value",
12264
- "aria-label",
12265
- "aria-labelledby",
12266
- "aria-describedby",
12267
- "aria-expanded",
12268
- "aria-pressed",
12269
- "aria-selected",
12270
- "aria-haspopup"
12271
- ]) {
12628
+ for (const attr of ["href", "role", "type", "title", "placeholder", "value", "aria-label"]) {
12272
12629
  keep.add(attr);
12273
12630
  }
12274
12631
  }
@@ -12888,9 +13245,9 @@ function renderNode(snapshot, node, nodesById, snapshotsByDocumentRef, snapshotI
12888
13245
  const snapshotAttributes = normalizeNodeAttributes(node.attributes);
12889
13246
  const authoredAttributes = stripInternalSnapshotAttributes(snapshotAttributes);
12890
13247
  const attributes = [...authoredAttributes];
12891
- const subtreeHidden = hasAttribute2(snapshotAttributes, OPENSTEER_HIDDEN_ATTR) || isLikelySubtreeHidden(node);
12892
- const selfHidden = !subtreeHidden && (hasAttribute2(snapshotAttributes, OPENSTEER_SELF_HIDDEN_ATTR) || isLikelySelfHidden(node, nodesById));
12893
- 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));
12894
13251
  if (interactive) {
12895
13252
  attributes.push({ name: OPENSTEER_INTERACTIVE_ATTR, value: "1" });
12896
13253
  }
@@ -13208,7 +13565,7 @@ function parseOpacity(value) {
13208
13565
  const parsed = Number.parseFloat(value);
13209
13566
  return Number.isFinite(parsed) ? parsed : Number.NaN;
13210
13567
  }
13211
- function hasAttribute2(attributes, name) {
13568
+ function hasAttribute3(attributes, name) {
13212
13569
  const normalizedName = name.toLowerCase();
13213
13570
  return attributes.some((attribute) => attribute.name.toLowerCase() === normalizedName);
13214
13571
  }
@@ -15169,12 +15526,12 @@ var OpensteerSessionRuntime = class {
15169
15526
  async (timeout) => {
15170
15527
  let descriptor2;
15171
15528
  let data;
15172
- if (input.schema !== void 0) {
15173
- assertValidOpensteerExtractionSchemaRoot(input.schema);
15529
+ if (input.template !== void 0) {
15530
+ assertValidOpensteerExtractionTemplateRoot(input.template);
15174
15531
  const fieldTargets = await timeout.runStep(
15175
15532
  () => compileOpensteerExtractionFieldTargets({
15176
15533
  pageRef,
15177
- schema: input.schema,
15534
+ template: input.template,
15178
15535
  dom: this.requireDom()
15179
15536
  })
15180
15537
  );
@@ -15206,7 +15563,7 @@ var OpensteerSessionRuntime = class {
15206
15563
  () => descriptors.write({
15207
15564
  persist,
15208
15565
  root: payload,
15209
- schemaHash: canonicalJsonString(input.schema),
15566
+ templateHash: canonicalJsonString(input.template),
15210
15567
  sourceUrl: pageInfo.url
15211
15568
  })
15212
15569
  );
@@ -15266,7 +15623,7 @@ var OpensteerSessionRuntime = class {
15266
15623
  artifacts,
15267
15624
  data: {
15268
15625
  ...input.persist === void 0 ? {} : { persist: input.persist },
15269
- ...descriptor?.payload.schemaHash === void 0 ? {} : { schemaHash: descriptor.payload.schemaHash },
15626
+ ...descriptor?.payload.templateHash === void 0 ? {} : { templateHash: descriptor.payload.templateHash },
15270
15627
  data: output.data
15271
15628
  },
15272
15629
  context: buildRuntimeTraceContext({
@@ -22343,39 +22700,6 @@ var OpensteerRuntime = class extends OpensteerSessionRuntime {
22343
22700
  );
22344
22701
  }
22345
22702
  };
22346
- var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
22347
- constructor(options) {
22348
- const rootPath = options.rootPath ?? path2.resolve(options.rootDir ?? process.cwd());
22349
- const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
22350
- const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
22351
- assertSupportedEngineOptions({
22352
- engineName,
22353
- ...options.browser === void 0 ? {} : { browser: options.browser },
22354
- ...options.context === void 0 ? {} : { context: options.context }
22355
- });
22356
- super(
22357
- buildSharedRuntimeOptions({
22358
- name: options.name,
22359
- ...options.rootDir === void 0 ? {} : { rootDir: options.rootDir },
22360
- rootPath,
22361
- ...options.environment === void 0 ? {} : { environment: options.environment },
22362
- ...options.browser === void 0 ? {} : { browser: options.browser },
22363
- ...options.launch === void 0 ? {} : { launch: options.launch },
22364
- ...options.context === void 0 ? {} : { context: options.context },
22365
- engineName,
22366
- ...options.engine === void 0 ? {} : { engine: options.engine },
22367
- ...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
22368
- ...options.policy === void 0 ? {} : { policy: options.policy },
22369
- ...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
22370
- ...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
22371
- cleanupRootOnClose,
22372
- ...options.observability === void 0 ? {} : { observability: options.observability },
22373
- ...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
22374
- ...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
22375
- })
22376
- );
22377
- }
22378
- };
22379
22703
  function buildSharedRuntimeOptions(input) {
22380
22704
  const ownership = resolveOwnership(input.browser);
22381
22705
  const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
@@ -22467,6 +22791,6 @@ function createOpensteerSemanticRuntime(input = {}) {
22467
22791
  });
22468
22792
  }
22469
22793
 
22470
- 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 };
22471
- //# sourceMappingURL=chunk-GEUHKPC2.js.map
22472
- //# sourceMappingURL=chunk-GEUHKPC2.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