n8n-nodes-alterlab 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"AlterLabApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAC;AAEtB,qBAAa,WAAY,YAAW,eAAe;IACjD,IAAI,SAAiB;IACrB,WAAW,SAAkB;IAC7B,gBAAgB,SACmF;IAEnG,UAAU,EAAE,eAAe,EAAE,CAmB3B;IAEF,YAAY,EAAE,oBAAoB,CAOhC;IAEF,IAAI,EAAE,sBAAsB,CAM1B;CACH"}
1
+ {"version":3,"file":"AlterLabApi.credentials.d.ts","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAChB,MAAM,cAAc,CAAC;AAEtB,qBAAa,WAAY,YAAW,eAAe;IACjD,IAAI,SAAiB;IACrB,WAAW,SAAkB;IAC7B,gBAAgB,SACmF;IAEnG,UAAU,EAAE,eAAe,EAAE,CAmB3B;IAEF,YAAY,EAAE,oBAAoB,CAOhC;IAEF,IAAI,EAAE,sBAAsB,CAU1B;CACH"}
@@ -34,8 +34,10 @@ class AlterLabApi {
34
34
  };
35
35
  this.test = {
36
36
  request: {
37
- baseURL: "={{$credentials.baseUrl}}",
38
- url: "/api/v1/usage",
37
+ // Keep exactly one trailing slash so n8n resolves the relative URL as a
38
+ // path reference instead of replacing a self-hosted path prefix.
39
+ baseURL: "={{($credentials.baseUrl || 'https://api.alterlab.io').replace(/\\/+$/, '') + '/'}}",
40
+ url: "={{($credentials.baseUrl || 'https://api.alterlab.io').replace(/\\/+$/, '').endsWith('/api/v1') ? 'usage' : 'api/v1/usage'}}",
39
41
  method: "GET",
40
42
  },
41
43
  };
@@ -1 +1 @@
1
- {"version":3,"file":"AlterLabApi.credentials.js","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,WAAW;IAAxB;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,cAAc,CAAC;QAC7B,qBAAgB,GACd,gGAAgG,CAAC;QAEnG,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACT,uYAAuY;aAC1Y;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,yBAAyB;gBAClC,WAAW,EACT,+DAA+D;aAClE;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,WAAW,EAAE,0BAA0B;iBACxC;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,eAAe;gBACpB,MAAM,EAAE,KAAK;aACd;SACF,CAAC;IACJ,CAAC;CAAA;AA3CD,kCA2CC"}
1
+ {"version":3,"file":"AlterLabApi.credentials.js","sourceRoot":"","sources":["../../credentials/AlterLabApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,WAAW;IAAxB;QACE,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,cAAc,CAAC;QAC7B,qBAAgB,GACd,gGAAgG,CAAC;QAEnG,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EACT,uYAAuY;aAC1Y;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,yBAAyB;gBAClC,WAAW,EACT,+DAA+D;aAClE;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,WAAW,EAAE,0BAA0B;iBACxC;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,wEAAwE;gBACxE,iEAAiE;gBACjE,OAAO,EACL,qFAAqF;gBACvF,GAAG,EACD,8HAA8H;gBAChI,MAAM,EAAE,KAAK;aACd;SACF,CAAC;IACJ,CAAC;CAAA;AA/CD,kCA+CC"}
@@ -1 +1 @@
1
- {"version":3,"file":"AlterLab.node.d.ts","sourceRoot":"","sources":["../../../nodes/AlterLab/AlterLab.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAKtB,qBAAa,QAAS,YAAW,SAAS;IACxC,WAAW,EAAE,oBAAoB,CAwlD/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CA47CxE"}
1
+ {"version":3,"file":"AlterLab.node.d.ts","sourceRoot":"","sources":["../../../nodes/AlterLab/AlterLab.node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAErB,MAAM,cAAc,CAAC;AAUtB,qBAAa,QAAS,YAAW,SAAS;IACxC,WAAW,EAAE,oBAAoB,CAksD/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAshDxE"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AlterLab = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ const url_1 = require("./url");
5
6
  const UTM = "utm_source=n8n&utm_medium=integration&utm_campaign=community_node";
6
7
  class AlterLab {
7
8
  constructor() {
@@ -471,6 +472,8 @@ class AlterLab {
471
472
  { name: "FAQ", value: "faq" },
472
473
  { name: "Recipe", value: "recipe" },
473
474
  { name: "Event", value: "event" },
475
+ { name: "Ecommerce Homepage", value: "ecommerce_homepage" },
476
+ { name: "Directory Listing", value: "directory_listing" },
474
477
  ],
475
478
  description: "Pre-defined extraction profile for structured data",
476
479
  },
@@ -499,6 +502,20 @@ class AlterLab {
499
502
  placeholder: "gpt-4o",
500
503
  description: "LLM model override for this request in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key for this request only.",
501
504
  },
505
+ {
506
+ displayName: "Extraction Provider",
507
+ name: "extractionProvider",
508
+ type: "options",
509
+ default: "",
510
+ options: [
511
+ { name: "Auto (use most recent BYOK key)", value: "" },
512
+ { name: "OpenAI", value: "openai" },
513
+ { name: "Anthropic", value: "anthropic" },
514
+ { name: "OpenRouter", value: "openrouter" },
515
+ { name: "Groq", value: "groq" },
516
+ ],
517
+ description: "LLM provider to use for extraction. Selects the matching BYOK key registered at your AlterLab dashboard. When omitted, the most recently used registered key is used automatically.",
518
+ },
502
519
  {
503
520
  displayName: "Promote Schema.org",
504
521
  name: "promoteSchemaOrg",
@@ -813,6 +830,20 @@ class AlterLab {
813
830
  placeholder: "gpt-4o",
814
831
  description: "LLM model override for each crawled page in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key.",
815
832
  },
833
+ {
834
+ displayName: "Extraction Provider",
835
+ name: "extractionProvider",
836
+ type: "options",
837
+ default: "",
838
+ options: [
839
+ { name: "Auto (use most recent BYOK key)", value: "" },
840
+ { name: "OpenAI", value: "openai" },
841
+ { name: "Anthropic", value: "anthropic" },
842
+ { name: "OpenRouter", value: "openrouter" },
843
+ { name: "Groq", value: "groq" },
844
+ ],
845
+ description: "LLM provider to use for extraction on each crawled page. Selects the matching BYOK key registered at your AlterLab dashboard.",
846
+ },
816
847
  ],
817
848
  },
818
849
  // ══════════════════════════════════════════════════════
@@ -1074,7 +1105,7 @@ class AlterLab {
1074
1105
  default: "",
1075
1106
  required: true,
1076
1107
  placeholder: "<html>...</html> or raw text / markdown",
1077
- description: "Raw content to extract from (HTML, text, or markdown — max 5 MB)",
1108
+ description: "Raw content to extract from (HTML, text, or markdown — max 500,000 characters)",
1078
1109
  displayOptions: {
1079
1110
  show: {
1080
1111
  resource: ["extract"],
@@ -1142,6 +1173,8 @@ class AlterLab {
1142
1173
  { name: "None", value: "" },
1143
1174
  { name: "Auto", value: "auto" },
1144
1175
  { name: "Article", value: "article" },
1176
+ { name: "Directory Listing", value: "directory_listing" },
1177
+ { name: "Ecommerce Homepage", value: "ecommerce_homepage" },
1145
1178
  { name: "Event", value: "event" },
1146
1179
  { name: "FAQ", value: "faq" },
1147
1180
  { name: "Job Posting", value: "job_posting" },
@@ -1150,6 +1183,25 @@ class AlterLab {
1150
1183
  ],
1151
1184
  description: "Pre-defined extraction profile (schema template)",
1152
1185
  },
1186
+ {
1187
+ displayName: "Extraction Template",
1188
+ name: "extractionTemplate",
1189
+ type: "options",
1190
+ default: "",
1191
+ options: [
1192
+ { name: "None", value: "" },
1193
+ { name: "Auto", value: "auto" },
1194
+ { name: "Article", value: "article" },
1195
+ { name: "Directory Listing", value: "directory_listing" },
1196
+ { name: "Ecommerce Homepage", value: "ecommerce_homepage" },
1197
+ { name: "Event", value: "event" },
1198
+ { name: "FAQ", value: "faq" },
1199
+ { name: "Job Posting", value: "job_posting" },
1200
+ { name: "Product", value: "product" },
1201
+ { name: "Recipe", value: "recipe" },
1202
+ ],
1203
+ description: "Shorthand alias for Extraction Profile. Mutually exclusive with Extraction Profile — use one or the other.",
1204
+ },
1153
1205
  {
1154
1206
  displayName: "Extraction Prompt",
1155
1207
  name: "extractionPrompt",
@@ -1167,6 +1219,20 @@ class AlterLab {
1167
1219
  placeholder: "gpt-4o",
1168
1220
  description: "LLM model override for this request in provider-specific format (e.g. gpt-4o, claude-opus-4-5-20251101, llama3-70b-8192). Overrides the model configured on your BYOK key for this request only.",
1169
1221
  },
1222
+ {
1223
+ displayName: "Extraction Provider",
1224
+ name: "extractionProvider",
1225
+ type: "options",
1226
+ default: "",
1227
+ options: [
1228
+ { name: "Auto (use most recent BYOK key)", value: "" },
1229
+ { name: "OpenAI", value: "openai" },
1230
+ { name: "Anthropic", value: "anthropic" },
1231
+ { name: "OpenRouter", value: "openrouter" },
1232
+ { name: "Groq", value: "groq" },
1233
+ ],
1234
+ description: "LLM provider to use for extraction. Selects the matching BYOK key registered at your AlterLab dashboard.",
1235
+ },
1170
1236
  {
1171
1237
  displayName: "Source URL",
1172
1238
  name: "sourceUrl",
@@ -1182,6 +1248,38 @@ class AlterLab {
1182
1248
  default: false,
1183
1249
  description: "Whether to include provenance/evidence for extracted fields",
1184
1250
  },
1251
+ {
1252
+ displayName: "Cache Control",
1253
+ name: "cache",
1254
+ type: "options",
1255
+ default: "auto",
1256
+ options: [
1257
+ {
1258
+ name: "Auto (return cached if available)",
1259
+ value: "auto",
1260
+ description: "Return cached LLM extraction result if available, otherwise call the LLM and cache the result",
1261
+ },
1262
+ {
1263
+ name: "Skip (bypass cache, always call LLM)",
1264
+ value: "skip",
1265
+ description: "Bypass cache lookup and always invoke the LLM. Result is still stored for future callers.",
1266
+ },
1267
+ {
1268
+ name: "Only (cached result or 404)",
1269
+ value: "only",
1270
+ description: "Return cached result only. Returns HTTP 404 if the result is not cached — never calls the LLM.",
1271
+ },
1272
+ ],
1273
+ description: "Cache control for LLM extraction results. auto: use cache if available (default). skip: always call LLM. only: return cached or 404.",
1274
+ },
1275
+ {
1276
+ displayName: "Cache TTL (Seconds)",
1277
+ name: "cacheTtl",
1278
+ type: "number",
1279
+ default: 3600,
1280
+ typeOptions: { minValue: 1, maxValue: 86400 },
1281
+ description: "How long to cache this extraction result in seconds (1–86400). Defaults to 3600s (1 hour). Max 86400s (24 hours).",
1282
+ },
1185
1283
  ],
1186
1284
  },
1187
1285
  // ══════════════════════════════════════════════════════
@@ -1535,7 +1633,7 @@ class AlterLab {
1535
1633
  };
1536
1634
  }
1537
1635
  async execute() {
1538
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27;
1636
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29;
1539
1637
  const items = this.getInputData();
1540
1638
  const results = [];
1541
1639
  for (let i = 0; i < items.length; i++) {
@@ -1543,13 +1641,17 @@ class AlterLab {
1543
1641
  const resource = this.getNodeParameter("resource", i);
1544
1642
  // ── Detect credential type ────────────────────────
1545
1643
  let authName = "alterLabApi";
1644
+ let credentials;
1546
1645
  try {
1547
- await this.getCredentials("alterLabOAuth2Api");
1646
+ credentials = await this.getCredentials("alterLabOAuth2Api");
1548
1647
  authName = "alterLabOAuth2Api";
1549
1648
  }
1550
1649
  catch {
1551
1650
  // OAuth2 not configured, fall back to API key
1651
+ credentials = await this.getCredentials("alterLabApi");
1552
1652
  }
1653
+ const apiBaseUrl = (0, url_1.normalizeBaseUrl)(credentials.baseUrl);
1654
+ const apiUrl = (endpoint) => (0, url_1.resolveApiUrl)(apiBaseUrl, endpoint);
1553
1655
  // ══════════════════════════════════════════════════
1554
1656
  // SESSION RESOURCE
1555
1657
  // ══════════════════════════════════════════════════
@@ -1593,7 +1695,7 @@ class AlterLab {
1593
1695
  body.expires_at = extra.expiresAt;
1594
1696
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1595
1697
  method: "POST",
1596
- url: "/api/v1/sessions",
1698
+ url: apiUrl("/api/v1/sessions"),
1597
1699
  body,
1598
1700
  json: true,
1599
1701
  returnFullResponse: true,
@@ -1617,7 +1719,7 @@ class AlterLab {
1617
1719
  qs.status = filters.status;
1618
1720
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1619
1721
  method: "GET",
1620
- url: "/api/v1/sessions",
1722
+ url: apiUrl("/api/v1/sessions"),
1621
1723
  qs,
1622
1724
  json: true,
1623
1725
  returnFullResponse: true,
@@ -1646,7 +1748,7 @@ class AlterLab {
1646
1748
  const sessionId = this.getNodeParameter("sessionId", i);
1647
1749
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1648
1750
  method: "GET",
1649
- url: `/api/v1/sessions/${sessionId}`,
1751
+ url: apiUrl(`/api/v1/sessions/${sessionId}`),
1650
1752
  json: true,
1651
1753
  returnFullResponse: true,
1652
1754
  ignoreHttpStatusErrors: true,
@@ -1694,7 +1796,7 @@ class AlterLab {
1694
1796
  }
1695
1797
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1696
1798
  method: "PATCH",
1697
- url: `/api/v1/sessions/${sessionId}`,
1799
+ url: apiUrl(`/api/v1/sessions/${sessionId}`),
1698
1800
  body,
1699
1801
  json: true,
1700
1802
  returnFullResponse: true,
@@ -1713,7 +1815,7 @@ class AlterLab {
1713
1815
  const sessionId = this.getNodeParameter("sessionId", i);
1714
1816
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1715
1817
  method: "DELETE",
1716
- url: `/api/v1/sessions/${sessionId}`,
1818
+ url: apiUrl(`/api/v1/sessions/${sessionId}`),
1717
1819
  json: true,
1718
1820
  returnFullResponse: true,
1719
1821
  ignoreHttpStatusErrors: true,
@@ -1732,7 +1834,7 @@ class AlterLab {
1732
1834
  const sessionId = this.getNodeParameter("sessionId", i);
1733
1835
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1734
1836
  method: "POST",
1735
- url: `/api/v1/sessions/${sessionId}/validate`,
1837
+ url: apiUrl(`/api/v1/sessions/${sessionId}/validate`),
1736
1838
  json: true,
1737
1839
  returnFullResponse: true,
1738
1840
  ignoreHttpStatusErrors: true,
@@ -1787,7 +1889,7 @@ class AlterLab {
1787
1889
  }
1788
1890
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1789
1891
  method: "POST",
1790
- url: `/api/v1/sessions/${sessionId}/refresh`,
1892
+ url: apiUrl(`/api/v1/sessions/${sessionId}/refresh`),
1791
1893
  body,
1792
1894
  json: true,
1793
1895
  returnFullResponse: true,
@@ -1813,7 +1915,7 @@ class AlterLab {
1813
1915
  const crawlId = this.getNodeParameter("crawlId", i);
1814
1916
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1815
1917
  method: "GET",
1816
- url: `/api/v1/crawl/${crawlId}`,
1918
+ url: apiUrl(`/api/v1/crawl/${crawlId}`),
1817
1919
  json: true,
1818
1920
  returnFullResponse: true,
1819
1921
  ignoreHttpStatusErrors: true,
@@ -1831,7 +1933,7 @@ class AlterLab {
1831
1933
  const crawlId = this.getNodeParameter("crawlId", i);
1832
1934
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1833
1935
  method: "DELETE",
1834
- url: `/api/v1/crawl/${crawlId}`,
1936
+ url: apiUrl(`/api/v1/crawl/${crawlId}`),
1835
1937
  json: true,
1836
1938
  returnFullResponse: true,
1837
1939
  ignoreHttpStatusErrors: true,
@@ -1873,6 +1975,9 @@ class AlterLab {
1873
1975
  if (settings.extractionModel) {
1874
1976
  body.extraction_model = settings.extractionModel;
1875
1977
  }
1978
+ if (settings.extractionProvider) {
1979
+ body.extraction_provider = settings.extractionProvider;
1980
+ }
1876
1981
  const advanced = {};
1877
1982
  if (settings.renderJs && settings.renderJs !== "false") {
1878
1983
  advanced.render_js = settings.renderJs === "auto" ? "auto" : true;
@@ -1891,9 +1996,10 @@ class AlterLab {
1891
1996
  if (Object.keys(advanced).length > 0)
1892
1997
  body.advanced = advanced;
1893
1998
  // Submit the crawl
1999
+ const crawlRequestUrl = apiUrl("/api/v1/crawl");
1894
2000
  const startResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1895
2001
  method: "POST",
1896
- url: "/api/v1/crawl",
2002
+ url: crawlRequestUrl,
1897
2003
  body,
1898
2004
  json: true,
1899
2005
  returnFullResponse: true,
@@ -1916,6 +2022,7 @@ class AlterLab {
1916
2022
  let pollDelay = 2000;
1917
2023
  const maxPollDelay = 10000;
1918
2024
  let finalBody = startBody;
2025
+ let crawlPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, startResponse, `/api/v1/crawl/${crawlId}`, crawlRequestUrl);
1919
2026
  while (Date.now() - pollStart < pollTimeoutMs) {
1920
2027
  await new Promise((resolve) => {
1921
2028
  setTimeout(resolve, pollDelay);
@@ -1923,11 +2030,12 @@ class AlterLab {
1923
2030
  pollDelay = Math.min(pollDelay * 1.5, maxPollDelay);
1924
2031
  const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1925
2032
  method: "GET",
1926
- url: `/api/v1/crawl/${crawlId}`,
2033
+ url: crawlPollUrl,
1927
2034
  json: true,
1928
2035
  returnFullResponse: true,
1929
2036
  ignoreHttpStatusErrors: true,
1930
2037
  });
2038
+ crawlPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, crawlPollUrl, crawlPollUrl);
1931
2039
  const pollStatus = pollResponse
1932
2040
  .statusCode;
1933
2041
  const pollBody = pollResponse.body;
@@ -1991,9 +2099,10 @@ class AlterLab {
1991
2099
  }
1992
2100
  }
1993
2101
  }
2102
+ const searchRequestUrl = apiUrl("/api/v1/search");
1994
2103
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
1995
2104
  method: "POST",
1996
- url: "/api/v1/search",
2105
+ url: searchRequestUrl,
1997
2106
  body,
1998
2107
  json: true,
1999
2108
  returnFullResponse: true,
@@ -2015,6 +2124,7 @@ class AlterLab {
2015
2124
  const searchPollStart = Date.now();
2016
2125
  const maxSearchPollMs = 120000; // 2 min max
2017
2126
  let finalData = data;
2127
+ let searchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, response, `/api/v1/search/${searchId}`, searchRequestUrl);
2018
2128
  while (Date.now() - searchPollStart < maxSearchPollMs) {
2019
2129
  await new Promise((resolve) => {
2020
2130
  setTimeout(resolve, searchPollDelay);
@@ -2022,11 +2132,12 @@ class AlterLab {
2022
2132
  searchPollDelay = Math.min(searchPollDelay * 1.5, maxSearchPollDelay);
2023
2133
  const pollResp = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2024
2134
  method: "GET",
2025
- url: `/api/v1/search/${searchId}`,
2135
+ url: searchPollUrl,
2026
2136
  json: true,
2027
2137
  returnFullResponse: true,
2028
2138
  ignoreHttpStatusErrors: true,
2029
2139
  });
2140
+ searchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResp, searchPollUrl, searchPollUrl);
2030
2141
  const pollStatus = pollResp
2031
2142
  .statusCode;
2032
2143
  const pollBody = pollResp
@@ -2082,7 +2193,7 @@ class AlterLab {
2082
2193
  body.sitemap_path = opts.sitemapPath;
2083
2194
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2084
2195
  method: "POST",
2085
- url: "/api/v1/map",
2196
+ url: apiUrl("/api/v1/map"),
2086
2197
  body,
2087
2198
  json: true,
2088
2199
  returnFullResponse: true,
@@ -2123,17 +2234,25 @@ class AlterLab {
2123
2234
  }
2124
2235
  if (opts.extractionProfile)
2125
2236
  body.extraction_profile = opts.extractionProfile;
2237
+ if (opts.extractionTemplate)
2238
+ body.extraction_template = opts.extractionTemplate;
2126
2239
  if (opts.extractionPrompt)
2127
2240
  body.extraction_prompt = opts.extractionPrompt;
2128
2241
  if (opts.extractionModel)
2129
2242
  body.extraction_model = opts.extractionModel;
2243
+ if (opts.extractionProvider)
2244
+ body.extraction_provider = opts.extractionProvider;
2130
2245
  if (opts.sourceUrl)
2131
2246
  body.source_url = opts.sourceUrl;
2132
2247
  if (opts.evidence)
2133
2248
  body.evidence = true;
2249
+ if (opts.cache && opts.cache !== "auto")
2250
+ body.cache = opts.cache;
2251
+ if (opts.cacheTtl && opts.cacheTtl !== 3600)
2252
+ body.cache_ttl = opts.cacheTtl;
2134
2253
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2135
2254
  method: "POST",
2136
- url: "/api/v1/extract",
2255
+ url: apiUrl("/api/v1/extract"),
2137
2256
  body,
2138
2257
  json: true,
2139
2258
  returnFullResponse: true,
@@ -2152,8 +2271,14 @@ class AlterLab {
2152
2271
  creditsUsed: (_p = data.credits_used) !== null && _p !== void 0 ? _p : 0,
2153
2272
  modelUsed: (_q = data.model_used) !== null && _q !== void 0 ? _q : null,
2154
2273
  extractionMethod: (_r = data.extraction_method) !== null && _r !== void 0 ? _r : "algorithmic",
2155
- contentSizeChars: (_s = data.content_size_chars) !== null && _s !== void 0 ? _s : 0,
2274
+ extractionProfile: (_s = data.extraction_profile) !== null && _s !== void 0 ? _s : null,
2275
+ contentSizeChars: (_t = data.content_size_chars) !== null && _t !== void 0 ? _t : 0,
2276
+ cacheHit: (_u = data.cache_hit) !== null && _u !== void 0 ? _u : false,
2156
2277
  };
2278
+ if (data.extraction_metadata !== undefined &&
2279
+ data.extraction_metadata !== null) {
2280
+ output.extractionMetadata = data.extraction_metadata;
2281
+ }
2157
2282
  // Flatten format results into top-level keys
2158
2283
  if (formats && typeof formats === "object") {
2159
2284
  if (formats.text !== undefined)
@@ -2191,9 +2316,10 @@ class AlterLab {
2191
2316
  const body = { urls };
2192
2317
  if (opts.webhookUrl)
2193
2318
  body.webhook_url = opts.webhookUrl;
2319
+ const batchRequestUrl = apiUrl("/api/v1/batch");
2194
2320
  const submitResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2195
2321
  method: "POST",
2196
- url: "/api/v1/batch",
2322
+ url: batchRequestUrl,
2197
2323
  body,
2198
2324
  json: true,
2199
2325
  returnFullResponse: true,
@@ -2212,11 +2338,12 @@ class AlterLab {
2212
2338
  continue;
2213
2339
  }
2214
2340
  // Poll until complete or timeout
2215
- const batchPollTimeoutMs = ((_t = opts.pollTimeout) !== null && _t !== void 0 ? _t : 300) * 1000;
2341
+ const batchPollTimeoutMs = ((_v = opts.pollTimeout) !== null && _v !== void 0 ? _v : 300) * 1000;
2216
2342
  const batchPollStart = Date.now();
2217
2343
  let batchDelay = 1000;
2218
2344
  const maxBatchDelay = 8000;
2219
2345
  let finalBatchBody = submitBody;
2346
+ let batchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, submitResponse, `/api/v1/batch/${batchId}`, batchRequestUrl);
2220
2347
  while (Date.now() - batchPollStart < batchPollTimeoutMs) {
2221
2348
  await new Promise((resolve) => {
2222
2349
  setTimeout(resolve, batchDelay);
@@ -2224,11 +2351,12 @@ class AlterLab {
2224
2351
  batchDelay = Math.min(batchDelay * 1.5, maxBatchDelay);
2225
2352
  const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2226
2353
  method: "GET",
2227
- url: `/api/v1/batch/${batchId}`,
2354
+ url: batchPollUrl,
2228
2355
  json: true,
2229
2356
  returnFullResponse: true,
2230
2357
  ignoreHttpStatusErrors: true,
2231
2358
  });
2359
+ batchPollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, batchPollUrl, batchPollUrl);
2232
2360
  const pollStatus = pollResponse
2233
2361
  .statusCode;
2234
2362
  const pollBody = pollResponse
@@ -2289,7 +2417,7 @@ class AlterLab {
2289
2417
  }
2290
2418
  const response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2291
2419
  method: "POST",
2292
- url: "/api/v1/scrape/estimate",
2420
+ url: apiUrl("/api/v1/scrape/estimate"),
2293
2421
  body,
2294
2422
  json: true,
2295
2423
  returnFullResponse: true,
@@ -2304,12 +2432,12 @@ class AlterLab {
2304
2432
  const data = responseBody;
2305
2433
  results.push({
2306
2434
  json: {
2307
- url: (_u = data.url) !== null && _u !== void 0 ? _u : "",
2308
- estimatedTier: (_v = data.estimated_tier) !== null && _v !== void 0 ? _v : "unknown",
2309
- estimatedCredits: (_w = data.estimated_credits) !== null && _w !== void 0 ? _w : 0,
2310
- confidence: (_x = data.confidence) !== null && _x !== void 0 ? _x : "low",
2311
- maxPossibleCredits: (_y = data.max_possible_credits) !== null && _y !== void 0 ? _y : 0,
2312
- reasoning: (_z = data.reasoning) !== null && _z !== void 0 ? _z : "",
2435
+ url: (_w = data.url) !== null && _w !== void 0 ? _w : "",
2436
+ estimatedTier: (_x = data.estimated_tier) !== null && _x !== void 0 ? _x : "unknown",
2437
+ estimatedCredits: (_y = data.estimated_credits) !== null && _y !== void 0 ? _y : 0,
2438
+ confidence: (_z = data.confidence) !== null && _z !== void 0 ? _z : "low",
2439
+ maxPossibleCredits: (_0 = data.max_possible_credits) !== null && _0 !== void 0 ? _0 : 0,
2440
+ reasoning: (_1 = data.reasoning) !== null && _1 !== void 0 ? _1 : "",
2313
2441
  },
2314
2442
  });
2315
2443
  continue;
@@ -2336,7 +2464,7 @@ class AlterLab {
2336
2464
  body.body = requestBody;
2337
2465
  }
2338
2466
  // Output options
2339
- if ((_0 = outputOptions.formats) === null || _0 === void 0 ? void 0 : _0.length) {
2467
+ if ((_2 = outputOptions.formats) === null || _2 === void 0 ? void 0 : _2.length) {
2340
2468
  body.formats = outputOptions.formats;
2341
2469
  }
2342
2470
  if (outputOptions.includeRawHtml) {
@@ -2438,6 +2566,9 @@ class AlterLab {
2438
2566
  if (extraction.extractionModel) {
2439
2567
  body.extraction_model = extraction.extractionModel;
2440
2568
  }
2569
+ if (extraction.extractionProvider) {
2570
+ body.extraction_provider = extraction.extractionProvider;
2571
+ }
2441
2572
  // Cost controls → nested "cost_controls" object
2442
2573
  const costCtrl = {};
2443
2574
  if (costControls.maxCredits && costControls.maxCredits > 0) {
@@ -2459,9 +2590,10 @@ class AlterLab {
2459
2590
  body.cost_controls = costCtrl;
2460
2591
  }
2461
2592
  // ── Make the API call ─────────────────────────────
2593
+ const scrapeRequestUrl = apiUrl("/api/v1/scrape");
2462
2594
  let response = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2463
2595
  method: "POST",
2464
- url: "/api/v1/scrape",
2596
+ url: scrapeRequestUrl,
2465
2597
  body,
2466
2598
  json: true,
2467
2599
  returnFullResponse: true,
@@ -2474,18 +2606,20 @@ class AlterLab {
2474
2606
  const jobId = responseBody.job_id;
2475
2607
  let delay = 500;
2476
2608
  const maxDelay = 5000;
2477
- const maxPollTime = (((_1 = outputOptions.timeout) !== null && _1 !== void 0 ? _1 : 90) + 30) * 1000; // timeout + 30s buffer
2609
+ const maxPollTime = (((_3 = outputOptions.timeout) !== null && _3 !== void 0 ? _3 : 90) + 30) * 1000; // timeout + 30s buffer
2478
2610
  const pollStart = Date.now();
2611
+ let scrapePollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, response, `/api/v1/jobs/${jobId}`, scrapeRequestUrl);
2479
2612
  while (Date.now() - pollStart < maxPollTime) {
2480
2613
  await new Promise((resolve) => setTimeout(resolve, delay));
2481
2614
  delay = Math.min(delay * 2, maxDelay);
2482
2615
  const pollResponse = await this.helpers.httpRequestWithAuthentication.call(this, authName, {
2483
2616
  method: "GET",
2484
- url: `/api/v1/jobs/${jobId}`,
2617
+ url: scrapePollUrl,
2485
2618
  json: true,
2486
2619
  returnFullResponse: true,
2487
2620
  ignoreHttpStatusErrors: true,
2488
2621
  });
2622
+ scrapePollUrl = (0, url_1.resolvePollUrl)(apiBaseUrl, pollResponse, scrapePollUrl, scrapePollUrl);
2489
2623
  const pollStatus = pollResponse
2490
2624
  .statusCode;
2491
2625
  const pollBody = pollResponse
@@ -2514,14 +2648,14 @@ class AlterLab {
2514
2648
  const data = responseBody;
2515
2649
  const content = data.content;
2516
2650
  const output = {
2517
- url: (_2 = data.url) !== null && _2 !== void 0 ? _2 : "",
2518
- statusCode: (_3 = data.status_code) !== null && _3 !== void 0 ? _3 : 0,
2519
- title: (_4 = data.title) !== null && _4 !== void 0 ? _4 : null,
2520
- author: (_5 = data.author) !== null && _5 !== void 0 ? _5 : null,
2521
- publishedAt: (_6 = data.published_at) !== null && _6 !== void 0 ? _6 : null,
2522
- cached: (_7 = data.cached) !== null && _7 !== void 0 ? _7 : false,
2523
- responseTimeMs: (_8 = data.response_time_ms) !== null && _8 !== void 0 ? _8 : 0,
2524
- sizeBytes: (_9 = data.size_bytes) !== null && _9 !== void 0 ? _9 : 0,
2651
+ url: (_4 = data.url) !== null && _4 !== void 0 ? _4 : "",
2652
+ statusCode: (_5 = data.status_code) !== null && _5 !== void 0 ? _5 : 0,
2653
+ title: (_6 = data.title) !== null && _6 !== void 0 ? _6 : null,
2654
+ author: (_7 = data.author) !== null && _7 !== void 0 ? _7 : null,
2655
+ publishedAt: (_8 = data.published_at) !== null && _8 !== void 0 ? _8 : null,
2656
+ cached: (_9 = data.cached) !== null && _9 !== void 0 ? _9 : false,
2657
+ responseTimeMs: (_10 = data.response_time_ms) !== null && _10 !== void 0 ? _10 : 0,
2658
+ sizeBytes: (_11 = data.size_bytes) !== null && _11 !== void 0 ? _11 : 0,
2525
2659
  };
2526
2660
  // Flatten multi-format content.
2527
2661
  // When extraction_schema is provided, the API overrides content.json
@@ -2529,32 +2663,32 @@ class AlterLab {
2529
2663
  // therefore carries the extraction result in that case.
2530
2664
  if (content && typeof content === "object") {
2531
2665
  output.markdown =
2532
- (_10 = content.markdown) !== null && _10 !== void 0 ? _10 : null;
2533
- output.text = (_11 = content.text) !== null && _11 !== void 0 ? _11 : null;
2534
- output.json = (_12 = content.json) !== null && _12 !== void 0 ? _12 : null;
2535
- output.jsonV2 = (_13 = content.json_v2) !== null && _13 !== void 0 ? _13 : null;
2536
- output.rag = (_14 = content.rag) !== null && _14 !== void 0 ? _14 : null;
2537
- output.html = (_15 = content.html) !== null && _15 !== void 0 ? _15 : null;
2666
+ (_12 = content.markdown) !== null && _12 !== void 0 ? _12 : null;
2667
+ output.text = (_13 = content.text) !== null && _13 !== void 0 ? _13 : null;
2668
+ output.json = (_14 = content.json) !== null && _14 !== void 0 ? _14 : null;
2669
+ output.jsonV2 = (_15 = content.json_v2) !== null && _15 !== void 0 ? _15 : null;
2670
+ output.rag = (_16 = content.rag) !== null && _16 !== void 0 ? _16 : null;
2671
+ output.html = (_17 = content.html) !== null && _17 !== void 0 ? _17 : null;
2538
2672
  }
2539
2673
  else {
2540
2674
  output.markdown = content !== null && content !== void 0 ? content : null;
2541
2675
  }
2542
2676
  // Extraction results — top-level filtered_content is still returned
2543
2677
  // by the API alongside the content.json override above.
2544
- output.filteredContent = (_16 = data.filtered_content) !== null && _16 !== void 0 ? _16 : null;
2545
- output.extractionMethod = (_17 = data.extraction_method) !== null && _17 !== void 0 ? _17 : null;
2678
+ output.filteredContent = (_18 = data.filtered_content) !== null && _18 !== void 0 ? _18 : null;
2679
+ output.extractionMethod = (_19 = data.extraction_method) !== null && _19 !== void 0 ? _19 : null;
2546
2680
  // Advanced outputs
2547
- output.screenshotUrl = (_18 = data.screenshot_url) !== null && _18 !== void 0 ? _18 : null;
2548
- output.pdfUrl = (_19 = data.pdf_url) !== null && _19 !== void 0 ? _19 : null;
2549
- output.ocrResults = (_20 = data.ocr_results) !== null && _20 !== void 0 ? _20 : null;
2550
- output.rawHtml = (_21 = data.raw_html) !== null && _21 !== void 0 ? _21 : null;
2681
+ output.screenshotUrl = (_20 = data.screenshot_url) !== null && _20 !== void 0 ? _20 : null;
2682
+ output.pdfUrl = (_21 = data.pdf_url) !== null && _21 !== void 0 ? _21 : null;
2683
+ output.ocrResults = (_22 = data.ocr_results) !== null && _22 !== void 0 ? _22 : null;
2684
+ output.rawHtml = (_23 = data.raw_html) !== null && _23 !== void 0 ? _23 : null;
2551
2685
  // Billing breakdown (flattened)
2552
2686
  const billing = data.billing;
2553
2687
  output.billing = {
2554
- cost: (_23 = (_22 = billing === null || billing === void 0 ? void 0 : billing.total_credits) !== null && _22 !== void 0 ? _22 : data.credits_used) !== null && _23 !== void 0 ? _23 : 0,
2555
- tier: (_25 = (_24 = billing === null || billing === void 0 ? void 0 : billing.tier_used) !== null && _24 !== void 0 ? _24 : data.tier_used) !== null && _25 !== void 0 ? _25 : "unknown",
2556
- savings: (_26 = billing === null || billing === void 0 ? void 0 : billing.savings) !== null && _26 !== void 0 ? _26 : 0,
2557
- suggestion: (_27 = billing === null || billing === void 0 ? void 0 : billing.optimization_suggestion) !== null && _27 !== void 0 ? _27 : null,
2688
+ cost: (_25 = (_24 = billing === null || billing === void 0 ? void 0 : billing.total_credits) !== null && _24 !== void 0 ? _24 : data.credits_used) !== null && _25 !== void 0 ? _25 : 0,
2689
+ tier: (_27 = (_26 = billing === null || billing === void 0 ? void 0 : billing.tier_used) !== null && _26 !== void 0 ? _26 : data.tier_used) !== null && _27 !== void 0 ? _27 : "unknown",
2690
+ savings: (_28 = billing === null || billing === void 0 ? void 0 : billing.savings) !== null && _28 !== void 0 ? _28 : 0,
2691
+ suggestion: (_29 = billing === null || billing === void 0 ? void 0 : billing.optimization_suggestion) !== null && _29 !== void 0 ? _29 : null,
2558
2692
  };
2559
2693
  results.push({ json: output });
2560
2694
  }
@@ -2611,7 +2745,7 @@ function handleApiError(ctx, statusCode, body, itemIndex) {
2611
2745
  case 402:
2612
2746
  throw new n8n_workflow_1.NodeApiError(ctx.getNode(), body, {
2613
2747
  message: "Insufficient balance",
2614
- description: `${detail}. Top up your balance at https://app.alterlab.io/dashboard/billing?${UTM}`,
2748
+ description: `Top up your balance at https://app.alterlab.io/dashboard/billing?${UTM}`,
2615
2749
  httpCode: "402",
2616
2750
  itemIndex,
2617
2751
  });