From a5e2fc4abce526567e3c7f679e505bf613157280 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Mon, 1 Jun 2026 21:02:39 +0300 Subject: [PATCH 01/17] add recall error for restarting run with another options --- lib/core.js | 33 ++++++++++++++++++++++++++++++++- lib/utils.js | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/core.js b/lib/core.js index 4dd99bccb..fc675a9b8 100644 --- a/lib/core.js +++ b/lib/core.js @@ -1437,7 +1437,8 @@ responseStatusCode: 'responseStatusCode', responseError: 'responseError', timeout: 'timeout', - fallback: 'fallback' + fallback: 'fallback', + recall: 'recall' // Make same iframely call with different options. }; function findRedirectError(result) { @@ -1458,6 +1459,21 @@ } } + function findRecallError(result) { + if (result) { + for(var i = 0; i < result.length; i++) { + var r = result[i]; + var recall = r.error && r.error[SYS_ERRORS.recall]; + if (typeof recall === "object") { + log(' -- plugin recall (by "' + r.method.pluginId + '")'); + return recall; + } else if (recall && typeof recall !== "object") { + log(' -- skip plugin recall, not object (by "' + r.method.pluginId + '")', recall); + } + } + } + } + function findResponseError(result, uri) { if (result) { for(var i = 0; i < result.length; i++) { @@ -1739,6 +1755,9 @@ options = {}; } + // Store original to run on recall. + var originalOptions = {...options}; + var initialCallback = cb; var fallbackInfo; @@ -1952,6 +1971,18 @@ return; } + // Find recall command. + var recall_data = findRecallError(result); + if (recall_data && options.isRecall) { + log(' -- ignore recursive recall'); + } else if (recall_data) { + abortCurrentRequest(); + var recallOptions = Object.assign({}, originalOptions, recall_data, {isRecall: true}); + run(uri, recallOptions, cb); + aborted = true; + return; + } + // Gather results. // Run before `responseError` to collect data and send in error. var hasNewData = useResult(usedMethods, context, pluginsContexts, allResults, result, options, asyncMethodCb); diff --git a/lib/utils.js b/lib/utils.js index dd3d58741..a940c04d3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -106,7 +106,7 @@ export function prepareRequestOptions(request_options, options) { } // Some calls (like oembed) use basic options without `getDomainOptions`. - var enable_domain_prerender = options?.getDomainOptions && options.getDomainOptions('meta.prerender'); + var enable_domain_prerender = options?.getDomainOptions && options.getDomainOptions('meta.prerender') || options.prerender; if (enable_domain_prerender) { setPrerender(enable_domain_prerender); } From 22f120ae93becef59435847f5e9d2a39e4e15adb Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Mon, 1 Jun 2026 22:23:06 +0300 Subject: [PATCH 02/17] collect text response --- .../CollectingHandlerForMutliTarget.js | 32 +++++++++++++-- .../system/htmlparser/TextResponseHandler.js | 39 +++++++++++++++++++ lib/plugins/system/htmlparser/htmlparser.js | 10 ++++- lib/utils.js | 6 ++- 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 lib/plugins/system/htmlparser/TextResponseHandler.js diff --git a/lib/plugins/system/htmlparser/CollectingHandlerForMutliTarget.js b/lib/plugins/system/htmlparser/CollectingHandlerForMutliTarget.js index efe915b80..29b8c6ff4 100644 --- a/lib/plugins/system/htmlparser/CollectingHandlerForMutliTarget.js +++ b/lib/plugins/system/htmlparser/CollectingHandlerForMutliTarget.js @@ -7,6 +7,7 @@ export function CollectingHandlerForMutliTarget(cbsArray){ this._cbsArray = cbsArray || []; + this._virtualCount = 0; this.events = []; } @@ -35,11 +36,16 @@ EVENTS.forEach(function(name) { }; }); +CollectingHandlerForMutliTarget.prototype.hasNoHandlers = function() { + return this._cbsArray.length === 0 && this._virtualCount === 0; +}; + CollectingHandlerForMutliTarget.prototype.addHandler = function(cbs) { + const wasEmpty = this.hasNoHandlers(); this._cbsArray.push(cbs); this._emitEventsFor(cbs); - if (this._cbsArray.length === 1) { + if (wasEmpty) { // Got first handler, resume stream. this.onFirstHandler(); } @@ -52,7 +58,7 @@ CollectingHandlerForMutliTarget.prototype.removeHandler = function(cbs) { var idx = that._cbsArray.indexOf(cbs); if (idx > -1) { that._cbsArray.splice(idx, 1); - if (that._cbsArray.length === 0) { + if (that.hasNoHandlers()) { // No handlers, pause stream. that.onNoHandlers(); } @@ -60,6 +66,26 @@ CollectingHandlerForMutliTarget.prototype.removeHandler = function(cbs) { }); }; +CollectingHandlerForMutliTarget.prototype.addVirtualHandler = function() { + const wasEmpty = this.hasNoHandlers(); + this._virtualCount++; + if (wasEmpty) { + this.onFirstHandler(); + } +}; + +CollectingHandlerForMutliTarget.prototype.removeVirtualHandler = function() { + var that = this; + process.nextTick(function() { + if (that._virtualCount > 0) { + that._virtualCount--; + if (that.hasNoHandlers()) { + that.onNoHandlers(); + } + } + }); +}; + CollectingHandlerForMutliTarget.prototype.emitCb = function(event) { this.events.push(event); this.callCb(event); @@ -128,4 +154,4 @@ CollectingHandlerForMutliTarget.prototype._emitEventsFor = function(cbs) { } }; -export const notPlugin = true; \ No newline at end of file +export const notPlugin = true; diff --git a/lib/plugins/system/htmlparser/TextResponseHandler.js b/lib/plugins/system/htmlparser/TextResponseHandler.js new file mode 100644 index 000000000..e9bc89f03 --- /dev/null +++ b/lib/plugins/system/htmlparser/TextResponseHandler.js @@ -0,0 +1,39 @@ +export class TextResponseHandler { + + constructor() { + this.text = ''; + this._onEndCallbacks = []; + this._ended = false; + } + + onData(chunk) { + // TODO: decode? + this.text += chunk; + } + + onEnd(callback) { + if (typeof callback === 'function') { + if (this._ended) { + callback(this.text); + } else { + this._onEndCallbacks.push(callback); + } + } + } + + end() { + this._ended = true; + for (const cb of this._onEndCallbacks) { + cb(this.text); + } + this._onEndCallbacks = []; + } + + attach(resp) { + resp.on('data', chunk => this.onData(chunk)); + resp.on('end', () => this.end()); + return this; + } +} + +export const notPlugin = true; diff --git a/lib/plugins/system/htmlparser/htmlparser.js b/lib/plugins/system/htmlparser/htmlparser.js index 0cedbf49a..58305340c 100644 --- a/lib/plugins/system/htmlparser/htmlparser.js +++ b/lib/plugins/system/htmlparser/htmlparser.js @@ -7,6 +7,7 @@ import * as metaUtils from '../meta/utils.js'; import { extendCookiesJar } from '../../../fetch.js'; var getUrlFunctional = utils.getUrlFunctional; import { CollectingHandlerForMutliTarget } from './CollectingHandlerForMutliTarget.js'; +import { TextResponseHandler } from './TextResponseHandler.js'; export default { @@ -181,6 +182,9 @@ export default { } } + // Collect full response text. + var responseTextHandler = new TextResponseHandler(); + // Init htmlparser handler. var handler = new CollectingHandlerForMutliTarget(); handler.onNoHandlers = function() { @@ -203,9 +207,13 @@ export default { // Do before resume? cb(null, { - htmlparser: handler + htmlparser: handler, + textresponse: responseTextHandler }); + // Bind on data, on end. + responseTextHandler.attach(resp); + // Proxy data. resp.on('data', parser.write.bind(parser)); resp.on('end', parser.end.bind(parser)); diff --git a/lib/utils.js b/lib/utils.js index a940c04d3..361a7aa78 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -102,11 +102,15 @@ export function prepareRequestOptions(request_options, options) { if (typeof prerender_option === 'string') { request_options.uri += (request_options.uri.indexOf('?') > -1 ? '&' : '?' ) + `prerender=${prerender_option}`; } + + if (typeof options?.prerender?.overrideOptions === 'function') { + options.prerender.overrideOptions(request_options); + } } } // Some calls (like oembed) use basic options without `getDomainOptions`. - var enable_domain_prerender = options?.getDomainOptions && options.getDomainOptions('meta.prerender') || options.prerender; + var enable_domain_prerender = options?.getDomainOptions && options.getDomainOptions('meta.prerender') || options?.prerender; if (enable_domain_prerender) { setPrerender(enable_domain_prerender); } From 9d800772b7abb7047da3a91af72bec22b37d880e Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 15:08:35 +0300 Subject: [PATCH 03/17] recall -> retry --- lib/core.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/core.js b/lib/core.js index fc675a9b8..a0c832287 100644 --- a/lib/core.js +++ b/lib/core.js @@ -1438,7 +1438,7 @@ responseError: 'responseError', timeout: 'timeout', fallback: 'fallback', - recall: 'recall' // Make same iframely call with different options. + retry: 'retry' // Make same iframely call with different options. }; function findRedirectError(result) { @@ -1459,16 +1459,16 @@ } } - function findRecallError(result) { + function findRetryError(result) { if (result) { for(var i = 0; i < result.length; i++) { var r = result[i]; - var recall = r.error && r.error[SYS_ERRORS.recall]; - if (typeof recall === "object") { - log(' -- plugin recall (by "' + r.method.pluginId + '")'); - return recall; - } else if (recall && typeof recall !== "object") { - log(' -- skip plugin recall, not object (by "' + r.method.pluginId + '")', recall); + var retry = r.error && r.error[SYS_ERRORS.retry]; + if (typeof retry === "object") { + log(' -- plugin retry (by "' + r.method.pluginId + '")'); + return retry; + } else if (retry && typeof retry !== "object") { + log(' -- skip plugin retry, not object (by "' + r.method.pluginId + '")', retry); } } } @@ -1755,7 +1755,7 @@ options = {}; } - // Store original to run on recall. + // Store original to run on retry. var originalOptions = {...options}; var initialCallback = cb; @@ -1971,14 +1971,14 @@ return; } - // Find recall command. - var recall_data = findRecallError(result); - if (recall_data && options.isRecall) { - log(' -- ignore recursive recall'); - } else if (recall_data) { + // Find retry command. + var retry_data = findRetryError(result); + if (retry_data && options.isRetry) { + log(' -- ignore recursive retry'); + } else if (retry_data) { abortCurrentRequest(); - var recallOptions = Object.assign({}, originalOptions, recall_data, {isRecall: true}); - run(uri, recallOptions, cb); + var retryOptions = Object.assign({}, originalOptions, retry_data, {isRetry: true}); + run(uri, retryOptions, cb); aborted = true; return; } From 06469001b3fc663172f20e6f9784d74d86f765e7 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 15:59:54 +0300 Subject: [PATCH 04/17] prerender with retry --- lib/plugins/system/meta/utils.js | 6 +++++- plugins/links/prerender/prerender-retry.js | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 plugins/links/prerender/prerender-retry.js diff --git a/lib/plugins/system/meta/utils.js b/lib/plugins/system/meta/utils.js index 7187ab536..b342dee91 100644 --- a/lib/plugins/system/meta/utils.js +++ b/lib/plugins/system/meta/utils.js @@ -2,8 +2,12 @@ export function getMetaCacheKey(url, whitelistRecord, options) { var meta_key = 'meta'; + if (options.prerender) { + meta_key += ':prerender'; + } + if (options.metaKeyPrefix) { - meta_key += ':' + options.metaKeyPrefix; + meta_key += ':' + options.metaKeyPrefix; } meta_key += ':' + url; diff --git a/plugins/links/prerender/prerender-retry.js b/plugins/links/prerender/prerender-retry.js new file mode 100644 index 000000000..9d6339a19 --- /dev/null +++ b/plugins/links/prerender/prerender-retry.js @@ -0,0 +1,19 @@ +export default { + + highestPriority: true, + + getData: function(url, __allowJSRender, cb) { + + if (CONFIG.PRERENDER && CONFIG.PRERENDER_URL && options.user_agent === CONFIG.FB_USER_AGENT + && !url.startsWith(CONFIG.PRERENDER_URL)) { + + cb({ + retry: { + prerender: true + } + }); + } else { + return cb(); + } + } +}; From a620ba0f94d5f2a1f21ddc3c888466c1d3bf6ae1 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 16:15:01 +0300 Subject: [PATCH 05/17] revert change meta key --- lib/plugins/system/meta/utils.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/plugins/system/meta/utils.js b/lib/plugins/system/meta/utils.js index b342dee91..dff12cf89 100644 --- a/lib/plugins/system/meta/utils.js +++ b/lib/plugins/system/meta/utils.js @@ -2,10 +2,6 @@ export function getMetaCacheKey(url, whitelistRecord, options) { var meta_key = 'meta'; - if (options.prerender) { - meta_key += ':prerender'; - } - if (options.metaKeyPrefix) { meta_key += ':' + options.metaKeyPrefix; } From d7b9dd7290ceced91c468b3b798598e6100a5227 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 16:15:52 +0300 Subject: [PATCH 06/17] refresh meta on prerender retry --- plugins/links/prerender/prerender-retry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/links/prerender/prerender-retry.js b/plugins/links/prerender/prerender-retry.js index 9d6339a19..9484d2fa2 100644 --- a/plugins/links/prerender/prerender-retry.js +++ b/plugins/links/prerender/prerender-retry.js @@ -9,6 +9,7 @@ export default { cb({ retry: { + refresh: true, // Refresh meta. prerender: true } }); From 42759f744d8a66f05e06f1f244764c787ce2b282 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 17:22:15 +0300 Subject: [PATCH 07/17] keep response body if `options.enableTextResponseHandler` --- lib/plugins/system/htmlparser/htmlparser.js | 25 ++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/plugins/system/htmlparser/htmlparser.js b/lib/plugins/system/htmlparser/htmlparser.js index 58305340c..76bb263b9 100644 --- a/lib/plugins/system/htmlparser/htmlparser.js +++ b/lib/plugins/system/htmlparser/htmlparser.js @@ -182,9 +182,6 @@ export default { } } - // Collect full response text. - var responseTextHandler = new TextResponseHandler(); - // Init htmlparser handler. var handler = new CollectingHandlerForMutliTarget(); handler.onNoHandlers = function() { @@ -205,14 +202,22 @@ export default { handler.abortController = abortController; handler.h2 = resp.h2; - // Do before resume? - cb(null, { - htmlparser: handler, - textresponse: responseTextHandler - }); + var result = { + htmlparser: handler + }; - // Bind on data, on end. - responseTextHandler.attach(resp); + var responseTextHandler; + if (options.enableTextResponseHandler) { + // Collect full response text. + responseTextHandler = new TextResponseHandler(); + // Bind on data, on end. + responseTextHandler.attach(resp); + + result.textresponse = responseTextHandler; + } + + // Do before resume? + cb(null, result); // Proxy data. resp.on('data', parser.write.bind(parser)); From 8e9da80786a12ba264a1d433fba906d62b171acd Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 19:18:02 +0300 Subject: [PATCH 08/17] retry: retriesCount to detect loop --- lib/core.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/core.js b/lib/core.js index a0c832287..e670d963a 100644 --- a/lib/core.js +++ b/lib/core.js @@ -1805,6 +1805,11 @@ return; } + if (options.retriesCount && options.retriesCount > (options.maxRedirects || CONFIG.MAX_REDIRECTS)) { + callbackWithErrorCode('retry loop', options, cb); + return; + } + // Remove default :443 and :80 from uri. uri = uri .replace(/^(http:\/\/[^\/]+):80(?!\d)/, '$1') @@ -1977,7 +1982,9 @@ log(' -- ignore recursive retry'); } else if (retry_data) { abortCurrentRequest(); - var retryOptions = Object.assign({}, originalOptions, retry_data, {isRetry: true}); + var retryOptions = Object.assign({}, originalOptions, retry_data); + retryOptions.retriesCount = (retryOptions.retriesCount || 0) + 1; + retryOptions.refresh = true; run(uri, retryOptions, cb); aborted = true; return; From 02cf86b2c93bb5c022d4767ba1f196caa5b4420c Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 19:19:19 +0300 Subject: [PATCH 09/17] TextResponseHandler > HtmlHandler --- .../{TextResponseHandler.js => HtmlHandler.js} | 2 +- lib/plugins/system/htmlparser/htmlparser.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename lib/plugins/system/htmlparser/{TextResponseHandler.js => HtmlHandler.js} (95%) diff --git a/lib/plugins/system/htmlparser/TextResponseHandler.js b/lib/plugins/system/htmlparser/HtmlHandler.js similarity index 95% rename from lib/plugins/system/htmlparser/TextResponseHandler.js rename to lib/plugins/system/htmlparser/HtmlHandler.js index e9bc89f03..8f33e9638 100644 --- a/lib/plugins/system/htmlparser/TextResponseHandler.js +++ b/lib/plugins/system/htmlparser/HtmlHandler.js @@ -1,4 +1,4 @@ -export class TextResponseHandler { +export class HtmlHandler { constructor() { this.text = ''; diff --git a/lib/plugins/system/htmlparser/htmlparser.js b/lib/plugins/system/htmlparser/htmlparser.js index 76bb263b9..f31cc1a09 100644 --- a/lib/plugins/system/htmlparser/htmlparser.js +++ b/lib/plugins/system/htmlparser/htmlparser.js @@ -7,7 +7,7 @@ import * as metaUtils from '../meta/utils.js'; import { extendCookiesJar } from '../../../fetch.js'; var getUrlFunctional = utils.getUrlFunctional; import { CollectingHandlerForMutliTarget } from './CollectingHandlerForMutliTarget.js'; -import { TextResponseHandler } from './TextResponseHandler.js'; +import { HtmlHandler } from './HtmlHandler.js'; export default { @@ -206,14 +206,14 @@ export default { htmlparser: handler }; - var responseTextHandler; - if (options.enableTextResponseHandler) { + var htmlHandler; + if (options.enableHtmlHandler) { // Collect full response text. - responseTextHandler = new TextResponseHandler(); + htmlHandler = new HtmlHandler(); // Bind on data, on end. - responseTextHandler.attach(resp); + htmlHandler.attach(resp); - result.textresponse = responseTextHandler; + result.htmlresponse = htmlHandler; } // Do before resume? From 61b9b42662d5f8018164f62dba6235e326d09823 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 19:28:43 +0300 Subject: [PATCH 10/17] enableHtmlHandler > enableHtmlResponse --- lib/plugins/system/htmlparser/htmlparser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/plugins/system/htmlparser/htmlparser.js b/lib/plugins/system/htmlparser/htmlparser.js index f31cc1a09..26a989f49 100644 --- a/lib/plugins/system/htmlparser/htmlparser.js +++ b/lib/plugins/system/htmlparser/htmlparser.js @@ -13,6 +13,7 @@ export default { provides: [ 'self', + 'htmlresponse', '__nonHtmlContentData', '__nonHtmlContentResponse', '__statusCode' @@ -207,7 +208,7 @@ export default { }; var htmlHandler; - if (options.enableHtmlHandler) { + if (options.enableHtmlResponse) { // Collect full response text. htmlHandler = new HtmlHandler(); // Bind on data, on end. From bd3f6bd39ceffd36411acb64150db6a1dec62593 Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 20:37:32 +0300 Subject: [PATCH 11/17] refresh is default for retry --- plugins/links/prerender/prerender-retry.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/links/prerender/prerender-retry.js b/plugins/links/prerender/prerender-retry.js index 9484d2fa2..9d6339a19 100644 --- a/plugins/links/prerender/prerender-retry.js +++ b/plugins/links/prerender/prerender-retry.js @@ -9,7 +9,6 @@ export default { cb({ retry: { - refresh: true, // Refresh meta. prerender: true } }); From d5631d79cd2a84885bfb79c9c6bfd4ab6883afbc Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Tue, 2 Jun 2026 22:21:09 +0300 Subject: [PATCH 12/17] move htmlHandler declaration --- lib/plugins/system/htmlparser/htmlparser.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/plugins/system/htmlparser/htmlparser.js b/lib/plugins/system/htmlparser/htmlparser.js index 26a989f49..df808c495 100644 --- a/lib/plugins/system/htmlparser/htmlparser.js +++ b/lib/plugins/system/htmlparser/htmlparser.js @@ -207,10 +207,9 @@ export default { htmlparser: handler }; - var htmlHandler; if (options.enableHtmlResponse) { // Collect full response text. - htmlHandler = new HtmlHandler(); + var htmlHandler = new HtmlHandler(); // Bind on data, on end. htmlHandler.attach(resp); From 196fc3b546c9e019e165731c3ae85cc37eb5c648 Mon Sep 17 00:00:00 2001 From: Ivan Paramonau Date: Wed, 3 Jun 2026 09:47:14 -0400 Subject: [PATCH 13/17] simplify prerender retries --- plugins/links/prerender/prerender-retry.js | 19 ------- plugins/links/prerender/prerender.js | 52 ------------------- .../links/prerender/react-app-fb-fallback.js | 44 ---------------- .../{checkAppFlag.js => retry-prerender.js} | 19 +++---- plugins/links/prerender/utils.js | 2 +- 5 files changed, 9 insertions(+), 127 deletions(-) delete mode 100644 plugins/links/prerender/prerender-retry.js delete mode 100644 plugins/links/prerender/prerender.js delete mode 100644 plugins/links/prerender/react-app-fb-fallback.js rename plugins/links/prerender/{checkAppFlag.js => retry-prerender.js} (50%) diff --git a/plugins/links/prerender/prerender-retry.js b/plugins/links/prerender/prerender-retry.js deleted file mode 100644 index 9d6339a19..000000000 --- a/plugins/links/prerender/prerender-retry.js +++ /dev/null @@ -1,19 +0,0 @@ -export default { - - highestPriority: true, - - getData: function(url, __allowJSRender, cb) { - - if (CONFIG.PRERENDER && CONFIG.PRERENDER_URL && options.user_agent === CONFIG.FB_USER_AGENT - && !url.startsWith(CONFIG.PRERENDER_URL)) { - - cb({ - retry: { - prerender: true - } - }); - } else { - return cb(); - } - } -}; diff --git a/plugins/links/prerender/prerender.js b/plugins/links/prerender/prerender.js deleted file mode 100644 index be4e591b5..000000000 --- a/plugins/links/prerender/prerender.js +++ /dev/null @@ -1,52 +0,0 @@ -import utils from './utils.js'; - -export default { - - highestPriority: true, - - provides: ['appUriData', 'whenPrerender'], - - getData: function(url, __allowJSRender, iframelyRun, options, meta, cb) { - - if (CONFIG.PRERENDER && CONFIG.PRERENDER_URL && options.user_agent === CONFIG.FB_USER_AGENT - && !url.startsWith(CONFIG.PRERENDER_URL)) { - - var prerenderUrl = CONFIG.PRERENDER_URL + encodeURIComponent(url); - var options2 = {...options, ...{ - debug: false, - refresh: true - }}; - - iframelyRun(prerenderUrl, options2, function(error, data) { - - var title = data && data.meta && ((data.meta.og && data.meta.og.title) || (data.meta.twitter && data.meta.twitter.title) || data.meta.title || data.meta['html-title']); - - if (data && data.meta && utils.maybeApp(data.meta)) { - return cb({ - responseStatusCode: 415 - }); - } else { - if (data.meta.canonical - && data.meta.canonical.startsWith(CONFIG.PRERENDER_URL) - ) { - delete data.meta.canonical; - } - return cb(error, { - appUriData: data, - whenPrerender: true - }); - } - }); - } else { - return cb(); - } - }, - - getMeta: function(appUriData, whenPrerender) { - return {...appUriData.meta}; - }, - - getLinks: function(appUriData, whenPrerender) { - return appUriData.links; - } -}; \ No newline at end of file diff --git a/plugins/links/prerender/react-app-fb-fallback.js b/plugins/links/prerender/react-app-fb-fallback.js deleted file mode 100644 index 35182ca4b..000000000 --- a/plugins/links/prerender/react-app-fb-fallback.js +++ /dev/null @@ -1,44 +0,0 @@ -import utils from './utils.js'; - -export default { - - highestPriority: true, - - provides: ['appUriData', 'whenReact'], - - getData: function(url, __allowJSRender, iframelyRun, options, cb) { - - if (options.user_agent === CONFIG.FB_USER_AGENT - || CONFIG.PRERENDER_URL && url.startsWith(CONFIG.PRERENDER_URL)) { - return cb(); - } - - var options2 = {...options, ...{ - debug: false, - refresh: true, - user_agent: CONFIG.FB_USER_AGENT - }}; - - iframelyRun(url, options2, function(error, data) { - - if (data && data.meta && utils.maybeApp(data.meta)) { - return cb({ - responseStatusCode: 415 - }); - } else { - return cb(error, { - appUriData: data, - whenReact: true - }); - } - }); - }, - - getMeta: function(appUriData, whenReact) { - return {...appUriData.meta} - }, - - getLinks: function(appUriData, whenReact) { - return appUriData.links; - } -}; \ No newline at end of file diff --git a/plugins/links/prerender/checkAppFlag.js b/plugins/links/prerender/retry-prerender.js similarity index 50% rename from plugins/links/prerender/checkAppFlag.js rename to plugins/links/prerender/retry-prerender.js index 58cc3aa30..26e956e51 100644 --- a/plugins/links/prerender/checkAppFlag.js +++ b/plugins/links/prerender/retry-prerender.js @@ -2,24 +2,21 @@ import utils from './utils.js'; export default { - provides: '__allowJSRender', + notPlugin: !CONFIG.PRERENDER_URL, - listed: true, + getData: function(meta, cb) { - getData: function(meta, url, options, cb) { + if (utils.maybeSPA(meta)) { - if (CONFIG.PRERENDER_URL && url.startsWith(CONFIG.PRERENDER_URL)) { - return cb(); - } - - if (utils.maybeApp(meta)) { - return cb(null, { - __allowJSRender: true, + return cb({ + retry: { + prerender: true + }, message: "This looks like JS app with no prerender. If you are the owner, please run templates on the server for Iframely robot." }); + } else { return cb(); } - } }; \ No newline at end of file diff --git a/plugins/links/prerender/utils.js b/plugins/links/prerender/utils.js index eca81159d..66ac0eac6 100644 --- a/plugins/links/prerender/utils.js +++ b/plugins/links/prerender/utils.js @@ -2,7 +2,7 @@ export default { notPlugin: true, - maybeApp: function(meta) { + maybeSPA: function(meta) { const title = (meta.og && meta.og.title) || (meta.twitter && meta.twitter.title) || meta.title || meta['html-title']; const maybeApp = From e6e55653411be2d412928c6923c503491a0fea8e Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Wed, 3 Jun 2026 20:09:45 +0300 Subject: [PATCH 14/17] prevent recursive retry --- lib/core.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/core.js b/lib/core.js index e670d963a..3e3e77bb8 100644 --- a/lib/core.js +++ b/lib/core.js @@ -1978,8 +1978,13 @@ // Find retry command. var retry_data = findRetryError(result); - if (retry_data && options.isRetry) { - log(' -- ignore recursive retry'); + if (retry_data + && options.retriesCount + // Prerender not changed + && !!options.prerender === !!retry_data.prerender + // User agent not changed + && options.user_agent == retry_data.user_agent) { + log(' -- ignore recursive prerender retry'); } else if (retry_data) { abortCurrentRequest(); var retryOptions = Object.assign({}, originalOptions, retry_data); From 74dbd4f4165c48b030da90944886134ade4672fa Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Thu, 4 Jun 2026 18:20:22 +0300 Subject: [PATCH 15/17] fix passing data param to reader.js --- modules/api/views.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/api/views.js b/modules/api/views.js index c555d1136..13be2274c 100644 --- a/modules/api/views.js +++ b/modules/api/views.js @@ -140,6 +140,7 @@ export default function(app) { dataMode: getBooleanParam(req, 'dataMode'), forceParams: req.query.meta === "true" ? CONFIG.DEBUG_CONTEXTS : null, whitelist: getBooleanParam(req, 'whitelist'), + // TOOD: obsolete? readability: getBooleanParam(req, 'readability'), getWhitelistRecord: whitelist.findWhitelistRecordFor, maxWidth: getIntParam(req, 'maxwidth') || getIntParam(req, 'max-width'), @@ -158,9 +159,15 @@ export default function(app) { } if (result.safe_html) { + var readerJsUrl = new URL('/reader.js', CONFIG.baseAppUrl); + readerJsUrl.searchParams.set('uri', uri); + if (req.query.dataMode) { + readerJsUrl.searchParams.set('dataMode', req.query.dataMode); + } + cache.set('html:' + version + ':' + uri, result.safe_html); result.links.unshift({ - href: CONFIG.baseAppUrl + "/reader.js?uri=" + encodeURIComponent(uri), + href: readerJsUrl.toString(), type: CONFIG.T.javascript, rel: [CONFIG.R.reader, CONFIG.R.inline] }); @@ -280,7 +287,9 @@ export default function(app) { iframelyCore.run(uri, { v: '1.3', getWhitelistRecord: whitelist.findWhitelistRecordFor, - readability: true + // TOOD: obsolete? + readability: true, + dataMode: getBooleanParam(req, 'dataMode'), }, function(error, data) { if (!data || !data.safe_html) { From 443f1e48dde4899a37c1c1ca888806f001600fda Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Thu, 4 Jun 2026 18:27:28 +0300 Subject: [PATCH 16/17] fix reader.js url --- modules/api/views.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/api/views.js b/modules/api/views.js index 13be2274c..89d6dfa23 100644 --- a/modules/api/views.js +++ b/modules/api/views.js @@ -159,15 +159,16 @@ export default function(app) { } if (result.safe_html) { - var readerJsUrl = new URL('/reader.js', CONFIG.baseAppUrl); - readerJsUrl.searchParams.set('uri', uri); + var readerJsParams = new url.URLSearchParams({ + uri: uri + }); if (req.query.dataMode) { - readerJsUrl.searchParams.set('dataMode', req.query.dataMode); + readerJsParams.set('dataMode', req.query.dataMode); } cache.set('html:' + version + ':' + uri, result.safe_html); result.links.unshift({ - href: readerJsUrl.toString(), + href: CONFIG.baseAppUrl + '/reader.js?' + readerJsParams.toString(), type: CONFIG.T.javascript, rel: [CONFIG.R.reader, CONFIG.R.inline] }); From 7981c063745e08bc12fff22689cdd434768a9cdf Mon Sep 17 00:00:00 2001 From: Nazar Leush Date: Thu, 4 Jun 2026 18:50:02 +0300 Subject: [PATCH 17/17] fix reader.js script query match --- views/readerjs.ejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/readerjs.ejs b/views/readerjs.ejs index b22b2bfce..8a99c5d60 100644 --- a/views/readerjs.ejs +++ b/views/readerjs.ejs @@ -18,11 +18,11 @@ $cont.parent().trigger('iframely.loaded'); } - var $container = $('[data-used!=true][iframely-container-for$="' + encodeURIComponent(iframely.uri) + '"]:first'); + var $container = $('[data-used!=true][iframely-container-for*="' + encodeURIComponent(iframely.uri) + '"]:first'); if ($container.length == 0) { - var $script = $('script[data-used!=true][src$="' + encodeURIComponent(iframely.uri) + '"]:first'); + var $script = $('script[data-used!=true][src*="' + encodeURIComponent(iframely.uri) + '"]:first'); $script.attr('data-used', true); var $container = $('
');