diff --git a/datalayer_core/handlers/service_worker/__init__.py b/datalayer_core/handlers/service_worker/__init__.py new file mode 100644 index 0000000..3bcaab4 --- /dev/null +++ b/datalayer_core/handlers/service_worker/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2023-2024 Datalayer, Inc. +# +# Datalayer License + diff --git a/datalayer_core/handlers/service_worker/handler.py b/datalayer_core/handlers/service_worker/handler.py new file mode 100644 index 0000000..1a6a514 --- /dev/null +++ b/datalayer_core/handlers/service_worker/handler.py @@ -0,0 +1,39 @@ +# Copyright (c) 2023-2024 Datalayer, Inc. +# +# Datalayer License + +from pathlib import Path +from typing import Optional +from tornado import web + +from jupyter_server.base.handlers import JupyterHandler + + +class ServiceWorkerHandler(web.StaticFileHandler, JupyterHandler): + """Serve the service worker script.""" + + def initialize(self): + """Initialize the API spec handler.""" + # Must match the folder containing the service worker asset as specified + # in the webpack.lab-config.js + extensionStatic = Path(__file__).parent.parent.parent / "labextension" / "static" + super().initialize(path=str(extensionStatic)) + + def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]: + """Only allow to serve the service worker""" + # Must match the filename name set in webpack.lab-config.js + if Path(absolute_path).name != 'lite-service-worker.js': + raise web.HTTPError(404) + return super().validate_absolute_path(root, absolute_path) + + def get_content_type(self): + """Get the content type.""" + return "text/javascript" + + def set_extra_headers(self, path: str) -> None: + """Add extra headers to the response""" + # Allow a service worker to get a broader scope than + # its path. + # See https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#examples + # https://medium.com/dev-channel/two-http-headers-related-to-service-workers-you-never-may-have-heard-of-c8862f76cc60 + self.set_header("Service-Worker-Allowed", "/") diff --git a/datalayer_core/serverapplication.py b/datalayer_core/serverapplication.py index 41e2f94..487b02d 100644 --- a/datalayer_core/serverapplication.py +++ b/datalayer_core/serverapplication.py @@ -12,6 +12,7 @@ from datalayer_core.handlers.index.handler import IndexHandler from datalayer_core.handlers.config.handler import ConfigHandler from datalayer_core.handlers.login.handler import LoginHandler +from datalayer_core.handlers.service_worker.handler import ServiceWorkerHandler from datalayer_core.authn.state import get_server_port @@ -109,6 +110,7 @@ def initialize_handlers(self): ("datalayer", IndexHandler), (url_path_join("datalayer", "config"), ConfigHandler), (url_path_join("datalayer", "login"), LoginHandler), + (url_path_join("datalayer", "service-worker", r"([^/]+\.js)"), ServiceWorkerHandler), ] self.handlers.extend(handlers) diff --git a/package.json b/package.json index 55549e1..bbfa421 100644 --- a/package.json +++ b/package.json @@ -160,8 +160,8 @@ "@jupyterlab/theme-light-extension": "4.1.0", "@jupyterlab/translation": "4.1.0", "@jupyterlab/ui-components": "4.1.0", - "@jupyterlite/server": "0.2.2", - "@jupyterlite/server-extension": "0.2.2", + "@jupyterlite/server": "^0.4.0", + "@jupyterlite/server-extension": "^0.4.0", "@jupyter/ydoc": "1.1.1", "@lumino/algorithm": "2.0.1", "@lumino/application": "2.2.0", @@ -247,6 +247,18 @@ "bundled": true, "singleton": true }, + "@jupyterlite/contents": { + "bundled": true, + "singleton": true + }, + "@jupyterlite/kernel": { + "bundled": true, + "singleton": true + }, + "@jupyterlite/server": { + "bundled": true, + "singleton": true + }, "@primer/react": { "bundled": true, "singleton": true diff --git a/pyproject.toml b/pyproject.toml index 36011ac..e39b9d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ exclude = [".github", "binder", ".yarn"] [tool.hatch.build.targets.wheel] artifacts = [ "datalayer_core/static", + "datalayer_core/labextension/static/lite-service-worker.js", "datalayer_core/about/about.md", ]