chore(send-to-readest): v0.2.1, zip packaging script, store submission doc (#4661)
Prep for the Chrome Web Store submission of the Send to Readest browser extension: - Bump manifest + package version 0.2.0 -> 0.2.1. - Add a `pnpm zip` script (scripts/zip.mjs) that builds and packages dist/ into a versioned, Web-Store-ready archive: manifest.json at the zip root, excluding webpack's `.LICENSE.txt` banners and `.DS_Store`. Ignore the produced `*.zip` in the extension's .gitignore. - Add STORE-SUBMISSION.md: copy-paste dashboard answers (single purpose, per-permission justifications, remote-code answer, data-use disclosures, listing copy) pointing at the canonical privacy policy hosted at https://www.readest.com/send-to-readest/privacy-policy. Verified: pnpm test:extension (51 passed), pnpm build-browser-ext, pnpm zip (valid archive), pnpm lint clean. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
# `chrome://extensions`.
|
||||
dist/
|
||||
|
||||
# Versioned upload archives produced by `pnpm zip`.
|
||||
*.zip
|
||||
|
||||
# Per-package node_modules (the workspace hoists most of it to the root,
|
||||
# but pnpm still drops a `.pnpm` symlink tree here).
|
||||
node_modules/
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# Chrome Web Store — submission copy
|
||||
|
||||
Copy-paste answers for the Developer Dashboard. Edit `<FILL IN>` placeholders
|
||||
before submitting. Keep these in sync with `manifest.json` — if a permission is
|
||||
added or removed, update the matching justification here.
|
||||
|
||||
---
|
||||
|
||||
## Single purpose
|
||||
|
||||
> Send to Readest saves the web page you are currently viewing into your
|
||||
> personal Readest library as a self-contained EPUB. When you click the
|
||||
> toolbar button, it extracts the readable article and its images and uploads
|
||||
> the result to your own Readest account.
|
||||
|
||||
## Permission justifications
|
||||
|
||||
Paste each into the **Privacy practices → Permission justification** field.
|
||||
|
||||
**`activeTab`**
|
||||
> Lets the extension read the page in the active tab at the moment the user
|
||||
> clicks the toolbar button, so it can capture the article the user is
|
||||
> currently viewing. No access is taken until the user invokes the action.
|
||||
|
||||
**`scripting`**
|
||||
> Used to inject the capture script into the active tab on click, which reads
|
||||
> the page's rendered DOM (via Mozilla Readability) to extract the article
|
||||
> body and its image references. It is not used to run any remotely hosted
|
||||
> code — the script is bundled in the extension.
|
||||
|
||||
**`storage`**
|
||||
> Caches the user's own Readest sign-in token (synced from the Readest website)
|
||||
> and an optional custom server URL in local extension storage, so the upload
|
||||
> can be authenticated without re-prompting for credentials. No browsing data
|
||||
> is stored.
|
||||
|
||||
**`offscreen`**
|
||||
> Creates an offscreen document to assemble the EPUB file, because the EPUB
|
||||
> building step needs DOM/Blob APIs that are unavailable in the service worker.
|
||||
> The offscreen document does no network or user-facing work.
|
||||
|
||||
**Host permissions (`<all_urls>`)**
|
||||
> The user can clip any web page, so the extension needs access to arbitrary
|
||||
> sites for two things, both triggered only by an explicit click:
|
||||
> (1) injecting the capture script into the page being clipped, and
|
||||
> (2) downloading the images that page references — using the user's existing
|
||||
> session so images behind a login or paywall capture correctly. Content and
|
||||
> images are sent only to the user's Readest account; nothing is sent to any
|
||||
> third party.
|
||||
|
||||
**Content scripts on `*.readest.com` / `localhost:3000`**
|
||||
> A content script runs only on Readest's own sites to copy the user's existing
|
||||
> Readest access token into the extension so uploads are authenticated to the
|
||||
> user's account. It never reads a password or refresh token.
|
||||
|
||||
## Remote code
|
||||
|
||||
> **No.** All libraries (zip.js, Mozilla Readability, DOMPurify, language
|
||||
> detection) are bundled in the extension package and execute locally. The
|
||||
> extension downloads only the images of the page the user chose to clip and
|
||||
> uploads the resulting EPUB to the user's Readest account.
|
||||
|
||||
## Data use disclosures
|
||||
|
||||
**Data types collected** (check in the dashboard):
|
||||
- ☑ **Website content** — the page text and images the user clips.
|
||||
- ☑ **Authentication information** — the user's Readest access token, used
|
||||
solely to authenticate the upload to the user's own account.
|
||||
- ☐ Everything else (personally identifiable info, location, financial,
|
||||
health, personal communications, web history, user activity) — **not**
|
||||
collected.
|
||||
|
||||
**Certifications** (all three apply — check each):
|
||||
- ☑ I do not sell or transfer user data to third parties, outside of the
|
||||
approved use cases.
|
||||
- ☑ I do not use or transfer user data for purposes unrelated to my item's
|
||||
single purpose.
|
||||
- ☑ I do not use or transfer user data to determine creditworthiness or for
|
||||
lending purposes.
|
||||
|
||||
**Privacy policy URL:** `https://www.readest.com/send-to-readest/privacy-policy`
|
||||
(canonical; maintained in the `readest-landing` repo at
|
||||
`src/app/[locale]/(legal)/send-to-readest/privacy-policy/page.mdx`)
|
||||
|
||||
## Store listing copy
|
||||
|
||||
**Name:** Send to Readest
|
||||
|
||||
**Short description (132 char max):**
|
||||
> Save web pages to your Readest library
|
||||
|
||||
**Detailed description (suggested):**
|
||||
> Send to Readest saves the page you're reading straight to your Readest
|
||||
> library with one click — as a clean, self-contained EPUB you can read on any
|
||||
> device.
|
||||
>
|
||||
> • One click captures the readable article, stripped of clutter.
|
||||
> • Images are bundled in, so the saved page opens fully offline.
|
||||
> • Works on pages behind a login or paywall — it captures what you can
|
||||
> actually see, using your existing session.
|
||||
> • The clipped book syncs to Readest on all your devices.
|
||||
>
|
||||
> You stay in control: nothing is captured until you click the button, and your
|
||||
> clips are sent only to your own Readest account. Sign in to Readest once and
|
||||
> you're set.
|
||||
|
||||
**Category:** Productivity
|
||||
|
||||
**Language:** English (en)
|
||||
|
||||
## Assets checklist (still needed before submitting)
|
||||
|
||||
- [ ] At least one screenshot, 1280×800 or 640×400 (PNG/JPG) — e.g. the popup
|
||||
mid-clip on a real article.
|
||||
- [ ] Store icon 128×128 — present at `icons/icon-128.png`.
|
||||
- [ ] Privacy policy hosted, URL filled in above.
|
||||
- [ ] (Optional) Promo tile 440×280 for featured placement.
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_app_name__",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"description": "__MSG_app_description__",
|
||||
"default_locale": "en",
|
||||
"permissions": ["activeTab", "scripting", "storage", "offscreen"],
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "@readest/send-to-readest-extension",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"private": true,
|
||||
"description": "Browser extension that clips the current web page into the user's Readest library as a self-contained EPUB.",
|
||||
"scripts": {
|
||||
"build": "webpack --mode production",
|
||||
"dev": "webpack --watch --mode development",
|
||||
"clean": "rimraf dist",
|
||||
"zip": "pnpm build && node scripts/zip.mjs",
|
||||
"i18n:extract": "node scripts/extract-i18n.mjs",
|
||||
"i18n:check": "node scripts/extract-i18n.mjs --check"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Package the built extension into a versioned, Chrome-Web-Store-ready zip.
|
||||
*
|
||||
* Invoked by `pnpm zip`, which builds first so the archive is never stale.
|
||||
* The zip root holds `manifest.json` directly — the layout the Web Store
|
||||
* dashboard expects — and drops the `.LICENSE.txt` banners webpack emits
|
||||
* plus macOS `.DS_Store` noise.
|
||||
*
|
||||
* Requires the `zip` CLI (present on macOS / Linux). This is a maintainer
|
||||
* packaging step, not part of the CI build.
|
||||
*/
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { existsSync, readFileSync, rmSync, statSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
||||
const DIST = join(ROOT, 'dist');
|
||||
const MANIFEST = join(DIST, 'manifest.json');
|
||||
|
||||
if (!existsSync(MANIFEST)) {
|
||||
console.error('[zip] dist/manifest.json not found — run `pnpm build` first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { version } = JSON.parse(readFileSync(MANIFEST, 'utf8'));
|
||||
const out = join(ROOT, `send-to-readest-${version}.zip`);
|
||||
if (existsSync(out)) rmSync(out);
|
||||
|
||||
// Zip the *contents* of dist/ (cwd: DIST) so manifest.json lands at the
|
||||
// archive root rather than under a `dist/` prefix.
|
||||
execFileSync('zip', ['-rq', out, '.', '-x', '*.DS_Store', '*.LICENSE.txt'], {
|
||||
cwd: DIST,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
const kb = Math.round(statSync(out).size / 1024);
|
||||
console.log(`[zip] send-to-readest-${version}.zip (${kb} KB) ready to upload`);
|
||||
Reference in New Issue
Block a user