Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Datasette Lite working without loading external resources #40

Open
simonw opened this issue Aug 15, 2022 · 24 comments
Open

Get Datasette Lite working without loading external resources #40

simonw opened this issue Aug 15, 2022 · 24 comments
Labels
enhancement New feature or request research

Comments

@simonw
Copy link
Owner

simonw commented Aug 15, 2022

If I'm going to eventually have it work offline as a PWA:

The first step is going to be having it work based entirely on files in this repository - with no external HTTP requests made at all.

I need that for both Pyodide and its WASM build as well as all of the Python wheels that Datasette Lite needs to install using micropip.

I think I've figured out how to do it: https://pyodide.org/en/stable/usage/loading-packages.html#installing-wheels-from-arbitrary-urls

Pure Python wheels can also be installed from any URL with micropip,

import micropip
micropip.install(
    'https://example.com/files/snowballstemmer-2.0.0-py2.py3-none-any.whl'
)

Micropip decides whether a file is a URL based on whether it ends in ".whl" or not.

Originally posted by @simonw in #26 (comment)

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

For simplicity here I'm inclined to have this https://github.com/simonw/datasette-lite Git repository contain vendored copies of all of the dependencies needed to run Datasette Lite.

That way anyone who clones this repo will have everything they need to run Datasette Lite themselves.

I like the privacy benefits of having everything loaded from the same domain, with no requests made at all to other domains.

@simonw simonw added enhancement New feature or request research labels Aug 15, 2022
@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I used Firefox to fully load Datasette Lite, then copied out a HAR file from the network pane (13.5MB of JSON).

Then I ran this:

cat /tmp/har.json | jq '.log.entries | map(.request.url)'  | sort | uniq

Resulting in this:

  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/MarkupSafe-2.1.1-cp310-cp310-emscripten_wasm32.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/PyYAML-6.0-cp310-cp310-emscripten_wasm32.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/distutils.tar",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/micropip-0.1-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/openssl-1.1.1n.zip",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/packages.json",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/packaging-21.3-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pluggy-1.0.0-py2.py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.asm.data",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.asm.js",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.asm.wasm",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide_py.tar",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyparsing-3.0.7-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/setuptools-62.0.0-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/six-1.16.0-py2.py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/ssl-1.0.0.zip",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/typing_extensions-4.1.1-py3-none-any.whl",
  "https://datasette.io/content.db",
  "https://files.pythonhosted.org/packages/04/a2/d918dcd22354d8958fe113e1a3630137e0fc8b44859ade3063982eacd2a4/idna-3.3-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/09/73/06cd6609f6f01b2336f26654a1e62ca8d02a0fdb86ec1055904124d76992/asgi_csrf-0.9-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/0a/98/98b3c6d954b6bd8531c611fb24be53ccc66438f493b05ec4b107942e27b7/Pint-0.18-py2.py3-none-any.whl",
  "https://files.pythonhosted.org/packages/0b/77/dbf4952b05efe08ab0ef4be14b6137717c00d0504f5a56ee6e80c010e6d0/click_default_group_wheel-1.2.2-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/12/d0/d998e0b558fd5808a4fdb48e906e6f57a4fda2177fa11ba2a9d16248ce92/uvicorn-0.18.2-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/4b/5b/6fc4a322c8ae1349d3cecf83f8412ec10a67b06b7518dea686cfef594b07/python_multipart-0.0.4-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/50/0e/73f7c45526aebb98d4bf22b91da3e1347b3fd8ae9d338e2999f806d070f6/datasette-0.62-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/52/b0/7b2e028b63d092804b6794595871f936aafa5e9322dcaaad50ebf67445b3/sniffio-1.2.0-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/60/0f/7a0eeea938eaf61074f29fed9717f2010e8d0e0905d36b38d3275a1e4622/h11-0.12.0-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/9a/cb/d30896ad0d288358bbae60576556df78422179fc08c630f863258a7800ea/hupper-1.10.3-py2.py3-none-any.whl",
  "https://files.pythonhosted.org/packages/ad/b9/260603ca0913072a10a4367c2dca9998706812a8c1f4558eca510f85ae16/httpcore-0.15.0-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/af/6d/ea3a5c3027c3f14b0321cd4f7e594c776ebe64e4b927432ca6917512a4f7/asgiref-3.5.2-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/c1/84/7bfe436fa6a4943eecb17c2cca9c84215299684575376d664ea6bf294439/janus-1.0.0-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/c3/22/4cba7e1b4f45ffbefd2ca817a6800ba1c671c26f288d7705f20289872012/anyio-3.6.1-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/c4/e5/63ca2c4edf4e00657584608bee1001302bbf8c5f569340b78304f2f446cb/rfc3986-1.5.0-py2.py3-none-any.whl",
  "https://files.pythonhosted.org/packages/ca/e4/b78d049f7cc7ed053ddbfdd59b2dcc7bd387458e2c2869b602975685d65e/aiofiles-0.8.0-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/e9/06/d3d367b7af6305b16f0d28ae2aaeb86154fa91f144f036c2d5002a5a202b/certifi-2022.6.15-py3-none-any.whl",
  "https://files.pythonhosted.org/packages/e9/fd/d8ff4bbf7ade1c9d60b53bf8234b44dcb2a9fcc7ae6933ae80ba38582f3e/httpx-0.23.0-py3-none-any.whl",
  "https://latest.datasette.io/-/static/app.css?cead5a",
  "https://latest.datasette.io/fixtures.db",
  "https://lite.datasette.io/",
  "https://lite.datasette.io/-/static/app.css?cead5a"
  "https://lite.datasette.io/favicon.ico",
  "https://lite.datasette.io/webworker.js",
  "https://plausible.io/api/event",
  "https://plausible.io/js/script.manual.js",
  "https://pypi.org/pypi/aiofiles/json",
  "https://pypi.org/pypi/anyio/json",
  "https://pypi.org/pypi/asgi-csrf/json",
  "https://pypi.org/pypi/asgiref/json",
  "https://pypi.org/pypi/certifi/json",
  "https://pypi.org/pypi/click-default-group-wheel/json",
  "https://pypi.org/pypi/click/json",
  "https://pypi.org/pypi/datasette/json",
  "https://pypi.org/pypi/h11/json",
  "https://pypi.org/pypi/httpcore/json",
  "https://pypi.org/pypi/httpx/json",
  "https://pypi.org/pypi/hupper/json",
  "https://pypi.org/pypi/idna/json",
  "https://pypi.org/pypi/itsdangerous/json",
  "https://pypi.org/pypi/janus/json",
  "https://pypi.org/pypi/jinja2/json",
  "https://pypi.org/pypi/mergedeep/json",
  "https://pypi.org/pypi/pint/json",
  "https://pypi.org/pypi/python-multipart/json",
  "https://pypi.org/pypi/rfc3986/json",
  "https://pypi.org/pypi/sniffio/json",
  "https://pypi.org/pypi/uvicorn/json",
[
]

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

Downloading all those wheels:

wget https://files.pythonhosted.org/packages/04/a2/d918dcd22354d8958fe113e1a3630137e0fc8b44859ade3063982eacd2a4/idna-3.3-py3-none-any.whl
wget https://files.pythonhosted.org/packages/09/73/06cd6609f6f01b2336f26654a1e62ca8d02a0fdb86ec1055904124d76992/asgi_csrf-0.9-py3-none-any.whl
wget https://files.pythonhosted.org/packages/0a/98/98b3c6d954b6bd8531c611fb24be53ccc66438f493b05ec4b107942e27b7/Pint-0.18-py2.py3-none-any.whl
wget https://files.pythonhosted.org/packages/0b/77/dbf4952b05efe08ab0ef4be14b6137717c00d0504f5a56ee6e80c010e6d0/click_default_group_wheel-1.2.2-py3-none-any.whl
wget https://files.pythonhosted.org/packages/12/d0/d998e0b558fd5808a4fdb48e906e6f57a4fda2177fa11ba2a9d16248ce92/uvicorn-0.18.2-py3-none-any.whl
wget https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl
wget https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
wget https://files.pythonhosted.org/packages/4b/5b/6fc4a322c8ae1349d3cecf83f8412ec10a67b06b7518dea686cfef594b07/python_multipart-0.0.4-py3-none-any.whl
wget https://files.pythonhosted.org/packages/50/0e/73f7c45526aebb98d4bf22b91da3e1347b3fd8ae9d338e2999f806d070f6/datasette-0.62-py3-none-any.whl
wget https://files.pythonhosted.org/packages/52/b0/7b2e028b63d092804b6794595871f936aafa5e9322dcaaad50ebf67445b3/sniffio-1.2.0-py3-none-any.whl
wget https://files.pythonhosted.org/packages/60/0f/7a0eeea938eaf61074f29fed9717f2010e8d0e0905d36b38d3275a1e4622/h11-0.12.0-py3-none-any.whl
wget https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl
wget https://files.pythonhosted.org/packages/9a/cb/d30896ad0d288358bbae60576556df78422179fc08c630f863258a7800ea/hupper-1.10.3-py2.py3-none-any.whl
wget https://files.pythonhosted.org/packages/ad/b9/260603ca0913072a10a4367c2dca9998706812a8c1f4558eca510f85ae16/httpcore-0.15.0-py3-none-any.whl
wget https://files.pythonhosted.org/packages/af/6d/ea3a5c3027c3f14b0321cd4f7e594c776ebe64e4b927432ca6917512a4f7/asgiref-3.5.2-py3-none-any.whl
wget https://files.pythonhosted.org/packages/c1/84/7bfe436fa6a4943eecb17c2cca9c84215299684575376d664ea6bf294439/janus-1.0.0-py3-none-any.whl
wget https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl
wget https://files.pythonhosted.org/packages/c3/22/4cba7e1b4f45ffbefd2ca817a6800ba1c671c26f288d7705f20289872012/anyio-3.6.1-py3-none-any.whl
wget https://files.pythonhosted.org/packages/c4/e5/63ca2c4edf4e00657584608bee1001302bbf8c5f569340b78304f2f446cb/rfc3986-1.5.0-py2.py3-none-any.whl
wget https://files.pythonhosted.org/packages/ca/e4/b78d049f7cc7ed053ddbfdd59b2dcc7bd387458e2c2869b602975685d65e/aiofiles-0.8.0-py3-none-any.whl
wget https://files.pythonhosted.org/packages/e9/06/d3d367b7af6305b16f0d28ae2aaeb86154fa91f144f036c2d5002a5a202b/certifi-2022.6.15-py3-none-any.whl
wget https://files.pythonhosted.org/packages/e9/fd/d8ff4bbf7ade1c9d60b53bf8234b44dcb2a9fcc7ae6933ae80ba38582f3e/httpx-0.23.0-py3-none-any.whl

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I tried this:

diff --git a/webworker.js b/webworker.js
index e27ff78..6abe837 100644
--- a/webworker.js
+++ b/webworker.js
@@ -50,9 +50,28 @@ async function startDatasette(settings) {
         names.append(name)
 
     import micropip
-    # Workaround for Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed
-    await micropip.install("h11==0.12.0")
-    await micropip.install("datasette")
+    await micropip.install("wheels/certifi-2022.6.15-py3-none-any.whl")
+    await micropip.install("wheels/python_multipart-0.0.4-py3-none-any.whl")
+    await micropip.install("wheels/itsdangerous-2.1.2-py3-none-any.whl")
+    await micropip.install("wheels/click-8.1.3-py3-none-any.whl")
+    await micropip.install("wheels/click_default_group_wheel-1.2.2-py3-none-any.whl")
+    await micropip.install("wheels/asgiref-3.5.2-py3-none-any.whl")
+    await micropip.install("wheels/h11-0.12.0-py3-none-any.whl")
+    await micropip.install("wheels/idna-3.3-py3-none-any.whl")
+    await micropip.install("wheels/sniffio-1.2.0-py3-none-any.whl")
+    await micropip.install("wheels/anyio-3.6.1-py3-none-any.whl")
+    await micropip.install("wheels/aiofiles-0.8.0-py3-none-any.whl")
+    await micropip.install("wheels/asgi_csrf-0.9-py3-none-any.whl")
+    await micropip.install("wheels/Pint-0.18-py2.py3-none-any.whl")
+    await micropip.install("wheels/uvicorn-0.18.2-py3-none-any.whl")
+    await micropip.install("wheels/Jinja2-3.0.3-py3-none-any.whl")
+    await micropip.install("wheels/mergedeep-1.3.4-py3-none-any.whl")
+    await micropip.install("wheels/hupper-1.10.3-py2.py3-none-any.whl")
+    await micropip.install("wheels/httpcore-0.15.0-py3-none-any.whl")
+    await micropip.install("wheels/janus-1.0.0-py3-none-any.whl")
+    await micropip.install("wheels/rfc3986-1.5.0-py2.py3-none-any.whl")
+    await micropip.install("wheels/httpx-0.23.0-py3-none-any.whl")
+    await micropip.install("wheels/datasette-0.62-py3-none-any.whl")
     # Install any extra ?install= dependencies
     install_urls = ${JSON.stringify(settings.installUrls)}
     if install_urls:

You have to get the order of installation exactly right, or one of the wheels will trigger another wheel to be loaded from PyPI.

I kept tweaking the order and watching the network pane in Firefox. The above diff almost gets it but there are still three wheels coming from PyPI:

image

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

Not sure how best to automate the process of figuring out those dependencies, grabbing the right wheel versions, caching them locally in the repo AND figuring out the right import order for them.

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

Just spotted this in the docs:

Micropip will also install the dependencies of the wheel. If dependency resolution is not desired, you may pass deps=False.

Looks like that's new in Pyodide 0.21.0 (I am on 0.20.0 right now): https://pyodide.org/en/stable/project/changelog.html#version-0-21-0

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

Upgrading to 0.21.0 and using deps=False almost worked but I got this error:

  File "/lib/python3.10/site-packages/datasette/utils/__init__.py", line 9, in <module>
    import markupsafe
ModuleNotFoundError: No module named 'markupsafe'

My guess is that markupsafe needs a C extension and hence the wheel has to be loaded from somewhere inside https://cdn.jsdelivr.net/pyodide/v0.21.0/full/

This looks like a useful way to look up files in there: https://cdn.jsdelivr.net/npm/pyodide@0.21.0/repodata.json

    "markupsafe": {
      "name": "MarkupSafe",
      "version": "2.1.1",
      "file_name": "MarkupSafe-2.1.1-cp310-cp310-emscripten_3_1_14_wasm32.whl",
      "install_dir": "site",
      "sha256": "c3cfbcc5f7927add3de4b3c698afe234730452cb0a2e566336b55ecdf16857c5",
      "depends": [],
      "imports": [
        "markupsafe"
      ]
    },

And sure enough this is a working URL: https://cdn.jsdelivr.net/pyodide/v0.21.0/full/MarkupSafe-2.1.1-cp310-cp310-emscripten_3_1_14_wasm32.whl

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

Same problem for:

  • PyYAML-6.0-cp310-cp310-emscripten_3_1_14_wasm32.whl
  • pluggy-1.0.0-py2.py3-none-any.whl
  • six-1.16.0-py2.py3-none-any.whl
  • and more

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I'm just going to grab all of these wheels:

  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/MarkupSafe-2.1.1-cp310-cp310-emscripten_wasm32.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/PyYAML-6.0-cp310-cp310-emscripten_wasm32.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/packaging-21.3-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pluggy-1.0.0-py2.py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyparsing-3.0.7-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/setuptools-62.0.0-py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/six-1.16.0-py2.py3-none-any.whl",
  "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/typing_extensions-4.1.1-py3-none-any.whl",

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

This loaded successfully (once I had the wheels/ folder populated):

diff --git a/webworker.js b/webworker.js
index e27ff78..cc9578e 100644
--- a/webworker.js
+++ b/webworker.js
@@ -1,4 +1,4 @@
-importScripts("https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js");
+importScripts("https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.js");
 
 function log(line) {
   console.log({line})
@@ -29,7 +29,7 @@ async function startDatasette(settings) {
     toLoad.push(["content.db", "https://datasette.io/content.db"]);
   }
   self.pyodide = await loadPyodide({
-    indexURL: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/"
+    indexURL: "https://cdn.jsdelivr.net/pyodide/v0.21.0/full/"
   });
   await pyodide.loadPackage('micropip', log);
   await pyodide.loadPackage('ssl', log);
@@ -50,9 +50,35 @@ async function startDatasette(settings) {
         names.append(name)
 
     import micropip
-    # Workaround for Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed
-    await micropip.install("h11==0.12.0")
-    await micropip.install("datasette")
+    await micropip.install("wheels/packaging-21.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/pyparsing-3.0.7-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/typing_extensions-4.1.1-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/six-1.16.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/MarkupSafe-2.1.1-cp310-cp310-emscripten_3_1_14_wasm32.whl", deps=False)
+    await micropip.install("wheels/PyYAML-6.0-cp310-cp310-emscripten_3_1_14_wasm32.whl", deps=False)
+    await micropip.install("wheels/pluggy-1.0.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/certifi-2022.6.15-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/python_multipart-0.0.4-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/itsdangerous-2.1.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/click-8.1.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/click_default_group_wheel-1.2.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/asgiref-3.5.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/h11-0.12.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/idna-3.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/sniffio-1.2.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/anyio-3.6.1-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/aiofiles-0.8.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/asgi_csrf-0.9-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/Pint-0.18-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/uvicorn-0.18.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/Jinja2-3.0.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/mergedeep-1.3.4-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/hupper-1.10.3-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/httpcore-0.15.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/janus-1.0.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/rfc3986-1.5.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/httpx-0.23.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/datasette-0.62-py3-none-any.whl", deps=False)
     # Install any extra ?install= dependencies
     install_urls = ${JSON.stringify(settings.installUrls)}
     if install_urls:

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I got it to work offline with my WiFi turned off!

I had to run this in the root directory:

wget https://latest.datasette.io/-/static/app.css
wget https://latest.datasette.io/fixtures.db

Then I had to grab the latest Pyodide release from https://github.com/pyodide/pyodide/releases/tag/0.21.0 - then uncompress it and move the pyodide folder into my datasette-lite folder.

Then I applied this diff and started the server and it worked:

diff --git a/index.html b/index.html
index de860bb..e2b1ede 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
 <html>
 <head>
   <title>Datasette</title>
-  <link rel="stylesheet" href="https://latest.datasette.io/-/static/app.css?cead5a">
+  <link rel="stylesheet" href="app.css">
 <style>
 #loading-indicator {
   text-align: center;
diff --git a/webworker.js b/webworker.js
index e27ff78..8b3a444 100644
--- a/webworker.js
+++ b/webworker.js
@@ -1,4 +1,4 @@
-importScripts("https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js");
+importScripts("/pyodide/pyodide.js");
 
 function log(line) {
   console.log({line})
@@ -25,11 +25,10 @@ async function startDatasette(settings) {
   if (needsDataDb) {
     toLoad.push(["data.db", 0]);
   } else {
-    toLoad.push(["fixtures.db", "https://latest.datasette.io/fixtures.db"]);
-    toLoad.push(["content.db", "https://datasette.io/content.db"]);
+    toLoad.push(["fixtures.db", "/fixtures.db"]);
   }
   self.pyodide = await loadPyodide({
-    indexURL: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/"
+    indexURL: "/pyodide/"
   });
   await pyodide.loadPackage('micropip', log);
   await pyodide.loadPackage('ssl', log);
@@ -50,9 +49,35 @@ async function startDatasette(settings) {
         names.append(name)
 
     import micropip
-    # Workaround for Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed
-    await micropip.install("h11==0.12.0")
-    await micropip.install("datasette")
+    await micropip.install("wheels/packaging-21.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/pyparsing-3.0.7-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/typing_extensions-4.1.1-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/six-1.16.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/MarkupSafe-2.1.1-cp310-cp310-emscripten_3_1_14_wasm32.whl", deps=False)
+    await micropip.install("wheels/PyYAML-6.0-cp310-cp310-emscripten_3_1_14_wasm32.whl", deps=False)
+    await micropip.install("wheels/pluggy-1.0.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/certifi-2022.6.15-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/python_multipart-0.0.4-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/itsdangerous-2.1.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/click-8.1.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/click_default_group_wheel-1.2.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/asgiref-3.5.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/h11-0.12.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/idna-3.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/sniffio-1.2.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/anyio-3.6.1-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/aiofiles-0.8.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/asgi_csrf-0.9-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/Pint-0.18-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/uvicorn-0.18.2-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/Jinja2-3.0.3-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/mergedeep-1.3.4-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/hupper-1.10.3-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/httpcore-0.15.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/janus-1.0.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/rfc3986-1.5.0-py2.py3-none-any.whl", deps=False)
+    await micropip.install("wheels/httpx-0.23.0-py3-none-any.whl", deps=False)
+    await micropip.install("wheels/datasette-0.62-py3-none-any.whl", deps=False)
     # Install any extra ?install= dependencies
     install_urls = ${JSON.stringify(settings.installUrls)}
     if install_urls:

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

One catch: the decompressed Pyodide folder is HUGE:

datasette-lite % du -h pyodide 
6.7M	pyodide/fonts
282M	pyodide

Because it has wheels for every Python wheel that Pyodide provides special WASM compiled versions of.

I only need a fraction of those. Maybe I don't need any of the wheels at all?

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I tried deleting the fonts and ALL of the .whl files and ALL of the files with test in the name (since they seemed to be .tar files with tests for things).

Got it down to 21M for the pyodide folder... but also broke it!

  File "/lib/python3.10/site-packages/_pyodide/_base.py", line 359, in run_async
    await coroutine
  File "<exec>", line 15, in <module>
ModuleNotFoundError: No module named 'micropip'

Evidently not every .whl should be deleted. Here are the 404s I'm seeing:

image

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I think the better way to approach this will be to create a pyodide folder with just the files I see Datasette Lite loading.

@simonw
Copy link
Owner Author

simonw commented Aug 15, 2022

I used shot-scraper with the following patch to capture a fresh list of all of the URLs.

diff --git a/shot_scraper/cli.py b/shot_scraper/cli.py
index 018581c..d3e2fc6 100644
--- a/shot_scraper/cli.py
+++ b/shot_scraper/cli.py
@@ -663,6 +663,7 @@ def take_shot(
 
     if not use_existing_page:
         page = context_or_page.new_page()
+        page.on("request", lambda request: print(">>", request.method, request.url))
     else:
         page = context_or_page
(shot-scraper) /tmp % shot-scraper https://lite.datasette.io/ --wait 10000
>> GET https://lite.datasette.io/
>> GET https://latest.datasette.io/-/static/app.css?cead5a
>> GET https://plausible.io/js/script.manual.js
>> GET https://lite.datasette.io/webworker.js
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.js
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide_py.tar
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.asm.js
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.asm.data
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/repodata.json
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyodide.asm.wasm
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/distutils.tar
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/micropip-0.1-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pyparsing-3.0.9-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/packaging-21.3-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/ssl-1.0.0.zip
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/openssl-1.1.1n.zip
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/setuptools-62.6.0-py3-none-any.whl
>> GET https://latest.datasette.io/fixtures.db
>> GET https://datasette.io/content.db
>> GET https://pypi.org/pypi/h11/json
>> GET https://files.pythonhosted.org/packages/60/0f/7a0eeea938eaf61074f29fed9717f2010e8d0e0905d36b38d3275a1e4622/h11-0.12.0-py3-none-any.whl
>> GET https://pypi.org/pypi/datasette/json
>> GET https://files.pythonhosted.org/packages/50/0e/73f7c45526aebb98d4bf22b91da3e1347b3fd8ae9d338e2999f806d070f6/datasette-0.62-py3-none-any.whl
>> GET https://pypi.org/pypi/janus/json
>> GET https://pypi.org/pypi/asgi-csrf/json
>> GET https://pypi.org/pypi/pint/json
>> GET https://pypi.org/pypi/hupper/json
>> GET https://pypi.org/pypi/jinja2/json
>> GET https://pypi.org/pypi/mergedeep/json
>> GET https://pypi.org/pypi/click-default-group-wheel/json
>> GET https://pypi.org/pypi/asgiref/json
>> GET https://pypi.org/pypi/httpx/json
>> GET https://pypi.org/pypi/click/json
>> GET https://pypi.org/pypi/uvicorn/json
>> GET https://pypi.org/pypi/aiofiles/json
>> GET https://pypi.org/pypi/itsdangerous/json
>> GET https://files.pythonhosted.org/packages/c1/84/7bfe436fa6a4943eecb17c2cca9c84215299684575376d664ea6bf294439/janus-1.0.0-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/0a/98/98b3c6d954b6bd8531c611fb24be53ccc66438f493b05ec4b107942e27b7/Pint-0.18-py2.py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/9a/cb/d30896ad0d288358bbae60576556df78422179fc08c630f863258a7800ea/hupper-1.10.3-py2.py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/09/73/06cd6609f6f01b2336f26654a1e62ca8d02a0fdb86ec1055904124d76992/asgi_csrf-0.9-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/12/d0/d998e0b558fd5808a4fdb48e906e6f57a4fda2177fa11ba2a9d16248ce92/uvicorn-0.18.2-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/0b/77/dbf4952b05efe08ab0ef4be14b6137717c00d0504f5a56ee6e80c010e6d0/click_default_group_wheel-1.2.2-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/af/6d/ea3a5c3027c3f14b0321cd4f7e594c776ebe64e4b927432ca6917512a4f7/asgiref-3.5.2-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/e9/fd/d8ff4bbf7ade1c9d60b53bf8234b44dcb2a9fcc7ae6933ae80ba38582f3e/httpx-0.23.0-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/ca/e4/b78d049f7cc7ed053ddbfdd59b2dcc7bd387458e2c2869b602975685d65e/aiofiles-0.8.0-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl
>> GET https://pypi.org/pypi/python-multipart/json
>> GET https://pypi.org/pypi/httpcore/json
>> GET https://pypi.org/pypi/sniffio/json
>> GET https://pypi.org/pypi/rfc3986/json
>> GET https://files.pythonhosted.org/packages/4b/5b/6fc4a322c8ae1349d3cecf83f8412ec10a67b06b7518dea686cfef594b07/python_multipart-0.0.4-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/ad/b9/260603ca0913072a10a4367c2dca9998706812a8c1f4558eca510f85ae16/httpcore-0.15.0-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/52/b0/7b2e028b63d092804b6794595871f936aafa5e9322dcaaad50ebf67445b3/sniffio-1.2.0-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/c4/e5/63ca2c4edf4e00657584608bee1001302bbf8c5f569340b78304f2f446cb/rfc3986-1.5.0-py2.py3-none-any.whl
>> GET https://pypi.org/pypi/anyio/json
>> GET https://pypi.org/pypi/idna/json
>> GET https://files.pythonhosted.org/packages/c3/22/4cba7e1b4f45ffbefd2ca817a6800ba1c671c26f288d7705f20289872012/anyio-3.6.1-py3-none-any.whl
>> GET https://files.pythonhosted.org/packages/04/a2/d918dcd22354d8958fe113e1a3630137e0fc8b44859ade3063982eacd2a4/idna-3.3-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/PyYAML-6.0-cp310-cp310-emscripten_3_1_14_wasm32.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/pluggy-1.0.0-py2.py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/typing_extensions-4.2.0-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/MarkupSafe-2.1.1-cp310-cp310-emscripten_3_1_14_wasm32.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/certifi-2022.6.15-py3-none-any.whl
>> GET https://cdn.jsdelivr.net/pyodide/v0.21.0/full/six-1.16.0-py2.py3-none-any.whl
Screenshot of 'https://lite.datasette.io/' written to 'lite-datasette-io.1.png'

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

I shipped that feature in shot-scraper. Now I can do this:

shot-scraper https://lite.datasette.io/ \
  --wait-for 'document.querySelector("h2")' \
  --log-requests - | \
  sqlite-utils memory stdin:nl 'select * from t' --flatten --dump | pbcopy

To generate this SQL file: https://gist.github.com/simonw/7f41a43ba0f177238ed7bdd95078a0d4

Which I can then open in Datasette Lite like this: https://lite.datasette.io/?sql=https://gist.githubusercontent.com/simonw/7f41a43ba0f177238ed7bdd95078a0d4/raw/4fc0f80decce4e1ea1e925cdc2bf3f05d73034ed/datasette-lite.sql#/data/stdin

And run this query to see just select url from t where url like '%.whl':

https://lite.datasette.io/?sql=https://gist.githubusercontent.com/simonw/7f41a43ba0f177238ed7bdd95078a0d4/raw/4fc0f80decce4e1ea1e925cdc2bf3f05d73034ed/datasette-lite.sql#/data?sql=select+url+from+t+where+url+like+%27%25.whl%27

Which returns:

https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyparsing-3.0.7-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/packaging-21.3-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/micropip-0.1-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/setuptools-62.0.0-py3-none-any.whl
https://files.pythonhosted.org/packages/60/0f/7a0eeea938eaf61074f29fed9717f2010e8d0e0905d36b38d3275a1e4622/h11-0.12.0-py3-none-any.whl
https://files.pythonhosted.org/packages/50/0e/73f7c45526aebb98d4bf22b91da3e1347b3fd8ae9d338e2999f806d070f6/datasette-0.62-py3-none-any.whl
https://files.pythonhosted.org/packages/e9/fd/d8ff4bbf7ade1c9d60b53bf8234b44dcb2a9fcc7ae6933ae80ba38582f3e/httpx-0.23.0-py3-none-any.whl
https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl
https://files.pythonhosted.org/packages/ad/b9/260603ca0913072a10a4367c2dca9998706812a8c1f4558eca510f85ae16/httpcore-0.15.0-py3-none-any.whl
https://files.pythonhosted.org/packages/c3/22/4cba7e1b4f45ffbefd2ca817a6800ba1c671c26f288d7705f20289872012/anyio-3.6.1-py3-none-any.whl
https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl
https://files.pythonhosted.org/packages/6a/34/cd29f4dd8a23ce45f2b8ce9631ff2d4205fb74eddb412a3dc4fd1e4aa800/certifi-2022.9.14-py3-none-any.whl
https://files.pythonhosted.org/packages/c4/e5/63ca2c4edf4e00657584608bee1001302bbf8c5f569340b78304f2f446cb/rfc3986-1.5.0-py2.py3-none-any.whl
https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl
https://files.pythonhosted.org/packages/64/82/3fdff66fca901b30e42c88e0c37ada35e181074e0c4fd8d7d7525107329d/uvicorn-0.18.3-py3-none-any.whl
https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl
https://files.pythonhosted.org/packages/9a/cb/d30896ad0d288358bbae60576556df78422179fc08c630f863258a7800ea/hupper-1.10.3-py2.py3-none-any.whl
https://files.pythonhosted.org/packages/0b/77/dbf4952b05efe08ab0ef4be14b6137717c00d0504f5a56ee6e80c010e6d0/click_default_group_wheel-1.2.2-py3-none-any.whl
https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl
https://files.pythonhosted.org/packages/68/5f/447e04e828f47465eeab35b5d408b7ebaaaee207f48b7136c5a7267a30ae/itsdangerous-2.1.2-py3-none-any.whl
https://files.pythonhosted.org/packages/0a/98/98b3c6d954b6bd8531c611fb24be53ccc66438f493b05ec4b107942e27b7/Pint-0.18-py2.py3-none-any.whl
https://files.pythonhosted.org/packages/af/6d/ea3a5c3027c3f14b0321cd4f7e594c776ebe64e4b927432ca6917512a4f7/asgiref-3.5.2-py3-none-any.whl
https://files.pythonhosted.org/packages/ca/e4/b78d049f7cc7ed053ddbfdd59b2dcc7bd387458e2c2869b602975685d65e/aiofiles-0.8.0-py3-none-any.whl
https://files.pythonhosted.org/packages/09/73/06cd6609f6f01b2336f26654a1e62ca8d02a0fdb86ec1055904124d76992/asgi_csrf-0.9-py3-none-any.whl
https://files.pythonhosted.org/packages/4b/5b/6fc4a322c8ae1349d3cecf83f8412ec10a67b06b7518dea686cfef594b07/python_multipart-0.0.4-py3-none-any.whl
https://files.pythonhosted.org/packages/c1/84/7bfe436fa6a4943eecb17c2cca9c84215299684575376d664ea6bf294439/janus-1.0.0-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/MarkupSafe-2.1.1-cp310-cp310-emscripten_wasm32.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/PyYAML-6.0-cp310-cp310-emscripten_wasm32.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/typing_extensions-4.1.1-py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/six-1.16.0-py2.py3-none-any.whl
https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pluggy-1.0.0-py2.py3-none-any.whl

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

I think the next step is to build a script which loops through those URLs and downloads them into the wheels/ directory, then figure out how to have that script run as part of the deployment to GitHub Pages: https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#creating-a-custom-github-actions-workflow-to-publish-your-site

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

Downloading all of those wheels produces a folder with 2.5M of total data in it. They don't compress well (since they are compressed already) - running gzip *.whl only drops that to 2.4M.

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

The size of those files:

wheels % ls -lh | awk '{print $5, "\t", $9}'
 	 
130K 	 Jinja2-3.0.3-py3-none-any.whl
12K 	 MarkupSafe-2.1.1-cp310-cp310-emscripten_wasm32.whl
205K 	 Pint-0.18-py2.py3-none-any.whl
120K 	 PyYAML-6.0-cp310-cp310-emscripten_wasm32.whl
14K 	 aiofiles-0.8.0-py3-none-any.whl
79K 	 anyio-3.6.1-py3-none-any.whl
10K 	 asgi_csrf-0.9-py3-none-any.whl
22K 	 asgiref-3.5.2-py3-none-any.whl
159K 	 certifi-2022.9.14-py3-none-any.whl
94K 	 click-8.1.3-py3-none-any.whl
3.9K 	 click_default_group_wheel-1.2.2-py3-none-any.whl
224K 	 datasette-0.62-py3-none-any.whl
54K 	 h11-0.12.0-py3-none-any.whl
67K 	 httpcore-0.15.0-py3-none-any.whl
83K 	 httpx-0.23.0-py3-none-any.whl
26K 	 hupper-1.10.3-py2.py3-none-any.whl
60K 	 idna-3.4-py3-none-any.whl
15K 	 itsdangerous-2.1.2-py3-none-any.whl
6.7K 	 janus-1.0.0-py3-none-any.whl
6.2K 	 mergedeep-1.3.4-py3-none-any.whl
16K 	 micropip-0.1-py3-none-any.whl
40K 	 packaging-21.3-py3-none-any.whl
13K 	 pluggy-1.0.0-py2.py3-none-any.whl
96K 	 pyparsing-3.0.7-py3-none-any.whl
31K 	 python_multipart-0.0.4-py3-none-any.whl
31K 	 rfc3986-1.5.0-py2.py3-none-any.whl
772K 	 setuptools-62.0.0-py3-none-any.whl
11K 	 six-1.16.0-py2.py3-none-any.whl
9.9K 	 sniffio-1.3.0-py3-none-any.whl
26K 	 typing_extensions-4.1.1-py3-none-any.whl
56K 	 uvicorn-0.18.3-py3-none-any.whl

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

Looking at those, it would be nice if I could get rid of:

205K 	 Pint-0.18-py2.py3-none-any.whl
159K 	 certifi-2022.9.14-py3-none-any.whl (since we don't do any TLS)
26K 	 hupper-1.10.3-py2.py3-none-any.whl (no restarts)
56K 	 uvicorn-0.18.3-py3-none-any.whl (we don't run a server)

Also setuptools is surprisingly big:

772K 	 setuptools-62.0.0-py3-none-any.whl

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

Honestly though, it's not really worth the effort right now - those four only add up to ~400KB. And if this all works out they will be cached for the long term by the service worker.

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

Loading https://lite.datasette.io/ a second time only transfers 22.55KB! Of 25.5MB total - but the vast majority of those assets are correctly cached already, which is nice.

@simonw
Copy link
Owner Author

simonw commented Sep 15, 2022

Here's a fun thing: I took a Firefox Profile of the initial load experience and uploaded it here: https://profiler.firefox.com/public/48aw3a07gj8s0y81f4wyqywa69q4csbmykbs550/calltree/?globalTrackOrder=xu0wxt&hiddenGlobalTracks=1wxs&hiddenLocalTracksByPid=1272-02w8~48832-0~76615-0~2594-0~15591-0~72954-0~8975-0~8852-0~773-0~73168-01~55239-0w5~7928-0&implementation=js&thread=ym&v=7

Then I deleted it because I wasn't sure if the profile I had uploaded would include sensitive data like cookies and suchlike.

@hydrosquall
Copy link

If we're willing to let the client fetch external resources for themselves on the first load, we can use the service-worker cache. I implemented one in my fork:

hydrosquall#6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request research
Projects
None yet
Development

No branches or pull requests

2 participants