@hraness/sponge 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/capture/worker.js +1 -1
- package/dist/src/app/article.js +5 -2
- package/extension/background.js +29 -0
- package/extension/manifest.json +1 -1
- package/native/runtime-manifest.json +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,12 +72,17 @@ never reads cookies, passwords or the browser's profile.
|
|
|
72
72
|
Once the extension is connected, a page that comes back thin from the public web
|
|
73
73
|
is tried again through the browser: first a plain fetch with your cookies, then a
|
|
74
74
|
muted background tab that runs the page's scripts and closes afterwards.
|
|
75
|
+
When a site shows a security check (Cloudflare's "Just a moment…"), the tab waits
|
|
76
|
+
up to 20 seconds for your browser to clear it on its own, as it usually does for
|
|
77
|
+
a browser the site already knows. Sponge never clicks or answers a check; one that
|
|
78
|
+
stays is reported as blocked.
|
|
75
79
|
`refresh ITEM --browser` (or `add URL --browser`) skips the public web and reads
|
|
76
80
|
only through the browser. Its popup and right-click menu save the current page or
|
|
77
81
|
a link. If no browser is connected, a `--browser` job fails as `browser-offline`
|
|
78
82
|
and can be retried.
|
|
79
83
|
|
|
80
|
-
Pages captured through the browser
|
|
84
|
+
Pages captured through the browser are private, even if the item was shared
|
|
85
|
+
before; `sponge visibility ITEM public` shares one again. Any program running as you can
|
|
81
86
|
ask the extension for a page through Sponge's local socket, the same reach it
|
|
82
87
|
already has over your files. Turning off "Let Sponge load pages" in the popup
|
|
83
88
|
stops all background captures. `sponge browser remove` unregisters Sponge from
|
package/dist/capture/worker.js
CHANGED
|
@@ -529,7 +529,7 @@ ${qhe}
|
|
|
529
529
|
${s.join(`
|
|
530
530
|
|
|
531
531
|
`)}
|
|
532
|
-
`}function zhe(t,e){let r=Number(t.replace(/,/g,""));if(!Number.isFinite(r)||r<0)return null;let n=e?.toLowerCase()==="m"?1e6:e?.toLowerCase()==="k"?1e3:1,i=Math.floor(r*n);return Number.isSafeInteger(i)?i:null}function Zhe(t,e){let r=e==="x"?[/\bread\s+([\d,.]+)\s*([km])?\s+repl(?:y|ies)\b/i,/\b([\d,.]+)\s*([km])?\s+repl(?:y|ies)\b/i]:[/\b(?:view|read|show)\s+(?:all\s+)?([\d,.]+)\s*([km])?\s+comments?\b/i];for(let n of r){let i=n.exec(t);if(i?.[1]===void 0)continue;let a=zhe(i[1],i[2]);if(a!==null)return a}return null}var Xhe=/(?:verify (?:that )?you are (?:a )?human|(?:complete|solve) (?:the )?captcha|\bcaptcha\b|access denied|request (?:has been )?blocked|unusual traffic|cloudflare ray id)/i,epe=/^(?:403(?: forbidden)?|access denied|request blocked|unusual traffic|verify (?:that )?you are (?:a )?human|human verification|captcha|security (?:check|verification)|attention required|just a moment(?:\.{3})?)$/i,tpe=/(?:\b(?:please )?(?:verify|confirm) (?:that )?you are (?:a )?human\b|\b(?:complete|solve) (?:the )?captcha\b|\b(?:you (?:do not|don't) have permission|you have been blocked)\b|\b(?:your|this) (?:request|access|ip(?: address)?) (?:has been|was|is) blocked\b|\bunusual traffic from your (?:computer )?network\b|\bautomated (?:queries|requests)\b|\b(?:before proceeding|to continue),? (?:please )?(?:verify|complete|enable)\b|\bcloudflare ray id\b)/i,rpe=/(?:^|\n)[ \t]*(?:#{1,6}[ \t]+)?(?:403(?: forbidden)?|access denied|request blocked|unusual traffic|verify (?:that )?you are (?:a )?human|captcha|security (?:check|verification))[.!]?[ \t]*(?:\r?\n|$)/i,npe=/\b(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)\b/i,ipe=/^(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)$/i,ape=/(?:^|\n)[ \t]*(?:#{1,6}[ \t]+)?(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)[.!]?[ \t]*(?:\r?\n|$)/i,spe=/\b(?:please )?(?:(?:try|retry) again(?: later| in \d+)|wait (?:a moment|before retrying)|respect the retry-after header)\b/i,ope=/(?:\bhow to\b|\btroubleshoot(?:ing)?\b|\b(?:this|the) (?:article|guide|tutorial)\b|\b(?:this|the) (?:article|guide|tutorial) explains?\b|\blearn (?:how|why)\b)/i,X7=4096,eH=160,upe=24;function cpe(t,e){if(t.length>X7)return!1;let r=na(t);if(r>eH)return!1;let n=(e??"").slice(0,Kr.title).replace(/\s+/g," ").trim(),i=`${n}
|
|
532
|
+
`}function zhe(t,e){let r=Number(t.replace(/,/g,""));if(!Number.isFinite(r)||r<0)return null;let n=e?.toLowerCase()==="m"?1e6:e?.toLowerCase()==="k"?1e3:1,i=Math.floor(r*n);return Number.isSafeInteger(i)?i:null}function Zhe(t,e){let r=e==="x"?[/\bread\s+([\d,.]+)\s*([km])?\s+repl(?:y|ies)\b/i,/\b([\d,.]+)\s*([km])?\s+repl(?:y|ies)\b/i]:[/\b(?:view|read|show)\s+(?:all\s+)?([\d,.]+)\s*([km])?\s+comments?\b/i];for(let n of r){let i=n.exec(t);if(i?.[1]===void 0)continue;let a=zhe(i[1],i[2]);if(a!==null)return a}return null}var Xhe=/(?:verify (?:that )?you are (?:a )?human|(?:complete|solve) (?:the )?captcha|\bcaptcha\b|access denied|request (?:has been )?blocked|unusual traffic|cloudflare ray id)/i,epe=/^(?:403(?: forbidden)?|access denied|request blocked|unusual traffic|verify (?:that )?you are (?:a )?human|human verification|captcha|security (?:check|verification)|attention required|just a moment(?:\.{3})?)$/i,tpe=/(?:\b(?:please )?(?:verify|confirm) (?:that )?you are (?:a )?human\b|\b(?:complete|completing|solve) (?:the |a )?(?:captcha|security check)\b|\b(?:you (?:do not|don't) have permission|you have been blocked)\b|\b(?:your|this) (?:request|access|ip(?: address)?) (?:has been|was|is) blocked\b|\bunusual traffic from your (?:computer )?network\b|\bautomated (?:queries|requests)\b|\b(?:before proceeding|to continue),? (?:please )?(?:verify|complete|enable)\b|\bcloudflare ray id\b)/i,rpe=/(?:^|\n)[ \t]*(?:#{1,6}[ \t]+)?(?:403(?: forbidden)?|access denied|request blocked|unusual traffic|verify (?:that )?you are (?:a )?human|captcha|security (?:check|verification))[.!]?[ \t]*(?:\r?\n|$)/i,npe=/\b(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)\b/i,ipe=/^(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)$/i,ape=/(?:^|\n)[ \t]*(?:#{1,6}[ \t]+)?(?:429(?: too many requests)?|too many requests|rate limit(?:ed| exceeded)?)[.!]?[ \t]*(?:\r?\n|$)/i,spe=/\b(?:please )?(?:(?:try|retry) again(?: later| in \d+)|wait (?:a moment|before retrying)|respect the retry-after header)\b/i,ope=/(?:\bhow to\b|\btroubleshoot(?:ing)?\b|\b(?:this|the) (?:article|guide|tutorial)\b|\b(?:this|the) (?:article|guide|tutorial) explains?\b|\blearn (?:how|why)\b)/i,X7=4096,eH=160,upe=24;function cpe(t,e){if(t.length>X7)return!1;let r=na(t);if(r>eH)return!1;let n=(e??"").slice(0,Kr.title).replace(/\s+/g," ").trim(),i=`${n}
|
|
533
533
|
${t}`;if(ope.test(i))return!1;if(r<=upe&&(rpe.test(t)||ape.test(t)))return!0;let a=epe.test(n)||ipe.test(n),s=npe.test(t)&&spe.test(t);return(a||Xhe.test(t)||s)&&(a||tpe.test(t)||s)}var tH=/(?:\b(?:sign|log) in to (?:continue|read|view|see|access|comment|reply)\b|\blogin required\b|\bmembers? only\b|\bsubscriber-only\b|\bsubscribe to (?:continue|read)\b|\bthis content is private\b|\byou must be logged in\b)/i,lpe=/\b(?:subscribe(?: now)? to (?:unlock (?:this|the) (?:article|story|content)|(?:keep|continue) reading)|create (?:an? |your )?account to (?:keep|continue) reading|register to (?:keep|continue) reading|sign up to (?:keep|continue) reading)\b/i,dpe=/^(?:subscribe(?: now)? to (?:unlock (?:this|the) (?:article|story|content)|(?:keep|continue) reading)|create (?:an? |your )?account to (?:keep|continue) reading|register to (?:keep|continue) reading|sign up to (?:keep|continue) reading)[.!]?$/i,fpe=/(?:^|\n)[ \t]*(?:#{1,6}[ \t]+)?(?:subscribe(?: now)? to (?:unlock (?:this|the) (?:article|story|content)|(?:keep|continue) reading)|create (?:an? |your )?account to (?:keep|continue) reading|register to (?:keep|continue) reading|sign up to (?:keep|continue) reading)[.!]?[ \t]*(?:\r?\n|$)/i,hpe=/\b(?:analy[sz](?:e|es|ed|ing)|discuss(?:es|ed|ing)?|examin(?:e|es|ed|ing)|quot(?:e|es|ed|ing)|stud(?:y|ies|ied|ying)|interface copy|wording|publishers? (?:sometimes )?(?:say|write|use))\b/i,ppe=/(?:enable javascript|javascript is disabled|something went wrong|try reloading)/i,mpe=/\bjoin\s+x\s+now\s+to\s+read\s+repl(?:y|ies)\b/i,gpe=/\blog\s*in\s*sign\s*up\b/i,Ape=/\blog\s*in\b/i,bpe=/\bsign\s*up\b/i;function xpe(t,e){if(t.length>X7)return!1;let r=na(t);if(r>eH)return!1;let n=(e??"").slice(0,Kr.title).replace(/\s+/g," ").trim();if(dpe.test(n)||fpe.test(t))return!0;if(r>48)return!1;let i=`${n}
|
|
534
534
|
${t}`;return lpe.test(i)&&!hpe.test(i)}function ype(t,e){return tH.test(t)?!0:e==="x"&&(mpe.test(t)||gpe.test(t)||Ape.test(t)&&bpe.test(t))}function Yv(t,e,r,n,i){let a=`${e??""}
|
|
535
535
|
${t}`;if(cpe(t,e))return"blocked";let s=tH.test(a)||xpe(t,e);return s&&t.length<1500?"auth-required":ppe.test(a)&&t.length<500||t.trim().length<40?"unsupported":s||n||i||r==="thread"||r==="comments"?"partial":"complete"}function wpe(t,e,r,n,i){let a={complete:5e3,partial:2e3,"auth-required":-2e3,blocked:-4e3,unsupported:-5e3},s=Y7(t.content,"image",100),o=Y7(t.content,"link",500),u=i.method.startsWith("browser")&&i.contentType?.toLowerCase().includes("text/plain")===!0?-500:0;return a[e]+Math.min(t.content.length,5e4)+Math.min(r,1e4)*5+Math.min(n,1e3)*50+s*100+o*5+u}function J7(t){let e=t.body.trim();if(e==="")return null;let r=qu(t.browserTitle,Kr.title),n=r===null?Epe(e):null,i=t.finalUrl.pathname,a=i.length;for(;a>0&&i.charCodeAt(a-1)===47;)a-=1;let s=i.lastIndexOf("/",a-1)+1,o=Jv(i,s,a,Kr.title);return{content:e,title:r??n??o??qu(t.finalUrl.hostname,Kr.title),author:null,published:null,description:null}}function Epe(t){let e=0;for(;e<t.length;){let r=t.indexOf(`
|
package/dist/src/app/article.js
CHANGED
|
@@ -127,9 +127,12 @@ export function ingestArticle(store, token, options) {
|
|
|
127
127
|
return { ok: true, itemId: source.itemId, revisionId: revision.revisionId, status };
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* A page read with the reader's sign-ins is private, even when the item was shared before: what the browser read may
|
|
132
|
+
* be the subscriber's text. `sponge visibility ITEM public` shares it again deliberately.
|
|
133
|
+
*/
|
|
131
134
|
function keepPrivate(store, itemId) {
|
|
132
|
-
if (store.identity(itemId).visibility.override
|
|
135
|
+
if (store.identity(itemId).visibility.override !== 'private')
|
|
133
136
|
store.setVisibility(itemId, 'private');
|
|
134
137
|
}
|
|
135
138
|
/** Prose words under which a page that names its paper's PDF is only a landing page: a title, authors and abstract. */
|
package/extension/background.js
CHANGED
|
@@ -89,6 +89,7 @@ async function renderPage(url, timeoutMs) {
|
|
|
89
89
|
try {
|
|
90
90
|
await chrome.tabs.update(tab.id, { muted: true });
|
|
91
91
|
if (!(await loaded(tab.id, deadline))) return { ok: false, code: 'timeout' };
|
|
92
|
+
await passedCheck(tab.id, Math.min(deadline, Date.now() + checkWaitMs));
|
|
92
93
|
const [result] = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: snapshot });
|
|
93
94
|
const page = result?.result;
|
|
94
95
|
if (!page || typeof page.html !== 'string') return { ok: false, code: 'failed' };
|
|
@@ -101,6 +102,34 @@ async function renderPage(url, timeoutMs) {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
/** How long a site's security check gets to let this browser through on its own. */
|
|
106
|
+
const checkWaitMs = 20_000;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Waits while the tab shows a security check (Cloudflare's "Just a moment…" and the like), which a real browser often
|
|
110
|
+
* clears by itself within seconds. Nothing is clicked or answered; a check that stays is captured as what it is.
|
|
111
|
+
*/
|
|
112
|
+
async function passedCheck(tabId, until) {
|
|
113
|
+
while (Date.now() < until) {
|
|
114
|
+
const [result] = await chrome.scripting.executeScript({ target: { tabId }, func: checking }).catch(() => [null]);
|
|
115
|
+
if (result?.result === false) return;
|
|
116
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
117
|
+
await loaded(tabId, until);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Runs in the page: whether it is a security check rather than the page itself. */
|
|
122
|
+
function checking() {
|
|
123
|
+
return (
|
|
124
|
+
/^(?:just a moment|one more step|attention required|security check|checking your browser)/i.test(
|
|
125
|
+
document.title.trim(),
|
|
126
|
+
) ||
|
|
127
|
+
document.querySelector(
|
|
128
|
+
'#challenge-running, #challenge-form, #challenge-stage, script[src*="/cdn-cgi/challenge-platform/"]',
|
|
129
|
+
) !== null
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
104
133
|
/** A new tab first reports its blank start page as complete; only the requested page counts. */
|
|
105
134
|
const navigated = (tab) => tab.status === 'complete' && !tab.pendingUrl && /^https?:/.test(tab.url ?? '');
|
|
106
135
|
|
package/extension/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Sponge",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.11",
|
|
5
5
|
"description": "Save pages to your local Sponge library with the sign-ins you already have. Cookies never leave the browser.",
|
|
6
6
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoSbZrBglr0h0193XOgavySnrG0DQif+WgYJ37CFSrbBwWP43rUz2ZzGRPvNveHgFZvFZlhGITJD0VC4NMQa2kxGAPP66RWEulIZDvGauxKonwcrlSneD+srPY8oMNX5sivTqw819uMwDA70oxBxKyTX4t4vwpwXuFX3Zf+5jwUOD3nz8XZFGT9uDh86MHyYYlURzsZQYBJSf2qAN17+OfhV7NHCWM5FdQodnOau6N/PcH/MstVGnbOaWeaTGged0PS2QGA5NDoOu5WZv1sUTHFqzM/Wsb/h1l8Kt5IfOZAXvhokhBBW7HTyYqgvX4wFHuj5fNioYshBUfMrKeZZFlQIDAQAB",
|
|
7
7
|
"minimum_chrome_version": "116",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
35
|
"build": {
|
|
36
|
-
"asrSourceSha256": "
|
|
36
|
+
"asrSourceSha256": "535c4265d279168ae6f971b875974669f6096dacefdfe217eeeb983eb300e9ab",
|
|
37
37
|
"clangVersion": "Apple clang version 21.0.0 (clang-2100.1.1.101)",
|
|
38
|
-
"embeddingSourceSha256": "
|
|
39
|
-
"mediaSourceSha256": "
|
|
38
|
+
"embeddingSourceSha256": "c0588228be185b42f031243e10de15aae6c0dd5d865dda63457083a37f478bf4",
|
|
39
|
+
"mediaSourceSha256": "b8af23eb8c2298235dde54f900ac523d6614c9b92afd5db36f2d21aaecdd9d07",
|
|
40
40
|
"rustcVersion": "rustc 1.97.1 (8bab26f4f 2026-07-14)",
|
|
41
41
|
"swiftVersion": "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)"
|
|
42
42
|
},
|
|
43
43
|
"minimumMacOS": "26.0",
|
|
44
|
-
"packageVersion": "0.6.
|
|
44
|
+
"packageVersion": "0.6.11",
|
|
45
45
|
"platform": "darwin",
|
|
46
46
|
"schemaVersion": 1,
|
|
47
|
-
"sourceCommit": "
|
|
47
|
+
"sourceCommit": "2b29176548506e04efd9f0a304de7c573b392e1d"
|
|
48
48
|
}
|