sync with main

This commit is contained in:
Min RK
2025-08-06 21:18:21 -07:00
35 changed files with 4821 additions and 2318 deletions

View File

@@ -53,5 +53,12 @@ updates:
- "*-loader"
update-types:
- major
# group major bumps of jest-related dependencies
jsx-jest:
patterns:
- "*jest*"
- "*test*"
update-types:
- major
schedule:
interval: monthly

View File

@@ -16,7 +16,7 @@ ci:
repos:
# autoformat and lint Python code
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.12
rev: v0.12.7
hooks:
- id: ruff
types_or:
@@ -29,8 +29,8 @@ repos:
- jupyter
# Autoformat: markdown, yaml, javascript (see the file .prettierignore)
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.6.2
hooks:
- id: prettier
exclude: .*/templates/.*|docs/source/_static/rest-api.yml|docs/source/rbac/scope-table.md

View File

@@ -58,7 +58,6 @@ for administration of the Hub and its users.
- A Linux/Unix based system
- [Python](https://www.python.org/downloads/) 3.8 or greater
- [nodejs/npm](https://www.npmjs.com/)
- If you are using **`conda`**, the nodejs and npm dependencies will be installed for
you by conda.

View File

@@ -62,18 +62,19 @@ paths:
properties:
class:
type: string
description: The Python class currently active for JupyterHub
Authentication
description: The Python class currently active for
JupyterHub Authentication
version:
type: string
description: The version of the currently active Authenticator
description: The version of the currently active
Authenticator
spawner:
type: object
properties:
class:
type: string
description: The Python class currently active for spawning
single-user notebook servers
description: The Python class currently active for
spawning single-user notebook servers
version:
type: string
description: The version of the currently active Spawner
@@ -256,8 +257,8 @@ paths:
parameters:
- $ref: "#/components/parameters/userName"
requestBody:
description: Updated user info. At least one key to be updated (name or admin)
is required.
description: Updated user info. At least one key to be updated (name or
admin) is required.
content:
application/json:
schema:
@@ -265,12 +266,12 @@ paths:
properties:
name:
type: string
description: the new name (optional, if another key is updated i.e.
admin)
description: the new name (optional, if another key is updated
i.e. admin)
admin:
type: boolean
description: update admin (optional, if another key is updated i.e.
name)
description: update admin (optional, if another key is updated
i.e. name)
required: true
responses:
200:
@@ -286,8 +287,8 @@ paths:
post:
operationId: post-user-activity
summary: Notify Hub of activity for a given user
description: Notify the Hub of activity by the user, e.g. accessing a service
or (more likely) actively using a server.
description: Notify the Hub of activity by the user, e.g. accessing a
service or (more likely) actively using a server.
parameters:
- $ref: "#/components/parameters/userName"
requestBody:
@@ -366,8 +367,8 @@ paths:
description: The user's notebook server has started
content: {}
202:
description: The user's notebook server has not yet started, but has been
requested
description: The user's notebook server has not yet started, but has
been requested
content: {}
security:
- oauth2:
@@ -380,8 +381,8 @@ paths:
- $ref: "#/components/parameters/userName"
responses:
202:
description: The user's notebook server has not yet stopped as it is taking
a while to stop
description: The user's notebook server has not yet stopped as it is
taking a while to stop
content: {}
204:
description: The user's notebook server has stopped
@@ -412,8 +413,8 @@ paths:
description: The user's notebook named-server has started
content: {}
202:
description: The user's notebook named-server has not yet started, but has
been requested
description: The user's notebook named-server has not yet started, but
has been requested
content: {}
security:
- oauth2:
@@ -448,8 +449,8 @@ paths:
required: false
responses:
202:
description: The user's notebook named-server has not yet stopped as it
is taking a while to stop
description: The user's notebook named-server has not yet stopped as
it is taking a while to stop
content: {}
204:
description: The user's notebook named-server has stopped
@@ -462,8 +463,8 @@ paths:
get:
operationId: get-user-shared
summary: List servers shared with user
description: Returns list of Shares granting the user access to servers owned
by others (new in 5.0)
description: Returns list of Shares granting the user access to servers
owned by others (new in 5.0)
parameters:
- $ref: "#/components/parameters/userName"
@@ -576,11 +577,13 @@ paths:
expires_in:
type: number
example: 3600
description: lifetime (in seconds) after which the requested token
will expire. Omit, or specify null or 0 for no expiration.
description: lifetime (in seconds) after which the requested
token will expire. Omit, or specify null or 0 for no
expiration.
note:
type: string
description: A note attached to the token for future bookkeeping
description: A note attached to the token for future
bookkeeping
roles:
type: array
description: |
@@ -758,7 +761,8 @@ paths:
- $ref: "#/components/parameters/sharedServerName"
responses:
200:
description: The permissions granted to members of `group` on `owner/server`
description: The permissions granted to members of `group` on
`owner/server`
content:
application/json:
schema:
@@ -1173,7 +1177,8 @@ paths:
description: |
The full URL for accepting the code,
if JupyterHub.public_url configuration is defined.
example: https://hub.example.org/hub/accept-share?code=abc123
example:
https://hub.example.org/hub/accept-share?code=abc123
security:
- oauth2:
- shares
@@ -1250,8 +1255,8 @@ paths:
get:
operationId: get-proxy
summary: Get the proxy's routing table
description: A convenience alias for getting the routing table directly from
the proxy
description: A convenience alias for getting the routing table directly
from the proxy
parameters:
- $ref: "#/components/parameters/paginationOffset"
- $ref: "#/components/parameters/paginationLimit"
@@ -1262,8 +1267,8 @@ paths:
application/json:
schema:
type: object
description: configurable-http-proxy routing table (see configurable-http-proxy
docs for details)
description: configurable-http-proxy routing table (see
configurable-http-proxy docs for details)
security:
- oauth2:
- proxy
@@ -1282,8 +1287,8 @@ paths:
summary: Notify the Hub about a new proxy
description: Notifies the Hub of a new proxy to use.
requestBody:
description: Any values that have changed for the new proxy. All keys are
optional.
description: Any values that have changed for the new proxy. All keys
are optional.
content:
application/json:
schema:
@@ -1374,8 +1379,8 @@ paths:
get:
operationId: get-auth-cookie
summary: Identify a user from a cookie
description: Used by single-user notebook servers to hand off cookie authentication
to the Hub
description: Used by single-user notebook servers to hand off cookie
authentication to the Hub
parameters:
- name: cookie_name
in: path
@@ -1499,12 +1504,12 @@ paths:
properties:
proxy:
type: boolean
description: Whether the proxy should be shutdown as well (default
from Hub config)
description: Whether the proxy should be shutdown as well
(default from Hub config)
servers:
type: boolean
description: Whether users' notebook servers should be shutdown
as well (default from Hub config)
description: Whether users' notebook servers should be
shutdown as well (default from Hub config)
required: false
responses:
202:
@@ -1648,8 +1653,8 @@ components:
type: string
server:
type: string
description: The user's notebook server's base URL, if running; null if
not.
description: The user's notebook server's base URL, if running; null
if not.
pending:
type: string
description: The currently pending action, if any
@@ -1680,8 +1685,8 @@ components:
properties:
name:
type: string
description: The server's name. The user's default server has an empty name
('')
description: The server's name. The user's default server has an empty
name ('')
ready:
type: boolean
description: |
@@ -1743,14 +1748,14 @@ components:
state:
type: object
properties: {}
description: Arbitrary internal state from this server's spawner. Only available
on the hub's users list or get-user-by-name method, and only with admin:users:server_state
scope. None otherwise.
description: Arbitrary internal state from this server's spawner. Only
available on the hub's users list or get-user-by-name method, and
only with admin:users:server_state scope. None otherwise.
user_options:
type: object
properties: {}
description: User specified options for the user's spawned instance of a
single-user server.
description: User specified options for the user's spawned instance of
a single-user server.
RequestIdentity:
description: |
The model for the entity making the request.
@@ -1918,8 +1923,8 @@ components:
items:
type: string
group:
description: the group being shared with (exactly one of 'user' or 'group'
will be non-null, the other will be null)
description: the group being shared with (exactly one of 'user' or
'group' will be non-null, the other will be null)
type:
- object
- "null"
@@ -1927,8 +1932,8 @@ components:
name:
type: string
user:
description: the user being shared with (exactly one of 'user' or 'group'
will be non-null, the other will be null)
description: the user being shared with (exactly one of 'user' or
'group' will be non-null, the other will be null)
type:
- object
- "null"
@@ -1941,8 +1946,8 @@ components:
format: date-time
ShareCode:
description: A single sharing code. There is at most one of these objects per
(server, user) or (server, group) combination.
description: A single sharing code. There is at most one of these objects
per (server, user) or (server, group) combination.
type: object
properties:
server:
@@ -1977,37 +1982,41 @@ components:
properties:
id:
type: string
description: The id of the API token. Used for modifying or deleting the
token.
description: The id of the API token. Used for modifying or deleting
the token.
user:
type: string
description: The user that owns a token (undefined if owned by a service)
description: The user that owns a token (undefined if owned by a
service)
service:
type: string
description: The service that owns the token (undefined of owned by a user)
description: The service that owns the token (undefined of owned by a
user)
roles:
type: array
description: Deprecated in JupyterHub 3, always an empty list. Tokens have
'scopes' starting from JupyterHub 3.
description: Deprecated in JupyterHub 3, always an empty list. Tokens
have 'scopes' starting from JupyterHub 3.
items:
type: string
scopes:
type: array
description: List of scopes this token has been assigned. New in JupyterHub
3. In JupyterHub 2.x, tokens were assigned 'roles' instead of scopes.
description: List of scopes this token has been assigned. New in
JupyterHub 3. In JupyterHub 2.x, tokens were assigned 'roles'
instead of scopes.
items:
type: string
note:
type: string
description: A note about the token, typically describing what it was created
for.
description: A note about the token, typically describing what it was
created for.
created:
type: string
description: Timestamp when this token was created
format: date-time
expires_at:
type: string
description: Timestamp when this token expires. Null if there is no expiry.
description: Timestamp when this token expires. Null if there is no
expiry.
format: date-time
last_activity:
type: string
@@ -2030,41 +2039,45 @@ components:
properties:
token:
type: string
description: The token itself. Only present in responses to requests for
a new token.
description: The token itself. Only present in responses to requests
for a new token.
id:
type: string
description: The id of the API token. Used for modifying or deleting the
token.
description: The id of the API token. Used for modifying or deleting
the token.
user:
type: string
description: The user that owns a token (undefined if owned by a service)
description: The user that owns a token (undefined if owned by a
service)
service:
type: string
description: The service that owns the token (undefined of owned by a user)
description: The service that owns the token (undefined of owned by a
user)
roles:
type: array
description: Deprecated in JupyterHub 3, always an empty list. Tokens have
'scopes' starting from JupyterHub 3.
description: Deprecated in JupyterHub 3, always an empty list. Tokens
have 'scopes' starting from JupyterHub 3.
items:
type: string
scopes:
type: array
description: List of scopes this token has been assigned. New in JupyterHub
3. In JupyterHub 2.x, tokens were assigned 'roles' instead of scopes.
description: List of scopes this token has been assigned. New in
JupyterHub 3. In JupyterHub 2.x, tokens were assigned 'roles'
instead of scopes.
items:
type: string
note:
type: string
description: A note about the token, typically describing what it was created
for.
description: A note about the token, typically describing what it was
created for.
created:
type: string
description: Timestamp when this token was created
format: date-time
expires_at:
type: string
description: Timestamp when this token expires. Null if there is no expiry.
description: Timestamp when this token expires. Null if there is no
expiry.
format: date-time
last_activity:
type: string
@@ -2094,22 +2107,23 @@ components:
tokenUrl: /hub/api/oauth2/token
scopes:
(no_scope): Identify the owner of the requesting entity.
self: The users own resources _(metascope for users, resolves to (no_scope)
for services)_
inherit: Everything that the token-owning entity can access _(metascope
for tokens)_
admin-ui: Access the admin page. Permission to take actions via the admin
page granted separately.
admin:users: Read, modify, create, and delete users and their authentication
state, not including their servers or tokens. This is an extremely privileged
scope and should be considered tantamount to superuser.
self: The users own resources _(metascope for users, resolves to
(no_scope) for services)_
inherit: Everything that the token-owning entity can access
_(metascope for tokens)_
admin-ui: Access the admin page. Permission to take actions via the
admin page granted separately.
admin:users: Read, modify, create, and delete users and their
authentication state, not including their servers or tokens. This
is an extremely privileged scope and should be considered
tantamount to superuser.
admin:auth_state: Read a users authentication state.
users: Read and write permissions to user models (excluding servers, tokens
and authentication state).
users: Read and write permissions to user models (excluding servers,
tokens and authentication state).
delete:users: Delete users.
list:users: List users, including at least their names.
read:users: Read user models (including the URL of the default server
if it is running).
read:users: Read user models (including the URL of the default
server if it is running).
read:users:name: Read names of users.
read:users:groups: Read users group membership.
read:users:activity: Read time of last user activity.
@@ -2118,24 +2132,25 @@ components:
read:roles:services: Read service role assignments.
read:roles:groups: Read group role assignments.
users:activity: Update time of last user activity.
admin:servers: Read, start, stop, create and delete user servers and their
state.
admin:servers: Read, start, stop, create and delete user servers and
their state.
admin:server_state: Read and write users server state.
servers: Start and stop user servers.
read:servers: Read users names and their server models (excluding the
server state).
read:servers: Read users names and their server models (excluding
the server state).
delete:servers: Stop and delete users' servers.
tokens: Read, write, create and delete user tokens.
read:tokens: Read user tokens.
admin:groups: Read and write group information, create and delete groups.
admin:groups: Read and write group information, create and delete
groups.
groups: 'Read and write group information, including adding/removing any
users to/from groups. Note: adding users to groups may affect permissions.'
list:groups: List groups, including at least their names.
read:groups: Read group models.
read:groups:name: Read group names.
delete:groups: Delete groups.
admin:services: Create, read, update, delete services, not including services
defined from config files.
admin:services: Create, read, update, delete services, not including
services defined from config files.
list:services: List services, including at least their names.
read:services: Read service models.
read:services:name: Read service names.
@@ -2148,7 +2163,7 @@ components:
read:groups:shares: Read servers shared with a group.
read:shares: Read information about shared access to servers.
shares: Manage access to shared servers.
proxy: Read information about the proxys routing table, sync the Hub
with the proxy and notify the Hub about a new proxy.
proxy: Read information about the proxys routing table, sync the
Hub with the proxy and notify the Hub about a new proxy.
shutdown: Shutdown the hub.
read:metrics: Read prometheus metrics.

View File

@@ -262,6 +262,7 @@ html_static_path = ["_static"]
html_theme = "jupyterhub_sphinx_theme"
html_theme_options = {
"header_links_before_dropdown": 6,
"icon_links": [
{
"name": "GitHub",
@@ -297,7 +298,10 @@ linkcheck_ignore = [
r"https://github.com/jupyterhub/jupyterhub/security/advisories/.*",
# Occasionally blocks CI checks with 403
r"https://www\.mysql\.com",
# Occasionally blocks CI checks with SSL error
r"https://mediaspace\.msu\.edu/.*",
]
linkcheck_anchors_ignore = [
"/#!",
"/#%21",

View File

@@ -12,9 +12,9 @@ Everyone in the Jupyter community is welcome to bring ideas and questions there.
We recommend that you first use our Discourse as all past and current discussions on it are archived and searchable. Thus, all discussions remain useful and accessible to the whole community.
## Gitter
## Zulip
We use [our Gitter channel](https://gitter.im/jupyterhub/jupyterhub) for online, real-time text chat; a place for more ephemeral discussions. When you're not on Discourse, you can stop here to have other discussions on the fly.
We use [Jupyter instance](https://jupyter.zulipchat.com/) of [Zulip](https://zulip.com/) for online, real-time text chat; a place for more ephemeral discussions. When you're not on Discourse, you can stop at Zulip to have other discussions on the fly.
## Github Issues

View File

@@ -8,7 +8,7 @@ you get set up on how to contribute to JupyterHub's documentation.
## Building documentation locally
We use [sphinx](https://www.sphinx-doc.org) to build our documentation. It takes
our documentation source files (written in [markdown](https://daringfireball.net/projects/markdown/) or [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) &
our documentation source files (written in [markdown](https://daringfireball.net/projects/markdown/) or [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) and
stored under the `docs/source` directory) and converts it into various
formats for people to read. To make sure the documentation you write or
change renders correctly, it is good practice to test it locally.

View File

@@ -3,7 +3,7 @@
# Contributing
We want you to contribute to JupyterHub in ways that are most exciting
and useful to you. We value documentation, testing, bug reporting & code equally,
and useful to you. We value documentation, testing, bug reporting and code equally,
and are glad to have your contributions in whatever form you wish.
Be sure to first check our [Code of Conduct](https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md)

View File

@@ -5,34 +5,34 @@
## System requirements
JupyterHub can only run on macOS or Linux operating systems. If you are
using Windows, we recommend using [VirtualBox](https://virtualbox.org)
using Windows, we recommend using [VirtualBox](https://www.virtualbox.org)
or a similar system to run [Ubuntu Linux](https://ubuntu.com) for
development.
### Install Python
JupyterHub is written in the [Python](https://python.org) programming language and
JupyterHub is written in the [Python](https://www.python.org) programming language and
requires you have at least version {{python_min}} installed locally. If you havent
installed Python before, the recommended way to install it is to use
[Miniforge](https://github.com/conda-forge/miniforge#download).
### Install nodejs
### Install NodeJS
[NodeJS {{node_min}}+](https://nodejs.org/en/) is required for building some JavaScript components.
`configurable-http-proxy`, the default proxy implementation for JupyterHub, is written in Javascript.
Some JavaScript components require you have at least version {{node_min}} of [NodeJS](https://nodejs.org/en/) installed locally.
`configurable-http-proxy`, the default proxy implementation for JupyterHub, is written in JavaScript.
If you have not installed NodeJS before, we recommend installing it in the `miniconda` environment you set up for Python.
You can do so with `conda install nodejs`.
Many in the Jupyter community use [`nvm`](https://github.com/nvm-sh/nvm) to
managing node dependencies.
### Install git
### Install Git
JupyterHub uses [Git](https://git-scm.com) & [GitHub](https://github.com)
for development & collaboration. You need to [install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to work on
JupyterHub. We also recommend getting a free account on GitHub.com.
JupyterHub uses [Git](https://git-scm.com) and [GitHub](https://github.com)
for development and collaboration. You need to [install Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to work on
JupyterHub. We also recommend getting a free account on GitHub.
## Setting up a development install
## Install JupyterHub for development
When developing JupyterHub, you would need to make changes and be able to instantly view the results of the changes. To achieve that, a developer install is required.
@@ -44,7 +44,7 @@ be achieved in many ways, for example, `tox`, `conda`, `docker`, etc. See this
a more detailed discussion.
:::
1. Clone the [JupyterHub git repository](https://github.com/jupyterhub/jupyterhub)
1. Clone the [JupyterHub Git repository](https://github.com/jupyterhub/jupyterhub)
to your computer.
```bash
@@ -65,7 +65,7 @@ a more detailed discussion.
npm -v
```
This should return a version number greater than or equal to 5.0.
This should return a version number greater than or equal to {{node_min}}.
3. Install `configurable-http-proxy` (required to run and test the default JupyterHub configuration):
@@ -92,7 +92,7 @@ a more detailed discussion.
4. Install an editable version of JupyterHub and its requirements for
development and testing. This lets you edit JupyterHub code in a text editor
& restart the JupyterHub process to see your code changes immediately.
and restart the JupyterHub process to see your code changes immediately.
```bash
python3 -m pip install --editable ".[test]"
@@ -109,7 +109,7 @@ a more detailed discussion.
Happy developing!
## Using DummyAuthenticator & SimpleLocalProcessSpawner
## Using DummyAuthenticator and SimpleLocalProcessSpawner
To simplify testing of JupyterHub, it is helpful to use
{class}`~jupyterhub.auth.DummyAuthenticator` instead of the default JupyterHub
@@ -132,17 +132,17 @@ The test configuration enables a few things to make testing easier:
- disable caching of static files
The default JupyterHub [authenticator](PAMAuthenticator)
& [spawner](LocalProcessSpawner)
and [spawner](LocalProcessSpawner)
require your system to have user accounts for each user you want to log in to
JupyterHub as.
DummyAuthenticator allows you to log in with any username & password,
DummyAuthenticator allows you to log in with any username and password,
while SimpleLocalProcessSpawner allows you to start servers without having to
create a Unix user for each JupyterHub user. Together, these make it
much easier to test JupyterHub.
Tip: If you are working on parts of JupyterHub that are common to all
authenticators & spawners, we recommend using both DummyAuthenticator &
authenticators and spawners, we recommend using both DummyAuthenticator and
SimpleLocalProcessSpawner. If you are working on just authenticator-related
parts, use only SimpleLocalProcessSpawner. Similarly, if you are working on
just spawner-related parts, use only DummyAuthenticator.

View File

@@ -98,7 +98,7 @@ the OAuth callback request.
to retrieve information about the owner of the token (the user).
This is the step where behavior diverges for different OAuth providers.
Up to this point, all OAuth providers are the same, following the OAuth specification.
However, OAuth does not define a standard for issuing tokens in exchange for information about their owner or permissions ([OpenID Connect](https://openid.net/connect/) does that),
However, OAuth does not define a standard for issuing tokens in exchange for information about their owner or permissions ([OpenID Connect](https://openid.net/developers/how-connect-works/) does that),
so this step may be different for each OAuth provider.
- Finally, the OAuth client stores its own record that the user is authorized in a cookie.
This could be the token itself, or any other appropriate representation of successful authentication.

View File

@@ -101,7 +101,7 @@ matching `*.jupyter.example.org`.
Unfortunately, for many institutional domains, wildcard DNS and SSL may not be available.
We also **strongly encourage** serving JupyterHub and user content on a domain that is _not_ a subdomain of any sensitive content.
For reasoning, see [GitHub's discussion of moving user content to github.io from \*.github.com](https://github.blog/2013-04-09-yummy-cookies-across-domains/).
For reasoning, see [GitHub's discussion of moving user content to github.io from \*.github.com](https://github.blog/engineering/yummy-cookies-across-domains/).
**If you do plan to serve untrusted users, enabling subdomains is highly encouraged**,
as it resolves many security issues, which are difficult to unavoidable when JupyterHub is on a single-domain.
@@ -186,7 +186,6 @@ For example:
- `Content-Security-Policy` header must prohibit popups and iframes from the same origin.
The following Content-Security-Policy rules are _insecure_ and readily enable users to access each others' servers:
- `frame-ancestors: 'self'`
- `frame-ancestors: '*'`
- `sandbox allow-popups`

View File

@@ -142,7 +142,7 @@ in a variety of deployment setups. This often entails connecting your JupyterHub
in these cases, and the security of your JupyterHub deployment will often depend on these decisions.
If you are worried about security, don't hesitate to reach out to the JupyterHub community in the
[Jupyter Community Forum](https://discourse.jupyter.org/c/jupyterhub). This community of practice has many
[Jupyter Community Forum](https://discourse.jupyter.org/c/jupyterhub/10). This community of practice has many
individuals with experience running secure JupyterHub deployments and will be very glad to help you out.
### Does JupyterHub provide computing or data infrastructure?

View File

@@ -35,7 +35,7 @@ This user shouldn't have a login shell or password (possible with -r).
## Set up sudospawner
Next, you will need [sudospawner](https://github.com/jupyter/sudospawner)
Next, you will need [sudospawner](https://github.com/jupyterhub/sudospawner)
to enable monitoring the single-user servers with sudo:
```bash
@@ -72,7 +72,7 @@ rhea ALL=(JUPYTER_USERS) NOPASSWD:JUPYTER_CMD
```
It might be useful to modify `secure_path` to add commands in path. (Search for
`secure_path` in the [sudo docs](https://www.sudo.ws/man/1.8.14/sudoers.man.html)
`secure_path` in the [sudo docs](https://www.sudo.ws)
As an alternative to adding every user to the `/etc/sudoers` file, you can
use a group in the last line above, instead of `JUPYTER_USERS`:

View File

@@ -71,4 +71,4 @@ aligned, rather than as an indicator of an existing problem.
Upgrade the version of the `jupyterhub` package in your user environment or image
so that it matches the version of JupyterHub running your JupyterHub server! If you
are using the [zero-to-jupyterhub](https://z2jh.jupyter.org) helm chart, you can find the appropriate
version of the `jupyterhub` package to install in your user image [here](https://jupyterhub.github.io/helm-chart/)
version of the `jupyterhub` package to install in your user image [here](https://hub.jupyter.org/helm-chart/)

View File

@@ -232,4 +232,4 @@ A list of the proxies that are currently available for JupyterHub (that we know
1. [`jupyterhub/configurable-http-proxy`](https://github.com/jupyterhub/configurable-http-proxy) The default proxy which uses node-http-proxy
2. [`jupyterhub/traefik-proxy`](https://github.com/jupyterhub/traefik-proxy) The proxy which configures traefik proxy server for jupyterhub
3. [`AbdealiJK/configurable-http-proxy`](https://github.com/AbdealiJK/configurable-http-proxy) A pure python implementation of the configurable-http-proxy
3. [`AbdealiJK/configurable-http-proxy`](https://github.com/corridor/configurable-http-proxy) A pure python implementation of the configurable-http-proxy

View File

@@ -201,7 +201,7 @@ Authorization header.
### Use requests
Using the popular Python [requests](https://docs.python-requests.org)
Using the popular Python [requests](https://requests.readthedocs.io)
library, an API GET request is made to [/users](rest-api-get-users), and the request sends an API token for
authorization. The response contains information about the users, here's example code to make an API request for the users of a JupyterHub deployment

View File

@@ -27,7 +27,7 @@ For specific version migrations:
The [changelog](changelog) contains information on what has
changed with the new JupyterHub release and any deprecation warnings.
Read these notes to familiarize yourself with the coming changes. There
might be new releases of the authenticators & spawners you use, so
might be new releases of the authenticators and spawners you use, so
read the changelogs for those too!
## Notify your users
@@ -41,7 +41,7 @@ If you use a different proxy or run `configurable-http-proxy`
independent of JupyterHub, your users will be able to continue using notebook
servers they had already launched, but will not be able to launch new servers or sign in.
## Backup database & config
## Backup database and config
Before doing an upgrade, it is critical to back up:
@@ -90,7 +90,7 @@ with:
conda install -c conda-forge jupyterhub==<version>
```
You should also check for new releases of the authenticator & spawner you
You should also check for new releases of the authenticator and spawner you
are using. You might wish to upgrade those packages, too, along with JupyterHub
or upgrade them separately.

View File

@@ -17,7 +17,7 @@ It has two main distributions which are developed to serve the needs of each of
1. [The Littlest JupyterHub](https://github.com/jupyterhub/the-littlest-jupyterhub) distribution is suitable if you need a small number of users (1-100) and a single server with a simple environment.
2. [Zero to JupyterHub with Kubernetes](https://github.com/jupyterhub/zero-to-jupyterhub-k8s) allows you to deploy dynamic servers on the cloud if you need even more users.
This distribution runs JupyterHub on top of [Kubernetes](https://k8s.io).
This distribution runs JupyterHub on top of [Kubernetes](https://kubernetes.io/).
```{note}
It is important to evaluate these distributions before you can continue with the

View File

@@ -84,7 +84,6 @@ The passed scopes are compared to the scopes required to access the API as follo
- if the API scopes are present within the set of passed scopes, the access is granted and the API returns its "full" response
- if that is not the case, another check is utilized to determine if subscopes of the required API scopes can be found in the passed scope set:
- if found, the RBAC framework employs the {ref}`filtering <vertical-filtering-target>` procedures to refine the API response to access only resource attributes corresponding to the passed scopes. For example, providing a scope `read:users:activity!group=class-C` for the `GET /users` API will return a list of user models from group `class-C` containing only the `last_activity` attribute for each user model
- if not found, the access to API is denied

View File

@@ -1831,7 +1831,7 @@ Highlights:
- More configuration of page templates and service display
- Pagination of the admin page improving performance with large numbers of users
- Improved control of user redirect
- Support for [jupyter-server](https://jupyter-server.readthedocs.io/en/latest/)-based single-user servers, such as [Voilà](https://voila-gallery.org) and latest JupyterLab.
- Support for [jupyter-server](https://jupyter-server.readthedocs.io/en/latest/)-based single-user servers, such as [Voilà](https://voila.readthedocs.io) and latest JupyterLab.
- Lots more improvements to documentation, HTML pages, and customizations
([full changelog](https://github.com/jupyterhub/jupyterhub/compare/1.1.0...1.2.0))

View File

@@ -16,17 +16,13 @@ Please submit pull requests to update information or to add new institutions or
- [BIDS - Berkeley Institute for Data Science](https://bids.berkeley.edu/)
- [Data 8](http://data8.org/)
- [Data 8](https://www.data8.org/)
- [GitHub organization](https://github.com/data-8)
- [NERSC](https://www.nersc.gov/)
- [Press release on Jupyter and Cori](https://www.nersc.gov/news-publications/nersc-news/nersc-center-news/2016/jupyter-notebooks-will-open-up-new-possibilities-on-nerscs-cori-supercomputer/)
- [Moving and sharing data](https://www.nersc.gov/assets/Uploads/03-MovingAndSharingData-Cholia.pdf)
- [Research IT](https://research-it.berkeley.edu)
- [JupyterHub server supports campus research computation](https://research-it.berkeley.edu/blog/17/01/24/free-fully-loaded-jupyterhub-server-supports-campus-research-computation)
- [JupyterHub server supports campus research computation](https://research-it.berkeley.edu/news/free-fully-loaded-jupyterhub-server-supports-campus-research-computation)
### University of California Davis
@@ -86,7 +82,7 @@ Within CERN, there are two noteworthy JupyterHub deployments in operation:
[ETH Zurich](https://ethz.ch/en.html), (Federal Institute of Technology Zurich), is a public research university in Zürich, Switzerland, with focus on science, technology, engineering, and mathematics, although its 16 departments span a variety of disciplines and subjects.
The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/organisation/departments/educational-development-and-technology.html) unit provides JupyterHub exclusively for teaching and learning, integrated in the learning management system [Moodle](https://ethz.ch/staffnet/en/teaching/academic-support/it-services-teaching/teaching-applications/moodle-service.html). Each course gets its individually configured JupyterHub environment deployed on a on-premise Kubernetes cluster.
The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/organisation/departments/teaching-and-learning.html) unit provides JupyterHub exclusively for teaching and learning, integrated in the learning management system [Moodle](https://ethz.ch/staffnet/en/teaching/academic-support/it-services-teaching/teaching-applications/moodle-service.html). Each course gets its individually configured JupyterHub environment deployed on a on-premise Kubernetes cluster.
- [ETH JupyterHub](https://ethz.ch/staffnet/en/teaching/academic-support/it-services-teaching/teaching-applications/jupyterhub.html) for teaching and learning
@@ -125,16 +121,15 @@ The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/o
### Paderborn University
- [Data Science (DICE) group](https://dice-research.org)
- [nbgraderutils](https://github.com/dice-group/nbgraderutils): Use JupyterHub + nbgrader + iJava kernel for online Java exercises. Used in lecture Statistical Natural Language Processing.
- [JavaOnlineExercises](https://github.com/dice-group/JavaOnlineExercises): Use JupyterHub + nbgrader + iJava kernel for online Java exercises. Used in lecture Statistical Natural Language Processing.
### Penn State University
- [Press release](https://news.psu.edu/story/523093/2018/05/24/new-open-source-web-apps-available-students-and-faculty): "New open-source web apps available for students and faculty"
- [Press release](https://www.psu.edu/news/academics/story/new-open-source-web-apps-available-students-and-faculty): "New open-source web apps available for students and faculty"
### University of California San Diego
- San Diego Supercomputer Center - Andrea Zonca
- [Deploy JupyterHub on a Supercomputer with SSH](https://zonca.github.io/2017/05/jupyterhub-hpc-batchspawner-ssh.html)
- [Run Jupyterhub on a Supercomputer](https://zonca.github.io/2015/04/jupyterhub-hpc.html)
- [Deploy JupyterHub on a VM for a Workshop](https://zonca.github.io/2016/04/jupyterhub-sdsc-cloud.html)
@@ -154,7 +149,7 @@ The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/o
### Elucidata
- What's new in Jupyter Notebooks @[Elucidata](https://elucidata.io/):
- What's new in Jupyter Notebooks @[Elucidata](https://www.elucidata.io/):
- [Using Jupyter Notebooks with Jupyterhub on GCP, managed by GKE](https://medium.com/elucidata/why-you-should-be-using-a-jupyter-notebook-8385a4ccd93d)
## Service Providers
@@ -174,7 +169,7 @@ The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/o
### Microsoft Azure
- [Azure Data Science Virtual Machine release notes](https://docs.microsoft.com/en-us/azure/machine-learning/machine-learning-data-science-linux-dsvm-intro)
- [Azure Data Science Virtual Machine release notes](https://learn.microsoft.com/en-us/azure/machine-learning/machine-learning-data-science-linux-dsvm-intro)
### Rackspace Carina
@@ -202,5 +197,5 @@ The [Educational Development and Technology](https://ethz.ch/en/the-eth-zurich/o
- https://www.walkingrandomly.com/?p=5734
- https://wrdrd.com/docs/consulting/education-technology
- https://bitbucket.org/jackhale/fenics-jupyter
- [LinuxCluster blog](https://linuxcluster.wordpress.com/category/application/jupyterhub/)
- [LinuxCluster blog](https://thelinuxcluster.com/category/application/jupyterhub/)
- [Spark Cluster on OpenStack with Multi-User Jupyter Notebook](https://arnesund.com/2015/09/21/spark-cluster-on-openstack-with-multi-user-jupyter-notebook/)

View File

@@ -563,7 +563,7 @@ and an example of its configuration is found [here](https://github.com/jupyter/n
nbviewer can also be run as a Hub-Managed Service as described [nbviewer README][nbviewer example]
section on securing the notebook viewer.
[requests]: https://docs.python-requests.org/en/master/
[requests]: https://requests.readthedocs.io
[services_auth]: ../api/services.auth.html
[nbviewer example]: https://github.com/jupyter/nbviewer#securing-the-notebook-viewer
[fastapi example]: https://github.com/jupyterhub/jupyterhub/tree/HEAD/examples/service-fastapi

View File

@@ -467,7 +467,7 @@ spawner, does not support limits and guarantees. One of the spawners
that supports limits and guarantees is the
[`systemdspawner`](https://github.com/jupyterhub/systemdspawner).
### Memory Limits & Guarantees
### Memory Limits and Guarantees
`c.Spawner.mem_limit`: A **limit** specifies the _maximum amount of memory_
that may be allocated, though there is no promise that the maximum amount will
@@ -487,7 +487,7 @@ available for the single-user notebook server to use. The environment variable
limits and providing these guarantees.** If these values are set to `None`, no
limits or guarantees are provided, and no environment values are set.
### CPU Limits & Guarantees
### CPU Limits and Guarantees
`c.Spawner.cpu_limit`: In supported spawners, you can set
`c.Spawner.cpu_limit` to limit the total number of cpu-cores that a

View File

@@ -46,7 +46,7 @@ If you want to run docker on a computer that has a public IP then you should
(as in MUST) **secure it with ssl** by adding ssl options to your docker
configuration or using an ssl enabled proxy.
[Mounting volumes](https://docs.docker.com/engine/admin/volumes/volumes/)
[Mounting volumes](https://docs.docker.com/engine/storage/volumes/)
enables you to persist and store the data generated by the docker container, even when you stop the container.
The persistent data can be stored on the host system, outside the container.

View File

@@ -11,7 +11,6 @@ Before installing JupyterHub, you will need:
installing Python packages is helpful.
- [Node.js {{node_min}}](https://www.npmjs.com/) or greater, along with npm. [Install Node.js/npm](https://docs.npmjs.com/getting-started/installing-node),
using your operating system's package manager.
- If you are using **`conda`**, the nodejs and npm dependencies will be installed for
you by conda.

6502
jsx/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,45 +35,45 @@
"testEnvironment": "jsdom"
},
"dependencies": {
"bootstrap": "^5.3.6",
"bootstrap": "^5.3.7",
"history": "^5.3.0",
"lodash": "^4.17.21",
"prop-types": "^15.8.1",
"react": "^19.1.0",
"react": "^19.1.1",
"react-bootstrap": "^2.10.10",
"react-dom": "^19.1.0",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0",
"react-redux": "^9.2.0",
"react-router": "^7.6.1",
"react-router": "^7.7.1",
"redux": "^5.0.1",
"regenerator-runtime": "^0.14.1"
},
"devDependencies": {
"@babel/core": "^7.27.4",
"@babel/preset-env": "^7.27.2",
"@babel/core": "^7.28.0",
"@babel/preset-env": "^7.28.0",
"@babel/preset-react": "^7.27.1",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.28.0",
"@testing-library/jest-dom": "^6.6.3",
"@eslint/js": "^9.32.0",
"@testing-library/jest-dom": "^6.6.4",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@webpack-cli/serve": "^3.0.1",
"babel-jest": "^29.7.0",
"babel-jest": "^30.0.5",
"babel-loader": "^10.0.0",
"css-loader": "^7.1.2",
"eslint": "^9.28.0",
"eslint-plugin-prettier": "^5.4.1",
"eslint": "^9.32.0",
"eslint-plugin-prettier": "^5.5.3",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-unused-imports": "^4.1.4",
"file-loader": "^6.2.0",
"globals": "^16.2.0",
"globals": "^16.3.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"prettier": "^3.5.3",
"jest": "^30.0.5",
"jest-environment-jsdom": "^30.0.5",
"prettier": "^3.6.2",
"style-loader": "^4.0.0",
"webpack": "^5.99.9",
"webpack": "^5.101.0",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.1"
"webpack-dev-server": "^5.2.2"
}
}

View File

@@ -139,7 +139,7 @@ test("Interacting with PaginationFooter causes page refresh", async () => {
render(groupsJsx(updateGroupsSpy));
});
expect(updateGroupsSpy).toBeCalledWith(0, 2);
expect(updateGroupsSpy).toHaveBeenCalledWith(0, 2);
var lastState =
mockReducers.mock.results[mockReducers.mock.results.length - 1].value;
@@ -153,5 +153,5 @@ test("Interacting with PaginationFooter causes page refresh", async () => {
});
expect(searchParams.get("offset")).toEqual("2");
// FIXME: useSelector mocks prevent updateGroups from being called
// expect(updateGroupsSpy).toBeCalledWith(2, 2);
// expect(updateGroupsSpy).toHaveBeenCalledWith(2, 2);
});

View File

@@ -591,14 +591,14 @@ test("Search for user calls updateUsers with name filter", async () => {
expect(searchParams.get("offset")).toEqual(null);
// FIXME: useSelector mocks prevent updateUsers from being called
// expect(mockUpdateUsers.mock.calls).toHaveLength(2);
// expect(mockUpdateUsers).toBeCalledWith(0, 100, "a");
// expect(mockUpdateUsers).toHaveBeenCalledWith(0, 100, "a");
await user.type(search, "b");
expect(search.value).toEqual("ab");
await act(async () => {
jest.runAllTimers();
});
expect(searchParams.get("name_filter")).toEqual("ab");
// expect(mockUpdateUsers).toBeCalledWith(0, 100, "ab");
// expect(mockUpdateUsers).toHaveBeenCalledWith(0, 100, "ab");
});
test("Interacting with PaginationFooter requests page update", async () => {
@@ -606,7 +606,7 @@ test("Interacting with PaginationFooter requests page update", async () => {
render(serverDashboardJsx());
});
expect(mockUpdateUsers).toBeCalledWith(defaultUpdateUsersParams);
expect(mockUpdateUsers).toHaveBeenCalledWith(defaultUpdateUsersParams);
var n = 3;
expect(searchParams.get("offset")).toEqual(null);
@@ -619,7 +619,7 @@ test("Interacting with PaginationFooter requests page update", async () => {
});
expect(searchParams.get("offset")).toEqual("2");
// FIXME: useSelector mocks prevent updateUsers from being called
// expect(mockUpdateUsers).toBeCalledWith({
// expect(mockUpdateUsers).toHaveBeenCalledWith({
// ...defaultUpdateUsersParams,
// offset: 2,
// });

View File

@@ -870,10 +870,10 @@ class SpawnProgressAPIHandler(APIHandler):
html_message = getattr(exc, "jupyterhub_html_message", "")
if html_message:
failed_event['html_message'] = html_message
await self.send_event(failed_event)
return
else:
raise web.HTTPError(400, "%s is not starting...", spawner._log_name)
await self.send_event(failed_event)
return
# retrieve progress events from the Spawner
async with aclosing(
@@ -1034,7 +1034,7 @@ class ActivityAPIHandler(APIHandler):
user.name,
server_name,
isoformat(last_activity),
isoformat(user.last_activity),
isoformat(spawner.last_activity),
)
self.db.commit()

View File

@@ -32,7 +32,7 @@ from dateutil.parser import parse as parse_date
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader
from jupyter_events.logger import EventLogger
from sqlalchemy.exc import OperationalError, SQLAlchemyError
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import contains_eager, selectinload
from tornado import gen, web
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop, PeriodicCallback
@@ -3097,9 +3097,10 @@ class JupyterHub(Application):
.filter(orm.Spawner.server != None)
# pre-load relationships to avoid O(N active servers) queries
.options(
joinedload(orm.User._orm_spawners),
joinedload(orm.Spawner.server),
contains_eager(orm.User._orm_spawners),
selectinload(orm.Spawner.server),
)
.populate_existing()
):
# instantiate Spawner wrapper and check if it's still alive
# spawner should be running

View File

@@ -62,7 +62,7 @@ async def test_submit_login_form(app, browser, user_special_chars):
await browser.goto(login_url)
await login(browser, user.name, password=user.name)
expected_url = public_url(app, user)
await expect(browser).to_have_url(expected_url)
await browser.wait_for_url(expected_url)
@pytest.mark.parametrize(
@@ -143,7 +143,7 @@ async def test_open_url_login(
await expect(browser).to_have_url(re.compile(pattern))
await expect(browser).not_to_have_url(re.compile(".*/user/.*"))
else:
await expect(browser).to_have_url(
await browser.wait_for_url(
re.compile(".*/user/" + f"{user_special_chars.urlname}/")
)
@@ -883,17 +883,15 @@ async def test_menu_bar(app, browser, page, logged_in, user_special_chars):
expected_url = f"hub/login?next={url_escape(app.base_url)}"
assert expected_url in browser.url
else:
await expect(browser).to_have_url(
await browser.wait_for_url(
re.compile(f".*/user/{user_special_chars.urlname}/")
)
await browser.go_back()
await expect(browser).to_have_url(re.compile(".*" + page))
await browser.wait_for_url(re.compile(".*" + page))
elif index == 3:
await expect(browser).to_have_url(re.compile(".*/login"))
await browser.wait_for_url(re.compile(".*/login"))
else:
await expect(browser).to_have_url(
re.compile(".*" + expected_link_bar_url[index])
)
await browser.wait_for_url(re.compile(".*" + expected_link_bar_url[index]))
# LOGOUT
@@ -924,8 +922,8 @@ async def test_user_logout(app, browser, url, user_special_chars):
# verify that user can login after logout
await login(browser, user.name, password=user.name)
await expect(browser).to_have_url(
re.compile(".*/user/" + f"{user_special_chars.urlname}/")
await browser.wait_for_url(
re.compile(".*/user/" + f"{user_special_chars.urlname}/"),
)
@@ -1016,7 +1014,7 @@ async def test_oauth_page(
await expect(scopes_element).not_to_be_visible()
for scopes_element in scopes_elements
]
# checking that all scopes granded to user are presented in POST form (scope_list)
# checking that all scopes granted to user are presented in POST form (scope_list)
scope_list_oauth_page = [
await scopes_element.get_attribute("value")
for scopes_element in scopes_elements
@@ -1288,8 +1286,8 @@ async def test_start_stop_server_on_admin_page(
spawn_btn_xpath = f'//a[contains(@href, "spawn/{username}")]/button[contains(@class, "btn-light")]'
spawn_btn = browser.locator(spawn_btn_xpath)
await expect(spawn_btn).to_be_enabled()
async with browser.expect_navigation(url=f"**/user/{username}/"):
await spawn_btn.click()
await browser.wait_for_url(url=f"**/user/{username}/")
async def click_access_server(browser, username):
"""access to the server for users via the Access Server button"""
@@ -1337,7 +1335,7 @@ async def test_start_stop_server_on_admin_page(
# click on Spawn page button
await click_spawn_page(browser, user2.name)
await expect(browser).to_have_url(re.compile(".*" + f"/user/{user2.name}/"))
await browser.wait_for_url(re.compile(".*" + f"/user/{user2.name}/"))
# open/return to the Admin page
admin_page = url_path_join(public_host(app), app.hub.base_url, "admin")
@@ -1491,18 +1489,18 @@ async def test_singleuser_xsrf(
await browser.goto(login_url)
await login(browser, browser_user.name, browser_user.name)
# end up at single-user
await expect(browser).to_have_url(re.compile(rf".*/user/{browser_user.name}/.*"))
await browser.wait_for_url(re.compile(rf".*/user/{browser_user.name}/.*"))
# wait for target user to start, too
await target_start
await app.proxy.add_user(target_user)
# visit target user, sets credentials for second server
await browser.goto(public_url(app, target_user))
await expect(browser).to_have_url(re.compile(r".*/oauth2/authorize"))
await browser.wait_for_url(re.compile(r".*/oauth2/authorize"))
auth_button = browser.locator('//button[@type="submit"]')
await expect(auth_button).to_be_enabled()
await auth_button.click()
await expect(browser).to_have_url(re.compile(rf".*/user/{target_user.name}/.*"))
await browser.wait_for_url(re.compile(rf".*/user/{target_user.name}/.*"))
# at this point, we are on a page served by target_user,
# logged in as browser_user
@@ -1644,8 +1642,8 @@ async def test_singleuser_xsrf(
url_path_join(app.base_url, f"hub/spawn/{browser_user.name}/{server_name}"),
)
await browser.goto(url)
await expect(browser).to_have_url(
re.compile(rf".*/user/{browser_user.name}/{server_name}/.*")
await browser.wait_for_url(
re.compile(rf".*/user/{browser_user.name}/{server_name}/.*"),
)
# from named server URL, make sure we can talk to a kernel
token = browser_user.new_api_token(scopes=["access:servers!user"])

View File

@@ -65,7 +65,9 @@ async def test_proxy_service(app, mockservice_url):
service = mockservice_url
name = service.name
await app.proxy.get_all_routes()
url = public_url(app, service) + '/foo'
url = public_url(app, service)
assert url.endswith("/")
url += "foo"
r = await async_requests.get(url, allow_redirects=False)
path = f'/services/{name}/foo'
r.raise_for_status()

195
package-lock.json generated
View File

@@ -24,15 +24,18 @@
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz",
"integrity": "sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==",
"license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
"engines": {
"node": ">=6"
}
},
"node_modules/@parcel/watcher": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz",
"integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
@@ -48,28 +51,30 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.4.1",
"@parcel/watcher-darwin-arm64": "2.4.1",
"@parcel/watcher-darwin-x64": "2.4.1",
"@parcel/watcher-freebsd-x64": "2.4.1",
"@parcel/watcher-linux-arm-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-glibc": "2.4.1",
"@parcel/watcher-linux-arm64-musl": "2.4.1",
"@parcel/watcher-linux-x64-glibc": "2.4.1",
"@parcel/watcher-linux-x64-musl": "2.4.1",
"@parcel/watcher-win32-arm64": "2.4.1",
"@parcel/watcher-win32-ia32": "2.4.1",
"@parcel/watcher-win32-x64": "2.4.1"
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz",
"integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -83,13 +88,14 @@
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz",
"integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -103,13 +109,14 @@
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz",
"integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -123,13 +130,14 @@
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz",
"integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -143,13 +151,35 @@
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz",
"integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -163,13 +193,14 @@
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz",
"integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -183,13 +214,14 @@
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz",
"integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -203,13 +235,14 @@
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz",
"integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -223,13 +256,14 @@
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz",
"integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -243,13 +277,14 @@
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz",
"integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -263,13 +298,14 @@
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz",
"integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -283,13 +319,14 @@
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz",
"integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -306,6 +343,7 @@
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
@@ -313,9 +351,9 @@
}
},
"node_modules/bootstrap": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
"integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
"funding": [
{
"type": "github",
@@ -336,6 +374,7 @@
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"fill-range": "^7.1.1"
@@ -345,10 +384,11 @@
}
},
"node_modules/chokidar": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
@@ -364,6 +404,7 @@
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
@@ -377,6 +418,7 @@
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -386,9 +428,9 @@
}
},
"node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
"integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true,
"license": "MIT"
},
@@ -397,6 +439,7 @@
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.10.0"
@@ -407,6 +450,7 @@
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-extglob": "^2.1.1"
@@ -420,6 +464,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.12.0"
@@ -428,13 +473,15 @@
"node_modules/jquery": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"license": "MIT"
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"braces": "^3.0.3",
@@ -448,6 +495,7 @@
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
@@ -457,6 +505,7 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/picomatch": {
@@ -464,6 +513,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8.6"
@@ -473,12 +523,13 @@
}
},
"node_modules/readdirp": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz",
"integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==",
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.16.0"
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
@@ -499,9 +550,9 @@
}
},
"node_modules/sass": {
"version": "1.89.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz",
"integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==",
"version": "1.90.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
"integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -520,10 +571,11 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
@@ -533,6 +585,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-number": "^7.0.0"

View File

@@ -51,7 +51,7 @@ test = [
# the test test_nbclassic_control_panel.
"nbclassic",
"pytest>=3.3",
"pytest-asyncio>=0.17,!=0.23.*",
"pytest-asyncio>=0.17,!=0.23.*,<1.0.0",
"pytest-cov",
"pytest-rerunfailures",
"requests-mock",