diff --git a/.eslintrc.js b/.eslintrc.js index e90a1ac4b..1de84e890 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -77,8 +77,12 @@ module.exports = { "semi": 2, "arrow-spacing": 2, "no-prototype-builtins": 1, + "@typescript-eslint/ban-types": 1, "@typescript-eslint/camelcase": 0, + "@typescript-eslint/no-explicit-any": 1, "@typescript-eslint/no-var-requires": 1, + "@typescript-eslint/no-unsafe-declaration-merging": 1, + "@typescript-eslint/no-unused-vars": 1, "@typescript-eslint/no-use-before-define": 1, "@typescript-eslint/no-empty-interface": 1 }, diff --git a/.gitignore b/.gitignore index dd85fec89..57ce2d83b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,8 @@ *.log /build /gitno +cache node_modules -doc/code -doc/_build -doc/__pycache__/ tags +docs/api/index.md +docs/.vitepress/dist/ diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index 50ef177a2..000000000 --- a/doc/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = python -msphinx -SPHINXPROJ = remoteStoragejs -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css deleted file mode 100644 index 4a0a6d1e5..000000000 --- a/doc/_static/css/custom.css +++ /dev/null @@ -1,11 +0,0 @@ -/* Add a left and right margin to parens in function signatures */ -.rst-content dl .sig-paren { - margin: 0 0.15em; -} - -@media screen and (min-width : 640px) { - .wy-table-responsive table th, - .wy-table-responsive table td { - white-space: normal; - } -} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html deleted file mode 100644 index e18c407a5..000000000 --- a/doc/_templates/layout.html +++ /dev/null @@ -1,5 +0,0 @@ - -{% extends "!layout.html" %} -{% block extrahead %} - -{% endblock %} diff --git a/doc/build-with-conda.sh b/doc/build-with-conda.sh deleted file mode 100755 index 568f56a0d..000000000 --- a/doc/build-with-conda.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# This script replicates the build process that ReadTheDocs uses, at least as well as it can - -# Get script location/path: https://stackoverflow.com/a/246128 -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -# Enter repository root -cd "$SCRIPT_DIR/.." || exit 1 - -# Setup conda environment -conda env create --quiet --name rtd_env --file doc/environment.yml -conda install --yes --quiet --name rtd_env mock pillow sphinx sphinx_rtd_theme -# Store conda bin path for later use -CONDA_BIN=/home/docs/.conda/envs/rtd_env/bin -"$CONDA_BIN/python" -m pip install -U --no-cache-dir recommonmark six readthedocs-sphinx-ext - -# Build docs -cd doc || exit 1 -"$CONDA_BIN/python" -m sphinx -v -T -E -b html -d _build/doctrees -D language=en . _build/html diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index e08cea763..000000000 --- a/doc/conf.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# remoteStorage.js documentation build configuration file, created by -# sphinx-quickstart on Fri Aug 25 14:11:51 2017. # # This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import datetime -import os -import sys -from pathlib import Path -sys.path.insert(0, os.path.abspath('.')) - -from version import __version__ - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autosectionlabel', - 'sphinx_js', - 'sphinx_issues' -] - -autosectionlabel_prefix_document = True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# Used to extract JSDoc function/class docs from source -js_language = 'typescript' -js_source_path = '../src/' -jsdoc_config_path = '../tsconfig.json' -primary_domain = 'js' - -# GitHub issues config -issues_github_path = 'remotestorage/remotestorage.js' - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -# source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = 'remoteStorage.js' -copyright = '2012-{current_year}, RS Contributors'.format( - current_year=datetime.date.today().strftime("%Y") -) -author = 'RS Contributors' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = __version__ -# The full version, including alpha/beta/rc tags. -release = __version__ - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# Default language for code highlighting -highlight_language = 'javascript' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# This is required for the alabaster theme -# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - 'donate.html', - ] -} - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'remoteStoragejsdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'remoteStoragejs.tex', 'remoteStorage.js Documentation', - 'RS Community', 'manual'), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'remotestoragejs', 'remoteStorage.js Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'remoteStoragejs', 'remoteStorage.js Documentation', - author, 'remoteStoragejs', 'One line description of project.', - 'Miscellaneous'), -] - -# -# HACKFIX WARNING -# TODO Remove this when RTD updates their Node.js version -# https://github.com/readthedocs/readthedocs-docker-images/issues/107 -# -# Manually add custom Node.js/npm from conda to PATH -python_executable_directory = Path(sys.executable).parent -sys.path.insert(0, str(python_executable_directory)) -os.environ["PATH"] = f"{python_executable_directory}:{os.environ['PATH']}" -# Verify Node.js version is correct -os.system('node --version') -os.system('npm --version') - -# -# HACKFIX WARNING -# TODO Remove this when there is official support for pre-build steps on RTD -# https://github.com/readthedocs/readthedocs.org/issues/6662 -# -typedoc_directory = Path('../node_modules/typedoc/bin').resolve() -if not typedoc_directory.exists(): - os.system('npm install') -assert typedoc_directory.exists(), "There was an issue with the `npm install`, please check the logs." -# -# HACKFIX WARNING -# TODO Remove this when RTD updates their Node.js version -# https://github.com/readthedocs/readthedocs-docker-images/issues/107 -# -# Manually add typedoc to PATH -os.system(f'chmod +x {typedoc_directory / "typedoc"}') -os.environ["PATH"] = f"{typedoc_directory}:{os.environ['PATH']}" diff --git a/doc/contributing.rst b/doc/contributing.rst deleted file mode 100644 index b6b0bd293..000000000 --- a/doc/contributing.rst +++ /dev/null @@ -1,18 +0,0 @@ -Contributing -============ - -This section contains information and help for people wanting to contribute -to remoteStorage.js development. - -Every bit helps, even fixing a typo is worth a pull request! - -.. toctree:: - :maxdepth: 2 - - contributing/code-overview.rst - contributing/building.rst - contributing/testing.rst - contributing/docs.rst - contributing/github-flow.rst - contributing/release-checklist.rst - contributing/internals diff --git a/doc/contributing/building.rst b/doc/contributing/building.rst deleted file mode 100644 index d882f86b1..000000000 --- a/doc/contributing/building.rst +++ /dev/null @@ -1,45 +0,0 @@ -Building -======== - -.. HINT:: - We're using npm scripts for all common tasks, so check out the ``scripts`` - section in ``package.json`` to learn about what they're doing exactly and - what else is available. - -Setup ------------ - -.. CODE:: console - - $ npm install --force - -This will install all dependencies. We currently use the ``--force`` flag -because of an issue with SphinxJS and TypeDoc (see :issue:`1276`). - -Development ------------ - -.. CODE:: console - - $ npm run dev - -This will watch ``src/`` for changes and build ``remotestorage.js`` in the -``release/`` directory every time you save a source file. Useful for testing -rs.js changes with an app, for example by creating a symlink to -``release/remotestorage.js``. - -This build includes `source maps `_ -directly, so you can easily place ``debugger`` statements in the code and step -through the actual source code in your browser's debugger tool. - -Production ----------- - -.. CODE:: console - - $ npm run build:release - -This creates the minified production build in ``release/``. - -It also creates a separate source maps file, which you can link to in case you -want to (e.g. to improve exception tracking/debugging in production). diff --git a/doc/contributing/code-overview.rst b/doc/contributing/code-overview.rst deleted file mode 100644 index f16ed1107..000000000 --- a/doc/contributing/code-overview.rst +++ /dev/null @@ -1,32 +0,0 @@ -Code overview -============= - -The code of remoteStorage.js consists of files in the ``src/`` folder of this -repo. These are built into a single file in the ``release/`` folder using -`webpack `_. Unit tests live in the ``test/`` folder -and are based on `Jaribu `_. - -The structure of the code is based around feature loading. Most files in -``src/`` correspond to a feature, e.g. ``discover.ts`` to -``RemoteStorage.Discover`` or ``caching.ts`` to ``RemoteStorage.Caching``. - -The feature loading happens synchronously during the page load in -``src/remotestorage.ts`` (just including this script in your app will lead to -executing the code that loads the features). - -Most features load under their own name, but for ``remoteStorage.local`` a -choice is made between ``RemoteStorage.IndexedDB``, -``RemoteStorage.LocalStorage`` and ``RemoteStorage.InMemoryCaching``, depending -on what the environment (browser, node.js, Electron, WebView, or other) -supports. - -For ``remoteStorage.local`` we then also have a `special mixin -`__ -called ``src/cachinglayer.ts``, which mixes in some common functions into the -object. - -The ``remoteStorage.remote`` feature is not loaded immediately, but only when -``RemoteStorage.Discover`` calls ``remoteStorage.setBackend()``, at which point a -choice is made between ``RemoteStorage.WireClient``, -``RemoteStorage.GoogleDrive``, ``RemoteStorage.Dropbox`` (or any other future -backend) to become the ``remote``. diff --git a/doc/contributing/docs.rst b/doc/contributing/docs.rst deleted file mode 100644 index 00af3119f..000000000 --- a/doc/contributing/docs.rst +++ /dev/null @@ -1,92 +0,0 @@ -.. highlight:: console - -Documentation -============= - -The documentation for remoteStorage.js is generated from `reStructuredText -`_ files in the ``doc/`` folder, as -well as `TypeDoc `_ code comments, which are being pulled -in via special declarations in those files. - -We use `Sphinx `_ to generate the documentation -website, and the `sphinx-js `_ -extension for handling the TypeDoc part. - -How to write reStructuredText and TypeDoc ------------------------------------------ - -For learning both the basics and advances features of reStructuredText, we -highly recommend the `reStructuredText Primer -`_ on the Sphinx website. - -For TypeDoc, you can find guides as well as a detailed reference `on -the project's website `_. - -Automatic builds and publishing -------------------------------- - -The documentation is published via `Read the Docs `_. -Whenever the Git repository's ``master`` branch is pushed to GitHub, RTD will -automatically build a new version of the site and publish it to -`remotestoragejs.readthedocs.io `_. - -This means that if you want to contribute to the documentation, you don't -necessarily have to set up Sphinx and sphinx-js locally (especially for small -changes). However, if you want to preview what your local changes look like -when they are rendered as HTML, you will have to set up local builds first. - -How to build the docs on your machine -------------------------------------- - -Setup -^^^^^ - -1. `Install Python and PIP `_ - (likely already installed) - -2. Install sphinx-js and extensions (from repository root):: - - $ pip install -r doc/requirements.txt - -3. Install TypeScript and TypeDoc globally (so Sphinx can use them):: - - $ npm -g install typescript typedoc - -Build -^^^^^ - -Run the following command to automatically watch and build the documentation:: - - $ npm run autobuild-docs - -This will start a web server, serving rendered HTML docs on ``_. - -.. HINT:: - The autobuild cannot watch for changes in TypeDoc comments as of now, so you - will need to re-run the command, or change something in a ``.rst`` file in - order for code documentation changes to be re-built. - -How to build the docs using ReadTheDocs' Docker image ------------------------------------------------------ - -This is useful for troubleshooting when the ReadTheDocs build is failing. - -Setup -^^^^^ - -1. `Install Docker `_ - -2. Pull the latest version of ``readthedocs/build`` image with the ``latest`` tag from Docker Hub:: - - $ docker pull readthedocs/build:latest - -Build -^^^^^ - -1. Enter a ``bash`` session while attaching this project as a volume:: - - $ docker run --rm -it -v ${PWD}:/app readthedocs/build:latest bash - -2. Run the ``build-with-conda.sh`` script to setup conda environment and build the docs like ReadTheDocs:: - - $ /app/doc/build-with-conda.sh diff --git a/doc/contributing/github-flow.rst b/doc/contributing/github-flow.rst deleted file mode 100644 index b15f5300c..000000000 --- a/doc/contributing/github-flow.rst +++ /dev/null @@ -1,129 +0,0 @@ -GitHub workflow -=============== - -General guidelines ------------------- - -- When you start working on an existing GitHub issue (or you plan on - doing that in the immediate future), assign it to yourself, so that - others can see it and don't start working on it in parallel. -- When you create a branch to work on something, use the naming scheme - described further down in this document. -- Never push directly to the ``master`` branch for any changes to the - source code itself. -- As soon as you want others to review your changes, or even just - discuss them, create a pull request. Don't forget to explain roughly - what it is you're doing in that branch, i.e. what the problem/idea is - and what the result is supposed to be, when merging the changes. If - necessary or helpful, mention related discussions from other issues. -- A pull request can be merged as soon as at least two people with - commit access to the repo have given a +1, meaning they reviewed - and tested the changes and have no further improvements to suggest. - -Branch names ------------- - -Using common branch names, that include topics and issue IDs, makes everyone's -lives much easier, and keep the repo clean. Branches on our organization -repositories should be created using the following scheme: - -``[bugfix|feature|docs|refactor]/[issue id]-[description_with_underscores]`` - -So for example, if you want to work on fixing a bug with let's say -initial sync, that is described in issue #423, the branch should look -something like: - -``bugfix/423-race_condition_on_initial_sync`` - -And if it's an enhancement to the widget it could look like this e.g.: - -``feature/321-customizable_widget_content`` - -If there's no issue yet, create one first! - -Pulling changes ---------------- - -Always use ``--rebase`` when pulling code from the remote repo. That way -your local changes are added on top of the current history, avoiding -merge commits and mixing up the commit history. You can set up Git -to use rebase on every pull by default by running ``git config --global -pull.rebase true`` once. - -If you also add the ``autostash`` option, Git will stash any changed files -before the pull and unstash them afterwards: ``git config --global -rebase.autoStash true``. - -If you don't want to configure both options globally, or you prefer a catchier -command name for updating a repository with remote changes, we recommend -configuring an alias, like so: ``git config --global alias.up 'pull --rebase ---autostash'``. Now you can simply run ``git up`` in select repos, or -everywhere. - -Commit messages ---------------- - -- The first line of the message (aptly called "subject line" in Git - terminology) should not be longer than 72 characters. -- If the subject line is not enough to describe the changes properly, - add a blank line after the subject line and then as much text as you - want, using normal language with capitalization, punctuation, etc. -- Always use messages that describe roughly *what* the change does and, - if not obvious, *why* this change leads to the desired result. -- Leave out any text that isn't directly associated with the changes, - that the commit introduces. Examples: "as suggested by @chucknorris", - "lol wtf was that", "not sure if this fixes it". -- Commit as much and often as possible locally (and with any message - that helps you during your work), and then clean up the history and - merge commits that belong together before pushing to the org repo. - You can do that with ``git rebase -i [ref]`` (`learn - more `__). -- You can reference issues from commit messages by adding keywords with - issue numbers. Certain keywords will even close the issue - automatically, once a branch is merged into master. For example - ``Fix widget flickering when opening bubble (fixes #423)`` will close - issue #423 when appearing on the master branch at GitHub. - -Reviewing pull requests ------------------------ - -- Check if it works, if it has unit tests, if the tests pass, and if - the linter is happy. -- Check if the code is understandable, with clear and unambiguous names for - functions and variables, and that it has TypeDoc comments and a changelog - entry. -- If the pull request was issued from a user's own repository, you will - have to fetch the code from there. If you haven't pulled - from their fork previously, you can add a new remote for it with - ``git remote add [username] [repo-url]``. Then, ``git fetch [username]`` - will fetch code from this remote, so you can then check out their branch - using ``git checkout [username]/branchname``. -- This will put you in a so-called 'detached HEAD' state, but don't - worry, everything is fine! If you want to work on that code, just create a - new branch from there with the command Git tells you then, or just go back - to your code with e.g. ``git checkout master`` later.) - -Merging pull requests ---------------------- - -- Once a pull request has two +1s for the latest changes from - collaborators, you can either merge it yourself or wait for somebody - to do it for you (which will happen very soon). -- If the new commits and their commit messages in that branch all make - sense on their own, you can use the merge button on GitHub directly. -- If there are a lot of small commits, which might not make sense on - their own, or pollute the main project history (often the case with - long running pull requests with a lot of additions during their - lifetime), fetch the latest changes to your local machine, and either - do an interactive rebase to clean up branch and merge normally, or - use ``git merge --squash`` to squash them all into one commit during - the merge. -- Whenever you squash multiple commits with either ``git rebase -i`` or - ``git merge --squash``, make sure to follow the commit message - guidelines above. Don't just leave all old commit messages in there - (which is the default), but delete them and create a new meaningful - message for the whole changeset. -- When squashing/editing/amending other peoples' commits, use - ``--author`` to set them as the original author. You don't need full - names for that, but just something that Git can find in the history. - It'll tell you if it can't find an author and let you do it again. diff --git a/doc/contributing/internals.rst b/doc/contributing/internals.rst deleted file mode 100644 index d580ba571..000000000 --- a/doc/contributing/internals.rst +++ /dev/null @@ -1,13 +0,0 @@ -Libary internals -================ - -This section contains information about some of the internals and -concepts of the remoteStorage.js library. - -.. toctree:: - :maxdepth: 2 - - internals/discovery-bootstrap - internals/caching - internals/cache-data-format - diff --git a/doc/contributing/internals/cache-data-format.rst b/doc/contributing/internals/cache-data-format.rst deleted file mode 100644 index 6000159eb..000000000 --- a/doc/contributing/internals/cache-data-format.rst +++ /dev/null @@ -1,216 +0,0 @@ -Data format of the local cache -============================== - -This section describes the structure and concepts of the local cache. - -Storing up to 4 revisions of each node --------------------------------------- - -Each cache node represents the versioning state of either one document -or one folder. The versioning state is represented by one or more of the -``common``, ``local``, ``remote``, and ``push`` revisions. Local -changes are stored in ``local``, and in ``push`` while an outgoing -request is active. Remote changes that have either not been fetched yet, -or have not been merged with local changes yet, are stored in -``remote``. - -autoMerge ---------- - -The ``sync.autoMerge`` function will try to merge local and remote -changes into the common revision of a node. It may emit change events -with a 'conflict' origin to indicate that an unpushed local change was -overruled by a remote change. - -When consulting the base client about the current value of a node, you -will get either its 'local' revision if it exists, or its 'common' -revision otherwise. The following are versioning tree diagrams of how -local and remote revisions of a node can interact: - -.. CODE:: text - - //in sync: - 1) . . . . [common] - - //dirty: - 2) . . . . [common] - \ - \ . . . . [remote] - - //local change: - 3) . . . . [common] . . . . [local] - - //conflict (should autoMerge): - 4) . . . . [common] . . . . [local] - \ - \ . . . . [remote] - - //pushing: - 5) . . . . [common] . . . . [push] . . . . [local] - - //pushing, and known dirty (should abort the push, or just wait for the conflict to occur): - 6) . . . . [common] . . . . [push] . . . . [local] - \ - \ . . . . [remote] - -Each of ``local``, ``push``, ``remote``, and ``common`` can have have -following properties: - -* for documents: - - * body - * contentType - * contentLength - * revision - * timestamp - -* for folders: - - * itemsMap (itemName -> true, or itemName -> false to indicate an - unmerged deletion) - * revision - * timestamp - -NB: The timestamp represents the last sync time, not the last modified -time. It is used by the ``isOutdated`` function in -``src/cachinglayer.js`` to determine if the data needs to be fetched -from remote again, or can be served from cache. - -"keep/revert" conflict resolution ---------------------------------- - -RemoteStorage implements a hub-and-spokes versioning system, with one -central remoteStorage server (the hub) and any number of clients (the -spokes). The clients will typically be unhosted web apps based on this -JS lib (remotestorage.js), but they don't have to be; they can also be -based on other client implementations, they can be hosted web apps, -desktop apps, native smartphone apps, etcetera. New versions of subtrees -always start at one of these clients. They are then sent to the server, -and from there to all the other clients. The server assigns the revision -numbers and sends them to the initiating client using HTTP ETag response -headers in response to PUT requests. remotestorage.js is a library that -attempts to make it easy to build remoteStorage applications, by hiding both -the push/pull synchronization and the version merging from the app -developer. Versioning conflicts between the local client and the remote -server are initially resolved as a 'remote wins', to which the client -code may respond with an explicit revert (putting the old, local version -back), any type of custom merge (putting the result of the merge in -place), or by doing nothing ("keep"), and leaving the remote result in -place. This system is called "keep/revert", since the library takes a -pro-active action ('remote wins'), which the app can then either keep, -or revert. - -Sync is tree-based: syncing a node is equivalent to syncing all its -children. There are two parts at play, that interact: transporting the -diffs to and from the remote server, and merging the local and remote -versions into one common version. Each document starts out as -non-existing in both its local and remote versions. From there on, it -can be created, updated, and deleted any number of times throughout its -history, both locally and remotely. If at some point in time it either -does not exist neither locally nor remotely, or its body and -content-type are the same byte-for-byte on both sides, then the two -stores are in agreement. If the document exists in only one of the -stores, or the document's body or its content-type differs between the -two stores, then the document is in conflict. - -The library is always aware of the latest local version, but it may or -may not be aware of the latest remote version, and therefore of whether -a conflict or agreement exists for the document. Likewise, the server is -not necessarily aware of the latest local version, if there are changes -that haven't been pushed out yet; nor does it care, though, since the -server does not get involved in conflict resolution. It only serializes -conditional updates from all clients into one canonical versioning -history. - -The lack of sync between client and server can be fixed by doing a GET, -PUT, or DELETE. A GET will return the current remote version; a -conditional PUT or DELETE will push out the change, while at the same -time checking if any unfetched remote changes exist. If they do, then -the push will fail, and the library will fetch instead. After this, the -library has a latest known common revision of the document, possibly a -local version if it was changed since then, and possibly a remote -version if it was changed since then, but the newer version has yet to -be retrieved. - -Before resolving a conflict, both revision histories are squashed. This -means that creating+deleting a document becomes a noop, and -deleting+creating, or updating it multiple times, becomes one single -update. Then, if the document was changed in different ways locally and -remotely, it goes into conflict state; if it was changed only locally or -only remotely, then the change is automatically accepted by the other -store (whether client to server or server to client). Note that in the -case of a successful conditional push request, this will already have -happened. - -Conflicts that are discovered by a document fetch, fire their -'keep/revert' event immediately. Conflicts that are discovered through a -parent folder fetch, or through a conditional push, fire their -'keep/revert' event after the new remote version is fetched. - -The library's conflict resolution strategy is 'remote wins'. This means -that the module will receive them in the form of change events with -origin 'conflict'. When receiving such a change event, the module can -still decide to revert it explicitly. - -As noted before, merging a subtree is done by merging each document that -exists within that subtree, in either or both stores. When the library -fetches a folder listing, it can detect a remote child change, which -then may or may not result in a conflict. When a folder listing comes -in, which has changed since the last time it was retrieved, four types -of information may be discovered: - -* which of the documents directly within the folder changed their - remote revision since the last check (new ETag on a document item) -* in which previously empty subtrees at least one document was created - (new folder item) -* in which subtrees all previously existing documents were deleted - (folder item disappeared) -* in which subtrees at least one document was either created, updated, - or deleted (new ETag on a folder item) - -All of these can occur in a folder that was at the same time either -unchanged, updated, or deleted locally. When updated, it might be that -different items were changed locally and remotely, or that the same item -was changed on both sides, either in the same way, or in different ways. - -The library handles all these cases so the module developer does not -need to worry about them. - -Implications for module design ------------------------------- - -There are a number of important implications for module design: - -* First of all, this sync process follows the 'asynchronous - synchronization' design principle - (https://github.com/offlinefirst/research/issues/9). Don't wait for - it to finish. The module should work with the local copy of the data, - and handle incoming updates through evented programming. The only - exception to this is where a body of data is too big to cache - locally, and the module needs to expose on-demand access of remote - data to the app. In all other cases, the module should expose the - local version as 'the truth'. -* Even then, IndexedDB is not fast enough to access from a button - click. Make sure to put an in-memory caching layer in the module, and - return control to the app immediately. An example of this approach is - the SyncedMap data structure used in - https://github.com/michielbdejong/meute. -* Use folders and subfolders. This allows the tree-based sync algorithm - to shine and efficiently detect changes in any of potentially - thousands of documents by checking the ETag from one single HTTP - request to the root folder of the tree. - -* Use meaningful collections. Multiple clients can each edit a - different document without ever entering in conflict with each other. - But editing the same document is interpreted as a conflict. For - instance, when two calendar apps both schedule an event on a certain - date, this would be a conflict if the module stores one document per - day. However, if the module stores one document per event, and - instead uses one /folder/ for each day, then the two events can - co-exist on the same day without generating a conflict. Documents are - a unit of conflict, but folders are not. Another example is storing - todo-list items with long UUID hashes instead of their list index - numbers as document names. Editing item "5" would conflict with - inserting a new item "5". But if both items have a long unique name, - then they don't clash with each other. So make sure to choose unique - item names for items that should not conflict. diff --git a/doc/contributing/internals/caching.rst b/doc/contributing/internals/caching.rst deleted file mode 100644 index 345ef7b5c..000000000 --- a/doc/contributing/internals/caching.rst +++ /dev/null @@ -1,25 +0,0 @@ -Caching -======= - -The caching strategies are stored in -``remoteStorage.caching._rootPaths``. For instance, on -https://myfavoritedrinks.remotestorage.io/, it has the value ``{ -/myfavoritedrinks/: "ALL" }``. - -These rootPaths are not stored in localStorage. If you refresh the page, -it is up to the app to set all caching strategies again during the -page load. - -The effect of the caching strategy is basically achieved through three -paths: - -1. Setting caching strategy 'ALL' for a path creates an empty node for - that path, unless it already exists. -2. The sync process will then do a GET request, and create new nodes - under any folder with an 'ALL' strategy, when that folder is fetched. -3. The sync process will create a new task for any node under an 'ALL' - strategy, unless a task already exists for one of its ancestors. - -The result is all paths with an explicit 'ALL' strategy will get -fetched, and if they are folders, then in the next round, all its -children will also be fetched, etcetera. diff --git a/doc/contributing/internals/discovery-bootstrap.rst b/doc/contributing/internals/discovery-bootstrap.rst deleted file mode 100644 index 13f1296f2..000000000 --- a/doc/contributing/internals/discovery-bootstrap.rst +++ /dev/null @@ -1,36 +0,0 @@ -Discovery bootstrap -=================== - -*This section describes how connecting to a storage works internally.* - -When the RemoteStorage instance is instantiated, it checks the fragment -of the URL to see if it contains an ``access_token`` or -``remotestorage`` parameter. In the first case, the access token is -given to the remote using ``remoteStorage.remote.configure()``. In the -second case, WebFinger discovery is triggered for the user address given -(see `storage-first section`_ of the remoteStorage spec). - -The user can also set the user address through the widget, or the app -can call ``remoteStorage.remote.configure({userAddress: -'user@host.com'})`` to set the user address. - -When a user address is set, but no other remote parameters are known -yet, WebFinger discovery will be triggered. From the WebFinger response, -the library extract the storage base URL, the storage API, and the OAuth -dialog URL. - -If no OAuth URL is given, Implied Auth is triggered: -https://github.com/remotestorage/remotestorage.js/issues/782 - -If an OAuth URL is known, but no token yet, the OAuth dance will be -started by setting the ``location.href`` of the window, redirecting -the user to that URL. When the dance comes back, the library will detect -the ``access_token`` from the window location during the page load, and -from that point onwards, the remote is connected. - -If the OAuth flow is PKCE, the window location will contain a ``code`` -parameter instead of ``access_token``. RS then makes a fetch to -remote.TOKEN_URL with the code, to retrieve the access token, and possibly -a refresh token as well. - -.. _storage-first section: https://tools.ietf.org/html/draft-dejong-remotestorage-09#section-11 diff --git a/doc/contributing/release-checklist.rst b/doc/contributing/release-checklist.rst deleted file mode 100644 index c6f35ea52..000000000 --- a/doc/contributing/release-checklist.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. highlight:: console - -Release checklist -================= - -* Build library and manually test all browsers you have access to, including - mobile devices and private browsing mode - -* Create changelog since last release - - * Collect and summarize changes using e.g.:: - - $ git log --no-merges ..HEAD - - * Add changes to `CHANGELOG.md` - * Commit to Git - -* Run ``npm version patch|minor|major|x.x.x-rc1``. This will automatically: - - * run the test suite - * update the version in package.json - * create a release build - * commit everything using version as commit description - * create a Git tag for the version - * push the release commit and tag to GitHub - -* Publish release notes on GitHub - - * Go to https://github.com/remotestorage/remotestorage.js/tags and click "Add release notes" - * Use version string as title and changelog items as description - * For RCs and betas, tick the "This is a pre-release" option on the bottom - * These notes will automatically be posted `to the community forums - ` - after a while - -* Publish to npm (https://www.npmjs.org/package/remotestoragejs):: - - $ npm publish - -* Update https://github.com/remotestorage/myfavoritedrinks to use new release - - * Replace ``remotestorage.js`` file with new release build - * Check if everything is still working - * Commit - * ``git push origin`` - * ``git push 5apps master`` - -* Link release announcement on Mastodon (remoteStorage@kosmos.social). This - will automatically cross-post to Twitter and IRC. - -* If it's an important release, also notify the Unhosted mailing list diff --git a/doc/contributing/testing.rst b/doc/contributing/testing.rst deleted file mode 100644 index f1bbbf100..000000000 --- a/doc/contributing/testing.rst +++ /dev/null @@ -1,37 +0,0 @@ -Testing -======= - -Before contributing to remoteStorage.js, make sure your patch passes the test -suite, and your code style passes the code linting suite. - -We use the `Jaribu `_ framework for our -test suites and `JSHint `_ for linting. Both are set -as dev dependencies in ``package.json``, so after installing those via ``npm -install``, you can use the following command to run everything at once: - -.. CODE:: console - - $ npm run test - -Or you can use the Jaribu executable directly in order to run the suite for a -single file: - -.. CODE:: console - - $ ./node_modules/.bin/jaribu test/unit/cachinglayer-suite.js - -.. TIP:: - If you add ``./node_modules/.bin`` to your ``PATH``, you can call - executables in any npm project directly. For example in ``~/.bashrc``, add - the line ``export PATH=$PATH:./node_modules/.bin`` (and run ``source - ~/.bashrc`` to load that change in open terminal sessions). Then you can - just run ``jaribu test/unit/foo_suite.js``. - -Continous integration ---------------------- - -The rs.js test suite is run by GitHub Actions on every push to our repo on GitHub. -When you open a pull request, your code will be tested there, too. You can -check out the build status and history at -https://github.com/remotestorage/remotestorage.js/actions, and the CI settings in -``.github/workflows/test-and-lint.yml``. diff --git a/doc/data-modules.rst b/doc/data-modules.rst deleted file mode 100644 index a71e299ad..000000000 --- a/doc/data-modules.rst +++ /dev/null @@ -1,33 +0,0 @@ -Data modules -============ - -A core idea of the remoteStorage protocol is that the same data can be used by -multiple apps. Imagine creating a to-do in one app, and tracking time on it in -another. - -Traditional Web apps make this possible with custom, proprietary APIs, which are -dependent on the app provider. Third party developers usually need to -register an application with the original provider to access that data via the -API, and this access can be revoked at any time. remoteStorage and `unhosted web apps -`_ in general give end users ultimate control over which -apps have access to their data. - -In order to make it easy and safe for your app data to be compatible with other -apps, we created the concept of data modules for rs.js, which can be shared and -developed collaboratively in the open. - -Data modules can contain as much or as little functionality as desired, from -defining data formats and types to data validation, formatting, processing, -transformation, encryption, indexing, and other use cases. - -Data modules make your app and its data interoperable with other apps. Sharing -your modules is generally encouraged, but it is also possible to encapsulate custom -functionality in a custom module made just for your app. - -.. toctree:: - :caption: Contents - :maxdepth: 2 - - data-modules/defining-a-module - data-modules/defining-data-types - data-modules/publishing-and-finding-modules diff --git a/doc/data-modules/defining-a-module.rst b/doc/data-modules/defining-a-module.rst deleted file mode 100644 index 0206d8165..000000000 --- a/doc/data-modules/defining-a-module.rst +++ /dev/null @@ -1,34 +0,0 @@ -Defining a module -================= - -A data module is just a JavaScript object containing a module name and a -builder function. - -The builder function receives two :doc:`base clients ` -when loaded: one for private data stored in ``/my-module-name/`` and one for -public data stored in ``/public/my-module-name/``. It must return an object, -defining the properties and functions to be used in the app as ``exports``: - -.. CODE:: javascript - - var Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { - return { - exports: { - addBookmark: function() {} - } - } - }}; - -You can then load it into your :doc:`RemoteStorage ` -instance either on initialization, or later using the ``addModule()`` function:: - - const remoteStorage = new RemoteStorage({ modules: [ Bookmarks ] }); - - // or later: - - remoteStorage.addModule(Bookmarks); - -It will then be available on the instance as its module name, allowing you to -call the functions and properties that the module exports:: - - remoteStorage.bookmarks.addBookmark(); diff --git a/doc/data-modules/defining-data-types.rst b/doc/data-modules/defining-data-types.rst deleted file mode 100644 index fad62ca7b..000000000 --- a/doc/data-modules/defining-data-types.rst +++ /dev/null @@ -1,84 +0,0 @@ -Defining data types -=================== - -Data types can be defined using the ``declareType()`` method. It expects a name -(which you can later use with ``storeObject()``), as well as a `JSON Schema`_ -object defining the actual structure and formatting of your data. - -Consider this simplified example of an archive bookmark: - -.. CODE:: javascript - - var Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { - - privateClient.declareType('archive-bookmark', { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "tags": { - "type": "array", - "default": [] - }, - }, - "required": [ "title", "url" ] - }); - - // ... - }}; - -Now that we have a basic data type in place for storing bookmarks, we can add a -function for storing them. This will actually validate the incoming data -against the type's schema, and reject the promise with detailed validation -errors in case the data format doesn't match:: - - var Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { - // ... - - return { - exports: { - - add: function (bookmark) { - bookmark.id = md5Hash(bookmark.url); // hash URL for nice ID - var path = "archive/" + bookmark.id; // use hashed URL as filename as well - - return privateClient.storeObject("archive-bookmark", path, bookmark). - then(function() { - return bookmark; // return bookmark with added ID property - }); - } - - } - } - }}; - - // and in your app: - - remoteStorage.bookmarks.add({ - title: 'Unhosted Web Apps', - url: 'https://unhosted.org', - tags: ['unhosted', 'remotestorage', 'offline-first'] - }) - .then(() => { - console.log('stored bookmark successfully'); - }) - .catch((err) => { - console.error('validation error:', err); - }); - -.. HINT:: - JSON Schema is very powerful and flexible. If you want to learn more about - it, check out the free e-book `Understanding JSON - Schema `_ for - example. The complete official specs can be found at - https://json-schema.org/specification - -.. _JSON Schema: http://json-schema.org diff --git a/doc/data-modules/publishing-and-finding-modules.rst b/doc/data-modules/publishing-and-finding-modules.rst deleted file mode 100644 index 71b710706..000000000 --- a/doc/data-modules/publishing-and-finding-modules.rst +++ /dev/null @@ -1,49 +0,0 @@ -Publishing and finding data modules -=================================== - -npm ---- - -The recommended way for publishing data modules is as `npm -`_ packages. - -Our naming convention for rs.js modules is -``remotestorage-module-mymodulename``. Thus, you can also find them by -`searching npm for "remotestorage-module" -`_. - -You can also add "remotestorage-module" and "remotestorage" to the ``keywords`` -property of your ``package.json``. - -GitHub & Co. ------------- - -If you use GitHub ‒ or any other code hosting/collaboration platform for that -matter ‒ for publishing your module's source code, please use the same naming -convention as for the npm module for the repo name. And it's a good idea to add -the topic/tag/label "remotestorage-module" there as well, of course. - -https://github.com/topics/remotestorage-module - -.. HINT:: - With npm, you can also install modules directly from a Git repo or GitHub, - pointing to just the repo or a branch name, tag, or commit: - https://docs.npmjs.com/files/package.json#github-urls - -Examples --------- - -* For a real-world example of a data module package, see e.g. the shares module - `on GitHub `_ and `on - npm `_. Check out - ``webpack.config.js`` and the source code in ``index.js`` to see how it is - built and exported. - -.. NOTE:: - Unfortunately, we didn't have any package management for data modules before - rs.js 1.0. To be fair, JavaScript package managers weren't actually a thing - yet, when this functionality was added to the library. However, it means - we're still in the process of porting and publishing modules and you won't - find very many existing data modules on npm right now. You can check the - `old modules repo `_ for source - code of legacy modules. diff --git a/doc/environment.yml b/doc/environment.yml deleted file mode 100644 index 9ab438093..000000000 --- a/doc/environment.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: rtd_env -channels: - - conda-forge - - defaults -dependencies: - - nodejs=14.15.1 - - pip=21.0.1 - - python=3.8.5 - - pip: - - MarkupSafe == 2.0.1 - - "git+https://github.com/hoodmane/sphinx-js@typedoc-0.17#egg=sphinx-js" - - sphinx-issues - - sphinx-autobuild - - sphinx_rtd_theme diff --git a/doc/getting-started.rst b/doc/getting-started.rst deleted file mode 100644 index a5bccaad3..000000000 --- a/doc/getting-started.rst +++ /dev/null @@ -1,15 +0,0 @@ -Getting started -=============== - -This section contains introductory documentation for app developers who are new -to *remoteStorage.js*. - -.. toctree:: - :maxdepth: 2 - - getting-started/how-to-add.rst - getting-started/initialize-and-configure.rst - getting-started/connect-widget.rst - getting-started/events.rst - getting-started/dropbox-and-google-drive.rst - getting-started/read-and-write-data.rst diff --git a/doc/getting-started/connect-widget.rst b/doc/getting-started/connect-widget.rst deleted file mode 100644 index 286e1adb2..000000000 --- a/doc/getting-started/connect-widget.rst +++ /dev/null @@ -1,57 +0,0 @@ -Using the Connect Widget add-on -=============================== - -The easiest option for letting people connect their storage to your app is using -the Connect Widget add-on library, which is written and maintained by the rs.js -core team. - -This is optional and an easy way to integrate all functionality into your own UI. -It's a great way to start with RS app development and, can be replaced with -custom code later on. - -.. HINT:: - If you haven't seen the widget in action yet, you can try it out e.g. with - `My Favorite Drinks `_ right now. - -Adding the library ------------------- - -The Connect Widget library is distributed the same way as *remoteStorage.js* -itself: as a :abbr:`UMD (Universal Module Definition)` build, compatible with -all JavaScript module systems, or as a global variable named ``Widget``, when -linked directly. - -You can find the connect widget as ``remotestorage-widget`` on npm, and its -source code and usage instructions `on GitHub -`_. - -Check out :doc:`Adding rs.js to an app ` for -examples of loading a UMD module in your code. - -Adding the widget ------------------ - -With the ``Widget`` class loaded, just create a new widget instance using the -:doc:`previously initialized ` -``remoteStorage`` instance, like so:: - - const widget = new Widget(remoteStorage); - -Then you can attach the widget to the DOM:: - - widget.attach(); - -Or if you want to attach it to a specific parent element, you can also hand it -a DOM element ID:: - - widget.attach('my-parent-element-id'); - -That's it! Now your users can use the widget in order to connect their storage, -and you can listen to the ``remoteStorage`` instance's events in order to get -informed about connection status, sync progress, errors, and so on. - -.. TIP:: - If you'd like to implement connect functionality in your own user interface - and code, the widget can serve as a useful source code example. For - everything it does, the Connect Widget only uses public APIs and events of - rs.js, which you can also use in your own code. diff --git a/doc/getting-started/dropbox-and-google-drive.rst b/doc/getting-started/dropbox-and-google-drive.rst deleted file mode 100644 index 0ef78433a..000000000 --- a/doc/getting-started/dropbox-and-google-drive.rst +++ /dev/null @@ -1,78 +0,0 @@ -Offering Dropbox and Google Drive storage options -================================================= - -.. image:: ../_images/screenshot-widget-choose.png - :width: 195px - :align: right - :alt: Screenshot of the connect-widget choose-backend screen - -rs.js has optional support for syncing data with Dropbox and Google Drive -instead of a RemoteStorage server. - -There are a few drawbacks, mostly sync performance and the lack of a permission -model. So apps can usually access all of a user's storage with these backends -(vs. only relevant parts of the storage with RS accounts). However, while RS -is not a widely known and deployed protocol, we find it helpful to let users -choose something they already know, and potentially migrate to an RS account -later on. - -For these additional backends to work, you will have to register your app with -Dropbox and/or Google first. Then you can configure your OAuth app ID/key like -so:: - - remoteStorage.setApiKeys({ - dropbox: 'your-app-key', - googledrive: 'your-client-id' - }); - -.. HINT:: - The :doc:`Connect widget` - will automatically show only the available storage options, based on the - presence of the `dropbox` and `googledrive` API keys. RemoteStorage is always - enabled. - -Dropbox -------- - -An app key can be obtained by `registering your app -`_. - -* Create a new "scoped" app for the "Dropbox API", with these scopes: - * account_info.read - * files.metadata.read - * files.metadata.write - * files.content.read - * files.content.write -* You need to set one or more OAuth2 redirect URIs for all routes a user can - connect from, for example ``http://localhost:8000`` for an app you are - developing locally. If the path is '/', rs.js drops it. - -Known issues -^^^^^^^^^^^^ - -* Storing files larger than 150MB is not yet supported -* Listing and deleting folders with more than 10000 files will cause problems -* Content-Type is not fully supported due to limitations of the Dropbox API -* Dropbox preserves cases but is not case-sensitive -* ``getItemURL`` is not implemented yet (see issue :issue:`1052`) - -Google Drive ------------- - -A client ID can be obtained by registering your app in the `Google Developers -Console `_. - -* Create an API, then add credentials for Google Drive API. Specify you will be - calling the API from a "Web browser (Javascript)" project. Select that you - want to access "User data". -* On the next screen, fill out the Authorized JavaScript origins and Authorized - redirect URIs for your app (for every route a user can connect from, same as - with Dropbox) -* Once your app is running in production, you will want to get verified by - Google to avoid a security warning when the user first connects their account - -Known issues -^^^^^^^^^^^^ - -* Sharing public files is not supported yet (see issue :issue:`1051`) -* ``getItemURL`` is not implemented yet (see issue :issue:`1054`) diff --git a/doc/getting-started/events.rst b/doc/getting-started/events.rst deleted file mode 100644 index dddc4700f..000000000 --- a/doc/getting-started/events.rst +++ /dev/null @@ -1,31 +0,0 @@ -Handling events -=============== - -In order to get informed about users connecting their storage, data being -transferred, the library going into offline mode, errors being thrown, and -other such things, you can listen to the events emitted by the -``RemoteStorage`` instance, as well as ``BaseClient`` instances. - -Simply register your event handler functions using the ``.on()`` method, like -so:: - - remoteStorage.on('connected', () => { - const userAddress = remoteStorage.remote.userAddress; - console.debug(`${userAddress} connected their remote storage.`); - }) - - remoteStorage.on('network-offline', () => { - console.debug(`We're offline now.`); - }) - - remoteStorage.on('network-online', () => { - console.debug(`Hooray, we're back online.`); - }) - -Check out the :doc:`RemoteStorage API doc` for a -complete list of events and when they're emitted. - -Also check out *Change events* in the :doc:`BaseClient API -doc`, which you can use to handle incoming data and -changes from the remote storage server. - diff --git a/doc/getting-started/how-to-add.rst b/doc/getting-started/how-to-add.rst deleted file mode 100644 index 898eaabd5..000000000 --- a/doc/getting-started/how-to-add.rst +++ /dev/null @@ -1,92 +0,0 @@ -Adding rs.js to an app -====================== - -rs.js is distributed as a single :abbr:`UMD (Universal Module -Definition)` build, which means it should work with all known JavaScript module -systems, as well as without one (using a global variable). - -We recommend adding the library from a JavaScript package manager, although you -may also just download the release build `from GitHub -`_. - -The package is available on `npm `_ as -``remotestoragejs``: - -.. code:: console - - $ npm install remotestoragejs - -Examples --------- - -ES6 module -^^^^^^^^^^ - -.. code:: javascript - - import RemoteStorage from 'remotestoragejs'; - -CommonJS module -^^^^^^^^^^^^^^^ - -.. code:: javascript - - var RemoteStorage = require('remotestoragejs'); - -AMD module -^^^^^^^^^^ - -For example with `RequireJS `_: - -.. code:: javascript - - requirejs.config({ - paths: { - RemoteStorage: './lib/remotestorage' - } - }); - - requirejs(['RemoteStorage'], function(RemoteStorage) { - // Here goes my app - }); - -No module system -^^^^^^^^^^^^^^^^ - -If you just link the build from HTML, it will add ``RemoteStorage`` as a global -variable to ``window``. - -.. code-block:: html - - - -Ember.js -^^^^^^^^ - -ES6 modules from npm should be supported natively soon, but for now you can use -`Browserify `_ via `ember-browserify -`_, enabling you to import the -module from npm like this: - -.. code:: javascript - - import RemoteStorage from 'npm:remotestoragejs'; - -Caveat emptor (no promises) ---------------------------- - -Please be aware of the fact that although rs.js is generally -compatible with older browsers as well as the latest ones, we do not include a -`polyfill `_ for JavaScript Promises -anymore. - -This means that, if you do not add your own polyfill, and no other library in -your build comes with one, rs.js will break in browsers, which do not support -Promises. A detailed overview of supported browsers is available `on -caniuse.com `_. Notable examples would be -Android up to 4.4 and Internet Explorer up to 11. - -You can find a list of polyfill libraries `on the Promises website -`_. A good choice for a small and -simple polyfill would be `es6-promise-auto -`_ for example. diff --git a/doc/getting-started/initialize-and-configure.rst b/doc/getting-started/initialize-and-configure.rst deleted file mode 100644 index 0aee50f1e..000000000 --- a/doc/getting-started/initialize-and-configure.rst +++ /dev/null @@ -1,73 +0,0 @@ -Initialization & configuration -============================== - -Now that you've imported the ``RemoteStorage`` class, here's how you typically -set things up. - -.. NOTE:: - Where and how you do this exactly will naturally depend on the rest of your - code, your JS framework, and personal preferences. - -.. role:: raw-html(raw) - :format: html - -Initializing an instance ------------------------- - -First step is to initialize a ``remoteStorage`` instance:: - - const remoteStorage = new RemoteStorage(); - -The constructor optionally takes a configuration object. Let's say we want to -enable debug logging to see in the console what rs.js is doing behind the -scenes:: - - const remoteStorage = new RemoteStorage({logging: true}); - -Or perhaps we're building an app that doesn't need local caching, but only -operates on the remote server/account:: - - const remoteStorage = new RemoteStorage({cache: false}); - -:raw-html:`` -Also see the :doc:`RemoteStorage API doc `. -:raw-html:`` - -Claiming access ---------------- - -Next, we need to tell rs.js which parts of the user's storage we want to -access. Let's say we want to read and write a user's favorite drinks, which -they might have added via the `My Favorite Drinks -`_ demo app:: - - remoteStorage.access.claim('myfavoritedrinks', 'rw'); - -Now, when they connect their storage, users will be asked to give the app -read/write access to the ``myfavoritedrinks/`` folder. And that's also what the -OAuth token, which we receive from their storage server, will be valid for, of -course. - -If you want to build a special app, like for example a backup utility, or a -data browser, you can also claim access to the entire storage (which is -generally discouraged):: - - remoteStorage.access.claim('*', 'rw'); - -:raw-html:`` -Also see the :doc:`Access API doc `. -:raw-html:`` - -Configuring caching -------------------- - -Last but not least, we'll usually want to configure caching (and with it -automatic sync) for the data we're accessing. The ``caching.enable()`` method -will activate full caching for the given path, meaning all of the items therein -will be automatically synced with the server:: - - remoteStorage.caching.enable('/myfavoritedrinks/') - -:raw-html:`` -See the :doc:`Caching API doc ` for details and options. -:raw-html:`` diff --git a/doc/getting-started/read-and-write-data.rst b/doc/getting-started/read-and-write-data.rst deleted file mode 100644 index d4cb86856..000000000 --- a/doc/getting-started/read-and-write-data.rst +++ /dev/null @@ -1,54 +0,0 @@ -Reading and writing data -======================== - -As soon as your :doc:`RemoteStorage ` instance is ready -for action (signaled by the ``ready`` event), we can start reading and writing -data. - -"Anonymous mode" ----------------- - -One of the unique features of rs.js is that users are not required to have -their storage connected in order to use the app; you can require connecting -storage if it fits your use case. Any data written locally is automatically -synced to the remote storage server when connecting an account. - -Using BaseClient ----------------- - -A ``BaseClient`` instance is the main endpoint for interacting with storage: -listing, reading, creating, updating and deleting documents, as well as -handling change events. - -Check out the :doc:`BaseClient API docs ` in order to -learn about all functions available for reading and writing data and how to use -them. - -There are two options for acquiring a BaseClient instance: - -Quick and dirty: creating a client via ``scope()`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This should mainly be used for manually exploring client functions and in -development. Using :func:`scope`, you can create a new BaseClient scoped to a -given path:: - - const client = remoteStorage.scope('/foo/'); - - // List all items in the "foo/" category/folder - client.getListing('') - .then(listing => console.log(listing)); - - // Write some text to "foo/bar.txt" - const content = 'The most simple things can bring the most happiness.' - client.storeFile('text/plain', 'bar.txt', content) - .then(() => console.log("data has been saved")); - -The recommended way: using clients in data modules -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The recommended way is to use the private and public ``BaseClient`` instances, -which are available in so-called :doc:`data modules `. Continue -to the next section in order to learn about them. - -.. rubric:: Footnotes diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 60c0967c8..000000000 --- a/doc/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. image:: rs-icon.svg - :height: 80px - :width: 80px - :align: right - -remoteStorage.js -================ - -Welcome to the remoteStorage.js documentation! - -remoteStorage.js is a JavaScript library for storing user data locally in the -browser, as well as connecting to `remoteStorage `_ -servers and syncing data across devices and applications. It is also capable of -connecting and syncing data with a person's Dropbox or Google Drive account -(optional). - -.. NOTE:: - For brevity's sake, we will also use the short name rs.js across these docs. - -.. toctree:: - :maxdepth: 2 - :caption: Contents - - why - getting-started - data-modules - js-api - nodejs - cordova - typescript - contributing - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/js-api.rst b/doc/js-api.rst deleted file mode 100644 index f1988a049..000000000 --- a/doc/js-api.rst +++ /dev/null @@ -1,11 +0,0 @@ -JavaScript API -============== - -.. toctree:: - :caption: Contents - :maxdepth: 2 - - js-api/remotestorage - js-api/base-client - js-api/access - js-api/caching diff --git a/doc/js-api/access.rst b/doc/js-api/access.rst deleted file mode 100644 index 3bbfd3b35..000000000 --- a/doc/js-api/access.rst +++ /dev/null @@ -1,26 +0,0 @@ -Access -====== - -This class is for requesting and managing access to modules/folders on -the remote. It gets initialized as ``remoteStorage.access``. - -List of functions ------------------ - -.. autofunction:: Access#claim(scope, mode) - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.access.claim('contacts', 'r'); - remoteStorage.access.claim('pictures', 'rw'); - - Claiming root access, meaning complete access to all files and folders - of a storage, can be done using an asterisk for the scope: - - .. code:: javascript - - remoteStorage.access.claim('*', 'rw'); - diff --git a/doc/js-api/base-client.rst b/doc/js-api/base-client.rst deleted file mode 100644 index c7f0c9020..000000000 --- a/doc/js-api/base-client.rst +++ /dev/null @@ -1,496 +0,0 @@ -BaseClient -========== - -A ``BaseClient`` instance is the main endpoint you will use for interacting -with a connected storage: listing, reading, creating, updating and deleting -documents, as well as handling incoming changes. - -Base clients are usually used in :doc:`data modules `, which are -loaded with two ``BaseClient`` instances by default: one for private and one for -public documents. - -However, you can also instantiate a BaseClient outside of a data module using -the ``remoteStorage.scope()`` function. Similarly, you can create a new scoped -client within another client, using the ``BaseClient``'s own :func:`scope`. - -.. contents:: - -Data read/write operations --------------------------- - -A ``BaseClient`` deals with three types of data: folders, objects and files: - -* :func:`getListing` returns a mapping of all items within a folder. - -* :func:`getObject` and :func:`storeObject` operate on JSON objects. Each object - has a type. - -* :func:`getFile` and :func:`storeFile` operates on files. Each file has a MIME - type. - -* :func:`getAll` returns all objects or files for the given folder path. - -* :func:`remove` operates on either objects or files (but not folders; folders - are created and removed implictly). - -.. _max-age: - -Caching logic for read operations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -All functions requesting/reading data will immediately return data from the -local store, *as long as it is reasonably up-to-date*. The default maximum age -of requested data is two times the periodic sync interval (10 seconds by -default). - -However, you can adjust this behavior by using the ``maxAge`` argument with any -of these functions, thereby changing the maximum age or removing the -requirement entirely. - -* If the ``maxAge`` requirement is set, and the last sync request for the path - is further in the past than the maximum age given, the folder will first be - checked for changes on the remote, and then the promise will be fulfilled - with the up-to-date document or listing. - -* If the ``maxAge`` requirement is set, and cannot be met because of network - problems, the promise will be rejected. - -* If the ``maxAge`` requirement is set to ``false``, or the library is in - offline mode, or no remote storage is connected (a.k.a. "anonymous mode"), - the promise will always be fulfilled with data from the local store. - -.. HINT:: - If :doc:`caching ` for the folder is turned off, none of - this applies and data will always be requested from the remote store - directly. - -List of functions -^^^^^^^^^^^^^^^^^ - -.. autofunction:: BaseClient#getListing(path, maxAge) - :short-name: - - Example usage: - - .. code:: javascript - - client.getListing('') - .then(listing => console.log(listing)); - - The folder listing is returned as a JSON object, with the root keys - representing the pathnames of child nodes. Keys ending in a forward slash - represent *folder nodes* (subdirectories), while all other keys represent - *data nodes* (files/objects). - - Data node information contains the item's ETag, content type and -length. - - Example of a listing object: - - .. code:: javascript - - { - "@context": "http://remotestorage.io/spec/folder-description", - "items": { - "thumbnails/": true, - "screenshot-20170902-1913.png": { - "ETag": "6749fcb9eef3f9e46bb537ed020aeece", - "Content-Length": 53698, - "Content-Type": "image/png;charset=binary" - }, - "screenshot-20170823-0142.png": { - "ETag": "92ab84792ef3f9e46bb537edac9bc3a1", - "Content-Length": 412401, - "Content-Type": "image/png;charset=binary" - } - } - } - - .. WARNING:: - At the moment, this function only returns detailed metadata, when caching - is turned off (``new RemoteStorage({cache: false})``). With caching turned - on, it will only contain the item names as properties with ``true`` as - value. See issues :issue:`721` and :issue:`1108` — contributions - welcome! - -.. autofunction:: BaseClient#getObject(path, maxAge) - :short-name: - - Example: - - .. code:: javascript - - client.getObject('/path/to/object') - .then(obj => console.log(obj)); - -.. autofunction:: BaseClient#storeObject(typeAlias, path, object) - :short-name: - - Example: - - .. code:: javascript - - var bookmark = { - url: 'http://unhosted.org', - description: 'Unhosted Adventures', - tags: ['unhosted', 'remotestorage', 'no-backend'] - } - var path = MD5Hash(bookmark.url); - - client.storeObject('bookmark', path, bookmark) - .then(() => console.log('bookmark saved')) - .catch((err) => console.log(err)); - -.. autofunction:: BaseClient#getAll(path, maxAge) - :short-name: - - Example usage: - - .. code:: javascript - - client.getAll('example-subdirectory/').then(objects => { - for (var path in objects) { - console.log(path, objects[path]); - } - }); - - Example response object: - - .. code:: javascript - - { - "27b8dc16483734625fff9de653a14e03": { - "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", - "id": "27b8dc16483734625fff9de653a14e03", - "url": "https://unhosted.org/", - "title": "Unhosted Web Apps", - "description": "Freedom from web 2.0's monopoly platforms", - "tags": [ - "unhosted", - "remotestorage" - ], - "createdAt": "2017-11-02T15:22:25.289Z", - "updatedAt": "2019-11-07T17:52:22.643Z" - }, - "900a5ca174bf57c56b79af0653053bdc": { - "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", - "id": "900a5ca174bf57c56b79af0653053bdc", - "url": "https://remotestorage.io/", - "title": "remoteStorage", - "description": "An open protocol for per-user storage on the Web", - "tags": [ - "unhosted", - "remotestorage" - ], - "createdAt": "2019-11-07T17:59:34.883Z" - } - } - - .. HINT:: - - For items that are not JSON-stringified objects (for example stored using - :func:`storeFile` instead of :func:`storeObject`), the object's value is - filled in with ``true``. - -.. autofunction:: BaseClient#getFile(path, maxAge) - :short-name: - - The response object contains two properties: - - .. table:: - - ============ ============================================================ - ``mimeType`` String representing the MIME Type of the document. - ``data`` Raw data of the document (either a string or an ArrayBuffer) - ============ ============================================================ - - Example usage (displaying an image): - - .. code:: javascript - - client.getFile('path/to/some/image').then(file => { - var blob = new Blob([file.data], { type: file.mimeType }); - var targetElement = document.findElementById('my-image-element'); - targetElement.src = window.URL.createObjectURL(blob); - }); - -.. autofunction:: BaseClient#storeFile(mimeType, path, body) - :short-name: - - Example (UTF-8 data): - - .. code:: javascript - - client.storeFile('text/html', 'index.html', '

Hello World!

') - .then(() => { console.log("Upload done") }); - - Example (Binary data): - - .. code:: javascript - - var input = document.querySelector('form#upload input[type=file]'); - var file = input.files[0]; - var fileReader = new FileReader(); - - fileReader.onload = function () { - client.storeFile(file.type, file.name, fileReader.result) - .then(() => { console.log("Upload done") }); - }; - - fileReader.readAsArrayBuffer(file); - -.. autofunction:: BaseClient#remove(path) - :short-name: - - Example: - - .. code:: javascript - - client.remove('path/to/object') - .then(() => console.log('item successfully deleted')); - -Change events -------------- - -``BaseClient`` offers a single event, named ``change``, which you can add a -handler for using the ``.on()`` function (same as in ``RemoteStorage``): - -.. code:: javascript - - client.on('change', function (evt) { - console.log('data was added, updated, or removed:', evt) - }); - -Using this event, you can stay informed about data changes, both remote (from -other devices or browsers), as well as locally (e.g. other browser tabs). - -In order to determine where a change originated from, look at the ``origin`` -property of the incoming event. Possible values are ``window``, ``local``, -``remote``, and ``conflict``, explained in detail below. - -Example: - -.. code:: javascript - - { - // Absolute path of the changed node, from the storage root - path: path, - // Path of the changed node, relative to this baseclient's scope root - relativePath: relativePath, - // See origin descriptions below - origin: 'window|local|remote|conflict', - // Old body of the changed node (local version in conflicts; undefined if creation) - oldValue: oldBody, - // New body of the changed node (remote version in conflicts; undefined if deletion) - newValue: newBody, - // Body when local and remote last agreed; only present in conflict events - lastCommonValue: lastCommonBody, - // Old contentType of the changed node (local version for conflicts; undefined if creation) - oldContentType: oldContentType, - // New contentType of the changed node (remote version for conflicts; undefined if deletion) - newContentType: newContentType, - // ContentType when local and remote last agreed; only present in conflict events - lastCommonContentType: lastCommonContentType - } - -In general, the dataType of the document can be extracted thus: - -.. code:: javascript - - const context = evt.newValue?.['@context'] || evt.oldValue?.['@context'] || evt.lastCommonValue?.['@context']; - -``local`` -^^^^^^^^^ - -Events with origin ``local`` are fired conveniently during the page load, so -that you can fill your views when the page loads. - -Example: - -.. code:: javascript - - { - path: '/public/design/color.txt', - relativePath: 'color.txt', - origin: 'local', - oldValue: undefined, - newValue: 'white', - oldContentType: undefined, - newContentType: 'text/plain' - } - -.. HINT:: - You may also use for example :func:`getAll` instead, and choose to - deactivate these. - -``remote`` -^^^^^^^^^^ - -Events with origin ``remote`` are fired when remote changes are discovered -during sync. - -.. NOTE:: - Automatically receiving remote changes depends on the :doc:`caching - ` settings for your module/paths. - -``window`` -^^^^^^^^^^ - -Events with origin `window` are fired whenever you change a value by calling a -method on the ``BaseClient``; these are disabled by default. - -.. HINT:: - You can enable them by configuring ``changeEvents`` for your - :doc:`RemoteStorage ` instance. - -``conflict`` -^^^^^^^^^^^^ - -Events with origin ``conflict`` are fired when a conflict occurs while pushing -out your local changes to the remote store. - -Say you changed 'color.txt' from 'white' to 'blue'; if you have set -``config.changeEvents.window`` to ``true`` (by passing it to the -``RemoteStorage`` constructor, see the Constructor section of the -:doc:`RemoteStorage API doc `), then you will receive: - -.. code:: javascript - - { - path: '/public/design/color.txt', - relativePath: 'color.txt', - origin: 'window', - oldValue: 'white', - newValue: 'blue', - oldContentType: 'text/plain', - newContentType: 'text/plain' - } - -But when this change is pushed out by asynchronous synchronization, this change -may be rejected by the server, if the remote version has in the meantime changed -from 'white' to for instance 'red'; this will then lead to a change event with -origin 'conflict' (usually a few seconds after the event with origin 'window', -if you have those activated). Note that since you already changed it from -'white' to 'blue' in the local version a few seconds ago, ``oldValue`` is now -your local value of 'blue': - -.. code:: javascript - - { - path: '/public/design/color.txt', - relativePath: 'color.txt', - origin: 'conflict', - oldValue: 'blue', - newValue: 'red', - oldContentType: 'text/plain', - newContentType: 'text/plain', - // Most recent known common ancestor body of local and remote - lastCommonValue: 'white', - // Most recent known common ancestor contentType of local and remote - lastCommonContentType: 'text/plain' - } - -Conflict Resolution -""""""""""""""""""" - -Conflicts are resolved by calling :func:`storeObject` or :func:`storeFile` on -the device where the conflict surfaced. Other devices are not aware of the -conflict. - -If there is an algorithm to merge the differences between local and remote -versions of the data, conflicts may be automatically resolved. -:func:`storeObject` or :func:`storeFile` must not be called synchronously from -the change event handler, nor by chaining Promises. :func:`storeObject` or -:func:`storeFile` must not be called until the next iteration of the JavaScript -Task Queue, using for example `setTimeout()`_. - -If no algorithm exists, conflict resolution typically involves displaying local -and remote versions to the user, and having the user merge them, or choose -which version to keep. - -.. _setTimeout(): https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout - -Data types ----------- - -.. autofunction:: BaseClient#declareType(alias, uri, schema) - :short-name: - - Example: - - .. code:: javascript - - client.declareType('todo-item', { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "finished": { - "type": "boolean" - "default": false - }, - "createdAt": { - "type": "date" - } - }, - "required": ["id", "title"] - }) - - Visit ``_ for details on how - to use JSON Schema. - -.. autofunction:: BaseClient#validate(object) - :short-name: - - - Example: - - .. code:: javascript - - var result = client.validate(document); - - // result: - // { - // error: null, - // missing: [], - // valid: true - // } - - -Caching -------- - -.. autofunction:: BaseClient#cache(path, strategy) - :short-name: - - Example: - - .. code:: javascript - - client.cache('documents/', 'ALL'); - -.. autofunction:: BaseClient#flush(path) - :short-name: - - Example: - - .. code:: javascript - - client.flush('documents/'); - -Other functions ---------------- - -.. autofunction:: BaseClient#getItemURL(path) - :short-name: - - .. WARNING:: - This method currently only works for remoteStorage - backends. The issues for implementing it for Dropbox and Google - Drive can be found at :issue:`1052` and :issue:`1054`. - -.. autofunction:: BaseClient#scope(path) - :short-name: diff --git a/doc/js-api/caching.rst b/doc/js-api/caching.rst deleted file mode 100644 index 17c6ab4bc..000000000 --- a/doc/js-api/caching.rst +++ /dev/null @@ -1,93 +0,0 @@ -Caching -======= - -The caching class gets initialized as ``remoteStorage.caching``, unless the -:doc:`RemoteStorage ` instance is created with the -option ``cache: false``, disabling caching entirely. - -In case your app hasn't explictly configured caching, the default setting is to -cache any documents that have been either created or requested since your app -loaded. For offline-capable apps, it usually makes sense to enable full, -automatic caching of all documents, which is what :func:`enable` will do. - -Enabling full caching has several benefits: - -* Speed of access: locally cached data is available to the app a lot faster. -* Offline mode: when all data is cached, it can also be read when your app - starts while being offline. -* Initial synchronization time: the amount of data your app caches can - have a significant impact on its startup time. - -Caching can be configured on a per-path basis. When caching is enabled for a -folder, it causes all subdirectories to be cached as well. - -.. _caching-strategies: - -Caching strategies ------------------- - -For each subtree, you can set the caching strategy to ``ALL``, ``SEEN`` -(default), and ``FLUSH``. - -* ``ALL`` means that once all outgoing changes have been pushed, sync will - start retrieving nodes to cache pro-actively. If a local copy exists - of everything, it will check on each sync whether the ETag of the root - folder changed, and retrieve remote changes if they exist. -* ``SEEN`` does this only for documents and folders that have been either - read from or written to at least once since connecting to the current - remote backend, plus their parent/ancestor folders up to the root (to - make tree-based sync possible). -* ``FLUSH`` will only cache outgoing changes, and forget them as soon as - they have been saved to remote successfully. - -List of functions -------------------- - -.. autofunction:: Caching#enable(path) - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.caching.enable('/bookmarks/'); - -.. autofunction:: Caching#disable(path) - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.caching.disable('/bookmarks/'); - -.. autofunction:: Caching#set(path, strategy) - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.caching.set('/bookmarks/archive/', 'SEEN'); - -.. autofunction:: Caching#checkPath(path) - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.caching.checkPath('documents/').then(strategy => { - console.log(`caching strategy for 'documents/': ${strategy}`)); - // "caching strategy for 'documents/': SEEN" - }); - -.. autofunction:: Caching#reset - :short-name: - - Example: - - .. code:: javascript - - remoteStorage.caching.reset(); - diff --git a/doc/js-api/remotestorage.rst b/doc/js-api/remotestorage.rst deleted file mode 100644 index c8022ada5..000000000 --- a/doc/js-api/remotestorage.rst +++ /dev/null @@ -1,314 +0,0 @@ -RemoteStorage -============= - -Constructor ------------ - -Create a ``remoteStorage`` class instance like so:: - - const remoteStorage = new RemoteStorage(); - -The constructor can optionally be called with a configuration object. This -example shows all default values:: - - const remoteStorage = new RemoteStorage({ - cache: true, - changeEvents: { - local: true, - window: false, - remote: true, - conflict: true - }, - cordovaRedirectUri: undefined, - logging: false, - modules: [] - }); - -.. NOTE:: - In the current version, it is only possible to use a single - ``RemoteStorage`` instance. You cannot connect to two different remotes yet. - We intend to support this eventually (see issue :issue:`991`) - -.. WARNING:: - For the change events configuration, you currently have to set all events - explicitly. Otherwise it would disable the unspecified ones. (see issue - :issue:`1025`) - -Events ------- - -You can handle events from your ``remoteStorage`` instance by using the -``.on()`` function. For example:: - - remoteStorage.on('connected', function() { - // Storage account has been connected, let’s roll! - }); - -.. _rs-events: - -List of events -^^^^^^^^^^^^^^ - -``ready`` -""""""""" - Emitted when all features are loaded and the RS instance is ready - -``not-connected`` -""""""""""""""""" - Emitted when ready, but no storage connected ("anonymous mode") - -``connected`` -""""""""""""" - Emitted when a remote storage has been connected - -``disconnected`` -"""""""""""""""" - Emitted after disconnect - -``error`` -""""""""" - Emitted when an error occurs; receives an error object as argument - - There are a handful of known errors, which are identified by the ``name`` - property of the error object: - - .. table:: - - ================== ============================================================ - Name Description - ================== ============================================================ - ``Unauthorized`` Emitted when a network request resulted in a 401 or 403 - response. You can use this event to handle invalid OAuth - tokens in custom UI (i.e. when a stored token has been - revoked or expired by the RS server). - ``DiscoveryError`` A variety of storage discovery errors, e.g. from user - address input validation, or user address lookup issues - ================== ============================================================ - - Example:: - - remoteStorage.on('error', err => console.log(err)); - - // { - // name: "Unauthorized", - // message: "App authorization expired or revoked.", - // stack: "Error↵ at new a.Unauthorized (vendor.js:65710:41870)" - // } - -``features-loaded`` -""""""""""""""""""" - Emitted when all features are loaded - -``connecting`` -"""""""""""""" - Emitted before webfinger lookup - -``authing`` -""""""""""" - Emitted before redirecting to the authing server - -``wire-busy`` -""""""""""""" - Emitted when a network request starts - -``wire-done`` -""""""""""""" - Emitted when a network request completes - -``sync-req-done`` -""""""""""""""""" - Emitted when a single sync request has finished. Callback functions - receive an object as argument, informing the client of remaining items - in the current sync task queue. - - Example:: - - remoteStorage.on('sync-req-done', result => console.log(result)); - - // { tasksRemaining: 21 } - - .. NOTE:: - The internal task queue holds at most 100 items at the same time, - regardless of the overall amount of items to sync. Therefore, this number - is only an indicator of sync status, not a precise amount of items left - to sync. It can be useful to determine if your app should display any - kind of sync status/progress information for the cycle or not. - -``sync-done`` -""""""""""""" - Emitted when a sync cycle has been completed and a new sync is scheduled. - - The callback function receives an object as argument, informing the client - if the sync process has completed successfully or not. - - Example:: - - remoteStorage.on('sync-done', result => console.log(result)); - - // { completed: true } - - If ``completed`` is ``false``, it means that some of the sync requests have - failed and will be retried in the next sync cycle (usually a few seconds - later in this case). This is not an unusual scenario on mobile networks or - when doing a large initial sync for example. - - For an app's user interface, you may want to consider the sync process as - ongoing in this case, and wait until your app sees a positive ``completed`` - status before updating the UI. - -``network-offline`` -""""""""""""""""""" - Emitted once when a wire request fails for the first time, and - ``remote.online`` is set to false - -``network-online`` -"""""""""""""""""" - Emitted once when a wire request succeeds for the first time after a failed - one, and ``remote.online`` is set back to true - -``sync-interval-change`` -"""""""""""""""""""""""" - Emitted when the sync interval changes - -List of functions ------------------ - -The following methods/functions can be called on your ``remoteStorage`` instance: - -.. autofunction:: RemoteStorage#connect(userAddress, [token]) - :short-name: - - Example:: - - remoteStorage.connect('user@example.com'); - -.. autofunction:: RemoteStorage#disconnect - :short-name: - - Example:: - - remoteStorage.disconnect(); - -.. autofunction:: RemoteStorage#enableLog - :short-name: - - Example:: - - remoteStorage.enableLog(); - -.. autofunction:: RemoteStorage#disableLog - :short-name: - - Example:: - - remoteStorage.disableLog(); - -.. autofunction:: RemoteStorage#getSyncInterval - :short-name: - - Example:: - - remoteStorage.getSyncInterval(); - // 10000 - -.. autofunction:: RemoteStorage#setSyncInterval(interval) - :short-name: - - Example:: - - remoteStorage.setSyncInterval(10000); - -.. autofunction:: RemoteStorage#getBackgroundSyncInterval - :short-name: - - Example:: - - remoteStorage.getBackgroundSyncInterval(); - // 60000 - -.. autofunction:: RemoteStorage#setBackgroundSyncInterval(interval) - :short-name: - - Example:: - - remoteStorage.setBackgroundSyncInterval(60000); - -.. autofunction:: RemoteStorage#getCurrentSyncInterval - :short-name: - - Example:: - - remoteStorage.getCurrentSyncInterval(); - // 15000 - -.. autofunction:: RemoteStorage#getRequestTimeout - :short-name: - - Example:: - - remoteStorage.getRequestTimeout(); - // 30000 - -.. autofunction:: RemoteStorage#setRequestTimeout(timeout) - :short-name: - - Example:: - - remoteStorage.setRequestTimeout(30000); - -.. autofunction:: RemoteStorage#scope(path) - :short-name: - - Example:: - - remoteStorage.scope('/pictures/').getListing(''); - remoteStorage.scope('/public/pictures/').getListing(''); - -.. autofunction:: RemoteStorage#setApiKeys(apiKeys) - :short-name: - - Example:: - - remoteStorage.setApiKeys({ - dropbox: 'your-app-key', - googledrive: 'your-client-id' - }); - -.. autofunction:: RemoteStorage#setCordovaRedirectUri(uri) - :short-name: - - Example:: - - remoteStorage.setCordovaRedirectUri('https://app.wow-much-app.com'); - -.. autofunction:: RemoteStorage#startSync - :short-name: - - Example:: - - remoteStorage.startSync(); - -.. autofunction:: RemoteStorage#stopSync - :short-name: - - Example:: - - remoteStorage.stopSync(); - -.. autofunction:: RemoteStorage#on(eventName, handler) - :short-name: - - Example:: - - remoteStorage.on('connected', function() { - console.log('user connected their storage'); - }); - -.. autofunction:: RemoteStorage#onChange(path, handler) - :short-name: - - Example:: - - remoteStorage.onChange('/bookmarks/', function() { - // your code here - }) diff --git a/doc/legacy.rst b/doc/legacy.rst deleted file mode 100644 index 8df5c402e..000000000 --- a/doc/legacy.rst +++ /dev/null @@ -1,12 +0,0 @@ -Legacy docs -=========== - -These documents have been converted from the old Markdown files in the repo's -``doc/`` folder, but have not yet been updated, rewritten, deleted, or -integrated into the new docs. In short: TO DO, PLZ HELP. ;) - -.. toctree:: - :maxdepth: 2 - :glob: - - legacy/* diff --git a/doc/legacy/json-ld.rst b/doc/legacy/json-ld.rst deleted file mode 100644 index 97d701678..000000000 --- a/doc/legacy/json-ld.rst +++ /dev/null @@ -1,33 +0,0 @@ -Data types -========== - -.. ATTENTION:: - This is 5 years old and needs a better explanation of JSON-LD. Also, we need - to introduce ``@type`` alongside the existing ``@context`` property, which - we're currently (mis)using for specific data types. And we should also think - about not scoping types in module names. - -A great thing about having data on the web, is to be able to link to it and -rearrange it to fit the current circumstances. To facilitate that, eventually -you need to know how the data at hand is structured. For documents on the web, -this is usually done via a MIME type. The MIME type of JSON objects however, is -always application/json. To add that extra layer of "knowing what this object -is", remoteStorage aims to use `JSON-LD `_. - -A first step in that direction is to add a ``@context`` attribute to all JSON -data put into remoteStorage. This is what the *type* is for. - -Within remoteStorage.js, ``@context`` values are built using three components: - -``http://remotestorage.io/spec/modules/`` - A prefix to guarantee uniqueness - -The module name - Module names should be unique as well - -The type name (given as argument to ``declareType()``) - Naming the particular kind of object within its module - -In retrospect, this means that whenever you introduce a new *type* in calls to -``storeObject()``, you should make sure that once your code is in the wild, -future versions of the code are compatible with the same JSON structure. diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index e18d468e0..000000000 --- a/doc/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=python -msphinx -) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=remoteStoragejs - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The Sphinx module was not found. Make sure you have Sphinx installed, - echo.then set the SPHINXBUILD environment variable to point to the full - echo.path of the 'sphinx-build' executable. Alternatively you may add the - echo.Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/doc/nodejs.rst b/doc/nodejs.rst deleted file mode 100644 index 95576ef78..000000000 --- a/doc/nodejs.rst +++ /dev/null @@ -1,86 +0,0 @@ -Usage with Node.js -================== - -Although remoteStorage.js was initially written for being used in browsers, we -do support using it in a Node.js environment as well. See :doc:`this section -` for getting started. - -.. HINT:: - Node 18 includes ``fetch`` natively, but earlier versions do not, and so it - may be necessary to set ``global.fetch`` with a polyfill such as `node-fetch`_. - -.. _node-fetch: https://www.npmjs.com/package/node-fetch - -The main difference between rs.js in a browser and using it on a server or in a -CLI program is how to connect a storage. The RS protocol uses the OAuth -Implicit Grant flow for clients to receive a bearer token, which they can use -in HTTP requests. This works by redirecting back to the Web application with -the token attached to the redirect URI as a URI fragment. - -Now, with rs.js in a browser, calling -``remoteStorage.connect('user@example.com')`` will take care of the entire -OAuth process, including the parsing of the URI after the redirect, saving the -token to localStorage and changing the library's state to connected. But in a -node.js program, that's obviously not possible, because there's no browser that -will open the OAuth dialog and receive the redirect with the token attached to -the redirect URI. - -connect() with a token ----------------------- - -For this reason, among others, you can call the connect function with a token -that you acquired beforehand:: - - remoteStorage.connect('user@example.com', 'abcdefghijklmnopqrstuvwxyz') - -This will skip the entire OAuth process, because you did that before in some -other way, of course. - -Obtaining a token ------------------ - -For some programs, like e.g. a server daemon, you can usually acquire the token -from your server manually, and then just configure it for example as -environment variable, when running your program. - -For CLI programs, and if you actually want to integrate the OAuth flow in your -program, one possible solution is the following: - -1. Set up a simple Web site/app, which you publish under a fitting domain/URI - that you can use as the OAuth redirect URI. -2. Have the user enter their user address and do a Webfinger lookup for auth - URL etc., e.g. using `webfinger.js - `_. -3. Create the OAuth request URI with the correct scope etc., and open a browser - window with that URI from your program (or prompt the user to open it). -4. Have the Web app, which the user is being redirected to, show the token to - the user, in order for them to copy and enter in your program -5. Connect with that token. - -You can find a complete example for this process in `rs-backup`_, a -remoteStorage backup CLI program. In particular `its code for connecting a -storage `_ -and the `simple Web page `_ its -using for the redirect. - -.. HINT:: - rs-backup is not using remoteStorage.js at all, which you might also want to - consider as an option when writing non-browser applications. - -Caveats -------- - -* IndexedDB and localStorage are not supported by default in Node.js, so the - library will fall back to in-memory storage for caching data locally. This - means that unsynchronized data will be lost between sessions and program - executions. - -Examples --------- - -* `hubot-remotestorage-logger`_, a Hubot script that logs chat messages to - remoteStorage-enabled accounts using the `chat-messages`_ module - -.. _hubot-remotestorage-logger: https://github.com/67P/hubot-remotestorage-logger -.. _chat-messages: https://www.npmjs.com/package/remotestorage-module-chat-messages -.. _rs-backup: https://github.com/skddc/rs-backup diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 1f0f8457f..000000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -MarkupSafe == 2.0.1 -git+https://github.com/hoodmane/sphinx-js@typedoc-0.17#egg=sphinx-js -sphinx-issues -sphinx-autobuild -sphinx_rtd_theme diff --git a/doc/typescript.rst b/doc/typescript.rst deleted file mode 100644 index b2f708b59..000000000 --- a/doc/typescript.rst +++ /dev/null @@ -1,13 +0,0 @@ -Usage with TypeScript -===================== - -Since version ``2.0.0``, the source code of rs.js itself has been ported to -TypeScript, and release builds now ship with generated type definitions. Thus, -there is no extra type definitions package to import/require, and everything -should just work out of the box. - -.. NOTE:: - - There's still a lot of room for improvement in our TypeScript usage. If - you're experienced with TypeScript, and interested in contributing to rs.js, - your help in this area would be most appreciated. diff --git a/doc/version.py b/doc/version.py deleted file mode 100644 index 4450a145d..000000000 --- a/doc/version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = '2.0.0-beta.6' diff --git a/doc/why.rst b/doc/why.rst deleted file mode 100644 index 54a73bbd1..000000000 --- a/doc/why.rst +++ /dev/null @@ -1,73 +0,0 @@ -Why use this? -============= - -Offline-first design --------------------- - -rs.js stores data locally first and syncs data with a remote storage account -second. This makes it a robust sync solution for mobile applications, where -slow and spotty network connections are a normal situation. - -Apps and use -cases that don't require caching (e.g. `Sharesome `_) -can keep selective data locally while not caching the rest. - -When a backend goes down, users can just keep using the app and have their data -automatically synced as soon as the server is back online. - -Zero backend ------------- - -rs.js is built for creating `unhosted`_ apps. Users can connect their own -storage account to apps on their devices, without needing to trust app -developers with private data. Developers can rapidly build apps without -investing in integrating, managing, maintaining, or securing data. - -A nice side effect of this design is that your app can scale to millions of -users with literally *zero* cost for storage. - -Also, if an app goes offline or is abandoned, people can continue to use -it across devices until they switch to a new one at their own pace. If an -abandoned app comes back at some point, many active users may still be there. - -.. _unhosted: https://remotestorage.io/#explainer-unhosted - -Data sharing ------------- - -Different apps can access the same data, so you can build an app that uses and -manipulates existing data, without building import/export features or having -users start over from scratch. - -Even better, you can get advanced capabilities for free by using shared, -open-source :doc:`data modules `, which you can cooperate on -with other developers. - -For example: enable the sharing of files by simply integrating the `shares module`_ -within a matter of minutes, giving you client-side thumbnail generation and other -features in the process. - -.. _shares module: https://github.com/skddc/remotestorage-module-shares - -Reliability ------------ - -The first prototype of rs.js was written in November 2010. Since then, it has -been used, tested, stabilized, and improved in over 4000 commits. The library -has been used in commercial apps by hundreds of thousands of users, and in -countries across the globe. Bugs and issues have been noted and fixed for -virtually every device, browser, privacy setting and network connection there is. - -In short: you can rely on rs.js to do its job. And if you do find a critical -bug, there's a team of people who will help with fixing it. - -One JS API for multiple storage options ---------------------------------------- - -rs.js optionally supports Dropbox and Google Drive as storage backends which -users can connect. Conveniently, as an app developer you don't have to -implement anything special in order for these backends to work with your code. -Just :doc:`configure OAuth app keys `, -and your users can choose between 3 different backends to connect. If you're not -using the :doc:`connect widget`, you may need to -create additional UI for these alternate backends. diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts new file mode 100644 index 000000000..af588bddf --- /dev/null +++ b/docs/.vitepress/config.mts @@ -0,0 +1,93 @@ +import { defineConfig } from 'vitepress' + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + title: "Documentation", + description: "", + ignoreDeadLinks: [ + /^http:\/\/localhost/, + '/unhosted' + ], + + themeConfig: { + // https://vitepress.dev/reference/default-theme-config + externalLinkIcon: true, + outline: { level: [2, 3] }, + + nav: [ + { text: 'remoteStorage.js', link: '/' }, + ], + + sidebar: [ + { + text: 'remoteStorage.js', link: '/', + items: [ + ] + }, + { + text: 'Guide', + items: [ + { text: 'Why use this?', link: '/why' }, + { + text: 'Getting started', link: '/getting-started/', + collapsed: true, + items: [ + { text: 'Adding rs.js to an app', link: '/getting-started/how-to-add' }, + { text: 'Initialization & configuration', link: '/getting-started/initialize-and-configure' }, + { text: 'Using the connect widget add-on', link: '/getting-started/connect-widget' }, + { text: 'Handling events', link: '/getting-started/events' }, + { text: 'Reading and writing data', link: '/getting-started/read-and-write-data' }, + { text: 'Loading data on launch', link: '/getting-started/loading-data' }, + ] + }, + { + text: 'Data modules', link: '/data-modules/', + collapsed: true, + items: [ + { text: 'Defining a module', link: '/data-modules/defining-a-module' }, + { text: 'Defining data types', link: '/data-modules/defining-data-types' }, + { text: 'Publishing and finding modules', link: '/data-modules/publishing-and-finding-modules' }, + ] + }, + { text: 'Usage with Node.js', link: '/nodejs' }, + { text: 'Usage in Cordova apps', link: '/cordova' }, + { text: 'Usage with TypeScript', link: '/typescript' }, + { text: 'Dropbox and Google Drive', link: '/dropbox-and-google-drive' }, + ] + }, + { + text: 'JavaScript API', + items: [ + { text: 'RemoteStorage', link: '/api/remotestorage/classes/RemoteStorage' }, + { text: 'BaseClient', link: '/api/baseclient/classes/BaseClient' }, + { text: 'Access', link: '/api/access/classes/Access' }, + { text: 'Caching', link: '/api/caching/classes/Caching' }, + ] + }, + { + text: 'Contributing', link: '/contributing/', + collapsed: true, + items: [ + { text: 'Building', link: '/contributing/building' }, + { text: 'Testing', link: '/contributing/testing' }, + { text: 'Documentation', link: '/contributing/docs' }, + { text: 'GitHub workflow', link: '/contributing/github-flow' }, + { text: 'Release checklist', link: '/contributing/release-checklist' }, + { text: 'Library internals', link: '/contributing/internals/', + collapsed: true, + items: [ + { text: 'Code overview', link: '/contributing/internals/code-overview' }, + { text: 'Discovery bootstrap', link: '/contributing/internals/discovery-bootstrap' }, + { text: 'Caching', link: '/contributing/internals/caching' }, + { text: 'Data format of the local cache', link: '/contributing/internals/cache-data-format' }, + ] + }, + ] + }, + ], + + socialLinks: [ + { icon: 'github', link: 'https://github.com/remotestorage/remotestorage.js' } + ] + } +}) diff --git a/docs/api/access/classes/Access.md b/docs/api/access/classes/Access.md new file mode 100644 index 000000000..cc58707ad --- /dev/null +++ b/docs/api/access/classes/Access.md @@ -0,0 +1,43 @@ +# Class: Access + +This class is for requesting and managing access to modules/folders on the +remote. It gets initialized as `remoteStorage.access`. + +## Methods + +### claim() + +> **claim**(`scope`, `mode`): `void` + +Claim access on a given scope with given mode. + +#### Parameters + +• **scope**: `string` + +An access scope, such as `contacts` or `calendar` + +• **mode**: `AccessMode` + +Access mode. Either `r` for read-only or `rw` for read/write + +#### Returns + +`void` + +#### Example + +```javascript +remoteStorage.access.claim('contacts', 'r'); +remoteStorage.access.claim('pictures', 'rw'); +``` + +Claiming root access, meaning complete access to all files and folders of a storage, can be done using an asterisk for the scope: + +```javascript +remoteStorage.access.claim('*', 'rw'); +``` + +#### Defined in + +[access.ts:73](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/access.ts#L73) diff --git a/docs/api/access/index.md b/docs/api/access/index.md new file mode 100644 index 000000000..627addbec --- /dev/null +++ b/docs/api/access/index.md @@ -0,0 +1,7 @@ +# access + +## Index + +### Classes + +- [Access](classes/Access.md) diff --git a/docs/api/baseclient/classes/BaseClient.md b/docs/api/baseclient/classes/BaseClient.md new file mode 100644 index 000000000..48cbb3949 --- /dev/null +++ b/docs/api/baseclient/classes/BaseClient.md @@ -0,0 +1,865 @@ +# Class: BaseClient + +A `BaseClient` instance is the main endpoint you will use for interacting +with a connected storage: listing, reading, creating, updating and deleting +documents, as well as handling incoming changes. + +Base clients are usually used in [data modules](../../../data-modules/), +which are loaded with two `BaseClient` instances by default: one for private +and one for public documents. + +However, you can also instantiate a BaseClient outside of a data module using +the `remoteStorage.scope()` function. Similarly, you can create a new scoped +client within another client, using the `BaseClient`'s own [scope](BaseClient.md#scope). + +## Read/write operations + +A `BaseClient` deals with three types of data: folders, objects and files. + +* [getListing](BaseClient.md#getlisting) returns a mapping of all items within a folder. + +* [getObject](BaseClient.md#getobject) and [storeObject](BaseClient.md#storeobject) operate on JSON objects. Each object + has a type. + +* [getFile](BaseClient.md#getfile) and [storeFile](BaseClient.md#storefile) operates on files. Each file has a MIME + type. + +* [getAll](BaseClient.md#getall) returns all objects or files for the given folder path. + +* [remove](BaseClient.md#remove) operates on either objects or files (but not folders; folders + are created and removed implictly). + +## Caching logic for read operations + +All functions requesting/reading data will immediately return data from the +local store, *as long as it is reasonably up-to-date*. If data is assumed to be +potentially outdated, they will check the remote storage for changes first, and then +return the requested data. + +The default maximum age of requested data is two times the periodic sync +interval (10 seconds by default). + +However, you can adjust this behavior by using the `maxAge` argument with any +of these functions, thereby changing the maximum age or removing the +requirement entirely. + +* If the `maxAge` requirement is set, and the last sync request for the path + is further in the past than the maximum age given, the folder will first be + checked for changes on the remote, and then the promise will be fulfilled + with the up-to-date document or listing. + +* If the `maxAge` requirement is set, and cannot be met because of network + problems, the promise will be rejected. + +* If the `maxAge` requirement is set to `false`, or the library is in + offline mode, or no remote storage is connected (a.k.a. "anonymous mode"), + the promise will always be fulfilled with data from the local store. + +> [!NOTE] +> If [caching](../../caching/classes/Caching.md) for the folder is turned off, none of +> this applies and data will always be requested from the remote store +> directly. + +## Change events + +A `BaseClient` emits only one type of event, named `change`, which you can add +a handler for using the `.on()` function (same as with [RemoteStorage](../../remotestorage/classes/RemoteStorage.md)): + +```js +client.on('change', function (evt) { + console.log('data was added, updated, or removed:', evt) +}); +``` + +Using this event, your app can stay informed about data changes, both remote +(from other devices or browsers), as well as locally (e.g. other browser tabs). + +In order to determine where a change originated from, look at the `origin` +property of the event. Possible values are `window`, `local`, `remote`, and +`conflict`, explained in detail below. + +#### Example + +```js +{ + // Absolute path of the changed node, from the storage root + path: path, + // Path of the changed node, relative to this baseclient's scope root + relativePath: relativePath, + // See origin descriptions below + origin: 'window|local|remote|conflict', + // Old body of the changed node (local version in conflicts; undefined if creation) + oldValue: oldBody, + // New body of the changed node (remote version in conflicts; undefined if deletion) + newValue: newBody, + // Body when local and remote last agreed; only present in conflict events + lastCommonValue: lastCommonBody, + // Old contentType of the changed node (local version for conflicts; undefined if creation) + oldContentType: oldContentType, + // New contentType of the changed node (remote version for conflicts; undefined if deletion) + newContentType: newContentType, + // ContentType when local and remote last agreed; only present in conflict events + lastCommonContentType: lastCommonContentType +} +``` + +### `local` + +Events with origin `local` are fired conveniently during the page load, so +that you can fill your views when the page loads. + +Example: + +```js +{ + path: '/public/design/color.txt', + relativePath: 'color.txt', + origin: 'local', + oldValue: undefined, + newValue: 'white', + oldContentType: undefined, + newContentType: 'text/plain' +} +``` + +> [!TIP] +> You may also use for example [getAll](BaseClient.md#getall) instead, and choose to +> deactivate these. + +### `remote` + +Events with origin `remote` are fired when remote changes are discovered +during sync. + +> [!NOTE] +> Automatically receiving remote changes depends on the [caching!Caching](../../caching/classes/Caching.md) settings +> for your module/paths. + +### `window` + +Events with origin `window` are fired whenever you change a value by calling a +method on the `BaseClient`; these are disabled by default. + +> [!TIP] +> You can enable them by configuring `changeEvents` for your +> [remoteStorage](../../remotestorage/classes/RemoteStorage.md) instance. + +### `conflict` + +Events with origin `conflict` are fired when a conflict occurs while pushing +out your local changes to the remote store. + +Let's say you changed the content of `color.txt` from `white` to `blue`; if +you have set `config.changeEvents.window` to `true` for your RemoteStorage instance, then you will receive: + +```js +{ + path: '/public/design/color.txt', + relativePath: 'color.txt', + origin: 'window', + oldValue: 'white', + newValue: 'blue', + oldContentType: 'text/plain', + newContentType: 'text/plain' +} +``` + +But when this change is pushed out by asynchronous synchronization, this change +may be rejected by the server, if the remote version has in the meantime changed +from `white` to for instance `red`; this will then lead to a change event with +origin `conflict` (usually a few seconds after the event with origin `window`, +if you have those activated). Note that since you already changed it from +`white` to `blue` in the local version a few seconds ago, `oldValue` is now +your local value of `blue`: + +```js +{ + path: '/public/design/color.txt', + relativePath: 'color.txt', + origin: 'conflict', + oldValue: 'blue', + newValue: 'red', + oldContentType: 'text/plain', + newContentType: 'text/plain', + // Most recent known common ancestor body of local and remote + lastCommonValue: 'white', + // Most recent known common ancestor contentType of local and remote + lastCommonContentType: 'text/plain' +} +``` + +#### Conflict Resolution + +Conflicts are resolved by calling [storeObject](BaseClient.md#storeobject) or [storeFile](BaseClient.md#storefile) on +the device where the conflict surfaced. Other devices are not aware of the +conflict. + +If there is an algorithm to merge the differences between local and remote +versions of the data, conflicts may be automatically resolved. +[storeObject](BaseClient.md#storeobject) or [storeFile](BaseClient.md#storefile) must not be called synchronously from +the change event handler, nor by chaining Promises. [storeObject](BaseClient.md#storeobject) or +[storeFile](BaseClient.md#storefile) must not be called until the next iteration of the JavaScript +Task Queue, using for example +[`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). + +If no algorithm exists, conflict resolution typically involves displaying local +and remote versions to the user, and having the user merge them, or choose +which version to keep. + +## Extends + +- `EventHandling` + +## Properties + +### base + +> **base**: `string` + +Base path, which this [BaseClient](BaseClient.md) operates on. + +For the module's `privateClient` this would be the module name, and for the +corresponding `publicClient` it is `/public//`. + +#### Defined in + +[baseclient.ts:239](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L239) + +## Methods + +### addEventListener() + +> **addEventListener**(`eventName`, `handler`): `void` + +Install an event handler for the given event name + +Usually called via [`on()`](#on) + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Defined in + +[eventhandling.ts:29](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L29) + +*** + +### cache() + +> **cache**(`path`, `strategy`): [`BaseClient`](BaseClient.md) + +Set caching strategy for a given path and its children. + +See [Caching strategies](../../caching/classes/Caching.html#caching-strategies) +for a detailed description of the available strategies. + +#### Parameters + +• **path**: `string` + +Path to cache + +• **strategy**: `"ALL"` \| `"SEEN"` \| `"FLUSH"` = `'ALL'` + +Caching strategy. One of 'ALL', 'SEEN', or FLUSH'. + Defaults to 'ALL'. + +#### Returns + +[`BaseClient`](BaseClient.md) + +The same `BaseClient` instance this method is called on to allow + for method chaining + +#### Example + +```ts +client.cache('lists/', 'SEEN'); +``` + +#### Defined in + +[baseclient.ts:683](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L683) + +*** + +### declareType() + +> **declareType**(`alias`, `uriOrSchema`, `schema`?): `void` + +Declare a remoteStorage object type using a JSON Schema. Visit +[json-schema.org](http://json-schema.org) for details. + +See [Defining data types](../../../data-modules/defining-data-types) for more info. + +#### Parameters + +• **alias**: `string` + +A type alias/shortname + +• **uriOrSchema**: `string` \| `JsonSchema` + +JSON-LD URI of the schema, or a JSON Schema object. + The URI is automatically generated if none given. + +• **schema?**: `JsonSchema` + +(optional) A JSON Schema object describing the object type + +#### Returns + +`void` + +#### Example + +```ts +client.declareType('todo-item', { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "finished": { + "type": "boolean" + "default": false + }, + "createdAt": { + "type": "date" + } + }, + "required": ["id", "title"] +}) +``` + +#### Defined in + +[baseclient.ts:733](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L733) + +*** + +### getAll() + +> **getAll**(`path`?, `maxAge`?): `Promise`\<`unknown`\> + +Get all objects directly below a given path. + +#### Parameters + +• **path?**: `string` + +(optional) Path to the folder. Must end in a forward slash. + +• **maxAge?**: `number` \| `false` + +(optional) Either `false` or the maximum age of cached + objects in milliseconds. See [caching logic for read + operations](#caching-logic-for-read-operations). + +#### Returns + +`Promise`\<`unknown`\> + +A promise for a collection of items + +#### Example + +```js +client.getAll('example-subdirectory/').then(objects => { + for (var path in objects) { + console.log(path, objects[path]); + } +}); +``` + +Example response: + +```js +{ + "27b8dc16483734625fff9de653a14e03": { + "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", + "id": "27b8dc16483734625fff9de653a14e03", + "url": "https://unhosted.org/", + "title": "Unhosted Web Apps", + "description": "Freedom from web 2.0's monopoly platforms", + "tags": [ + "unhosted", + "remotestorage" + ], + "createdAt": "2017-11-02T15:22:25.289Z", + "updatedAt": "2019-11-07T17:52:22.643Z" + }, + "900a5ca174bf57c56b79af0653053bdc": { + "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", + "id": "900a5ca174bf57c56b79af0653053bdc", + "url": "https://remotestorage.io/", + "title": "remoteStorage", + "description": "An open protocol for per-user storage on the Web", + "tags": [ + "unhosted", + "remotestorage" + ], + "createdAt": "2019-11-07T17:59:34.883Z" + } +} +``` +> [!NOTE] +> For items that are not JSON-stringified objects (for example stored using +> [storeFile](BaseClient.md#storefile) instead of [storeObject](BaseClient.md#storeobject)), the object's value is +> filled in with `true`. + +#### Defined in + +[baseclient.ts:395](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L395) + +*** + +### getFile() + +> **getFile**(`path`, `maxAge`?): `Promise`\<`unknown`\> + +Get the file at the given path. A file is raw data, as opposed to +a JSON object (use [getObject](BaseClient.md#getobject) for that). + +#### Parameters + +• **path**: `string` + +Relative path from the module root (without leading slash). + +• **maxAge?**: `number` \| `false` + +(optional) Either ``false`` or the maximum age of + the cached file in milliseconds. See [caching logic for read + operations](#caching-logic-for-read-operations). + +#### Returns + +`Promise`\<`unknown`\> + +An object containing the content type as well as the file's content: + +* `mimeType`
+ String representing the MIME Type of the document. +* `data`
+ Raw data of the document (either a string or an ArrayBuffer) + +#### Example + +Displaying an image: + +```js +client.getFile('path/to/some/image').then(file => { + const blob = new Blob([file.data], { type: file.mimeType }); + const targetElement = document.findElementById('my-image-element'); + targetElement.src = window.URL.createObjectURL(blob); +}); +``` + +#### Defined in + +[baseclient.ts:456](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L456) + +*** + +### getItemURL() + +> **getItemURL**(`path`): `string` + +Retrieve full URL of a document. Useful for example for sharing the public +URL of an item in the ``/public`` folder. + +#### Parameters + +• **path**: `string` + +Path relative to the module root. + +#### Returns + +`string` + +The full URL of the item, including the storage origin, or `undefined` + if no remote storage is connected + +> [!WARNING] +> This method currently only works for remoteStorage +> backends. The GitHub issues for implementing it for Dropbox and Google +> are 1052 and 1054. + +#### Defined in + +[baseclient.ts:655](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L655) + +*** + +### getListing() + +> **getListing**(`path`?, `maxAge`?): `Promise`\<`unknown`\> + +Get a list of child nodes below a given path. + +#### Parameters + +• **path?**: `string` + +The path to query. It must end with a forward slash. + +• **maxAge?**: `number` \| `false` + +(optional) Either `false` or the maximum age of cached + listing in milliseconds. See [caching logic for read + operations](#caching-logic-for-read-operations). + +#### Returns + +`Promise`\<`unknown`\> + +A promise for a folder listing object + +#### Example + +```js +client.getListing().then(listing => console.log(listing)); +``` + +The folder listing is returned as a JSON object, with the root keys +representing the pathnames of child nodes. Keys ending in a forward slash +represent _folder nodes_ (subdirectories), while all other keys represent +_data nodes_ (files/objects). + +Data node information contains the item's ETag, content type and -length. + +Example of a listing object: + +```js +{ + "@context": "http://remotestorage.io/spec/folder-description", + "items": { + "thumbnails/": true, + "screenshot-20170902-1913.png": { + "ETag": "6749fcb9eef3f9e46bb537ed020aeece", + "Content-Length": 53698, + "Content-Type": "image/png;charset=binary" + }, + "screenshot-20170823-0142.png": { + "ETag": "92ab84792ef3f9e46bb537edac9bc3a1", + "Content-Length": 412401, + "Content-Type": "image/png;charset=binary" + } + } +} +``` + +> [!WARNING] +> At the moment, this function only returns detailed metadata, when +> caching is turned off. With caching turned on, it will only contain the +> item names as properties with `true` as value. See issues 721 and 1108 — +> contributions welcome! + +#### Defined in + +[baseclient.ts:326](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L326) + +*** + +### getObject() + +> **getObject**(`path`, `maxAge`?): `Promise`\<`unknown`\> + +Get a JSON object from the given path. + +#### Parameters + +• **path**: `string` + +Relative path from the module root (without leading slash). + +• **maxAge?**: `number` \| `false` + +(optional) Either `false` or the maximum age of + cached object in milliseconds. See [caching logic for read + operations](#caching-logic-for-read-operations). + +#### Returns + +`Promise`\<`unknown`\> + +A promise, resolving with the requested object, or `null` if non-existent + +#### Example + +```ts +client.getObject('/path/to/object').then(obj => console.log(obj)); +``` + +#### Defined in + +[baseclient.ts:540](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L540) + +*** + +### on() + +> **on**(`eventName`, `handler`): `void` + +Register an event handler for the given event name + +Alias for [addEventListener](BaseClient.md#addeventlistener) + +#### Parameters + +• **eventName**: `string` + +Name of the event + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +Function to handle the event + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.on('connected', function() { + console.log('storage account has been connected'); +}); +``` + +#### Defined in + +[eventhandling.ts:55](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L55) + +*** + +### remove() + +> **remove**(`path`): `Promise`\<`unknown`\> + +Remove node at given path from storage. Triggers synchronization. + +#### Parameters + +• **path**: `string` + +Path relative to the module root. + +#### Returns + +`Promise`\<`unknown`\> + +#### Example + +```ts +client.remove('path/to/object').then(() => console.log('item deleted')); +``` + +#### Defined in + +[baseclient.ts:629](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L629) + +*** + +### removeEventListener() + +> **removeEventListener**(`eventName`, `handler`): `void` + +Remove a previously installed event handler + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Defined in + +[eventhandling.ts:62](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L62) + +*** + +### scope() + +> **scope**(`path`): [`BaseClient`](BaseClient.md) + +Instantiate a new client, scoped to a subpath of the current client's +path. + +#### Parameters + +• **path**: `string` + +The path to scope the new client to + +#### Returns + +[`BaseClient`](BaseClient.md) + +A new `BaseClient` operating on a subpath of the current base path + +#### Defined in + +[baseclient.ts:272](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L272) + +*** + +### storeFile() + +> **storeFile**(`mimeType`, `path`, `body`): `Promise`\<`string`\> + +Store raw data at a given path. + +#### Parameters + +• **mimeType**: `string` + +MIME media type of the data being stored + +• **path**: `string` + +Path relative to the module root + +• **body**: `string` \| `ArrayBuffer` \| `ArrayBufferView` + +Raw data to store + +#### Returns + +`Promise`\<`string`\> + +A promise for the created/updated revision (ETag) + +#### Example + +UTF-8 data: + +```js +client.storeFile('text/html', 'index.html', '

Hello World!

') + .then(() => { console.log("File saved") }); +``` + +Binary data: + +```js +const input = document.querySelector('form#upload input[type=file]'); +const file = input.files[0]; +const fileReader = new FileReader(); + +fileReader.onload = function () { + client.storeFile(file.type, file.name, fileReader.result) + .then(() => { console.log("File saved") }); +}; + +fileReader.readAsArrayBuffer(file); +``` + +#### Defined in + +[baseclient.ts:502](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L502) + +*** + +### storeObject() + +> **storeObject**(`typeAlias`, `path`, `object`): `Promise`\<`string`\> + +Store an object at given path. Triggers synchronization. See [declareType](BaseClient.md#declaretype) and +[Defining data types](../../../data-modules/defining-data-types) +for info on object types. + +Must not be called more than once per second for any given `path`. + +#### Parameters + +• **typeAlias**: `string` + +Unique type of this object within this module. + +• **path**: `string` + +Path relative to the module root. + +• **object**: `object` + +A JavaScript object to be stored at the given path. + Must be serializable as JSON. + +#### Returns + +`Promise`\<`string`\> + +Resolves with revision on success. Rejects with an error object, + if schema validations fail. + +#### Example + +```ts +const bookmark = { + url: 'http://unhosted.org', + description: 'Unhosted Adventures', + tags: ['unhosted', 'remotestorage', 'no-backend'] +} +const path = MD5Hash(bookmark.url); + +client.storeObject('bookmark', path, bookmark) + .then(() => console.log('bookmark saved')) + .catch((err) => console.log(err)); +``` + +#### Defined in + +[baseclient.ts:588](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L588) + +*** + +### validate() + +> **validate**(`object`): `object` + +Validate an object against the associated schema. + +#### Parameters + +• **object** + +JS object to validate. Must have a `@context` property. + +#### Returns + +`object` + +An object containing information about the validation result + +#### Example + +```ts +var result = client.validate(document); + +// result: +// { +// error: null, +// missing: [], +// valid: true +// } +``` + +#### Defined in + +[baseclient.ts:765](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/baseclient.ts#L765) diff --git a/docs/api/baseclient/index.md b/docs/api/baseclient/index.md new file mode 100644 index 000000000..d1816377a --- /dev/null +++ b/docs/api/baseclient/index.md @@ -0,0 +1,7 @@ +# baseclient + +## Index + +### Classes + +- [BaseClient](classes/BaseClient.md) diff --git a/docs/api/caching/classes/Caching.md b/docs/api/caching/classes/Caching.md new file mode 100644 index 000000000..04fb37dc4 --- /dev/null +++ b/docs/api/caching/classes/Caching.md @@ -0,0 +1,210 @@ +# Class: Caching + +The caching class gets initialized as `remoteStorage.caching`, unless the +[RemoteStorage](../../remotestorage/classes/RemoteStorage.md) instance is created with the option `cache: false`, disabling +caching entirely. + +In case your app hasn't explictly configured caching, the default setting is to +cache any documents that have been either created or requested since your app +loaded. For offline-capable apps, it usually makes sense to enable full, +automatic caching of all documents, which is what [enable](Caching.md#enable) will do. + +Enabling full caching has several benefits: + +* Speed of access: locally cached data is available to the app a lot faster. +* Offline mode: when all data is cached, it can also be read when your app + starts while being offline. +* Initial synchronization time: the amount of data your app caches can + have a significant impact on its startup time. + +Caching can be configured on a per-path basis. When caching is enabled for a +folder, it causes all subdirectories to be cached as well. + +## Caching strategies + +For each subtree, you can set the caching strategy to ``ALL``, ``SEEN`` +(default), and ``FLUSH``. + +* `ALL` means that once all outgoing changes have been pushed, sync will + start retrieving nodes to cache pro-actively. If a local copy exists + of everything, it will check on each sync whether the ETag of the root + folder changed, and retrieve remote changes if they exist. +* `SEEN` does this only for documents and folders that have been either + read from or written to at least once since connecting to the current + remote backend, plus their parent/ancestor folders up to the root (to + make tree-based sync possible). +* `FLUSH` will only cache outgoing changes, and forget them as soon as + they have been saved to remote successfully. + +## Methods + +### checkPath() + +> **checkPath**(`path`): `string` + +Retrieve caching setting for a given path, or its next parent +with a caching strategy set. + +#### Parameters + +• **path**: `string` + +Path to retrieve setting for + +#### Returns + +`string` + +caching strategy for the path + +#### Example + +```js +remoteStorage.caching.checkPath('documents/').then(strategy => { + console.log(`caching strategy for 'documents/': ${strategy}`)); + // "caching strategy for 'documents/': SEEN" +}); +``` + +#### Defined in + +[caching.ts:157](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L157) + +*** + +### disable() + +> **disable**(`path`): `void` + +Disable caching for a given path. + +Uses caching strategy ``FLUSH`` (meaning items are only cached until +successfully pushed to the remote). + +#### Parameters + +• **path**: `string` + +Path to disable caching for + +#### Returns + +`void` + +#### Example + +```js +remoteStorage.caching.disable('/bookmarks/'); +``` + +#### Defined in + +[caching.ts:124](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L124) + +*** + +### enable() + +> **enable**(`path`): `void` + +Enable caching for a given path. + +Uses caching strategy ``ALL``. + +#### Parameters + +• **path**: `string` + +Path to enable caching for + +#### Returns + +`void` + +#### Example + +```js +remoteStorage.caching.enable('/bookmarks/'); +``` + +#### Defined in + +[caching.ts:107](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L107) + +*** + +### onActivate() + +> **onActivate**(`cb`): `void` + +Set a callback for when caching is activated for a path. + +#### Parameters + +• **cb** + +Callback function + +#### Returns + +`void` + +#### Defined in + +[caching.ts:133](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L133) + +*** + +### reset() + +> **reset**(): `void` + +Reset the state of caching by deleting all caching information. + +#### Returns + +`void` + +#### Example + +```js +remoteStorage.caching.reset(); +``` + +#### Defined in + +[caching.ts:175](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L175) + +*** + +### set() + +> **set**(`path`, `strategy`): `void` + +Configure caching for a given path explicitly. + +Not needed when using ``enable``/``disable``. + +#### Parameters + +• **path**: `string` + +Path to cache + +• **strategy**: `"ALL"` \| `"SEEN"` \| `"FLUSH"` + +Caching strategy. One of 'ALL', 'SEEN', or 'FLUSH'. + +#### Returns + +`void` + +#### Example + +```js +remoteStorage.caching.set('/bookmarks/archive/', 'SEEN'); +``` + +#### Defined in + +[caching.ts:67](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/caching.ts#L67) diff --git a/docs/api/caching/index.md b/docs/api/caching/index.md new file mode 100644 index 000000000..d09e271f9 --- /dev/null +++ b/docs/api/caching/index.md @@ -0,0 +1,7 @@ +# caching + +## Index + +### Classes + +- [Caching](classes/Caching.md) diff --git a/docs/api/eventhandling/index.md b/docs/api/eventhandling/index.md new file mode 100644 index 000000000..efbb5ba4d --- /dev/null +++ b/docs/api/eventhandling/index.md @@ -0,0 +1,7 @@ +# eventhandling + +## Index + +### Type Aliases + +- [EventHandler](type-aliases/EventHandler.md) diff --git a/docs/api/eventhandling/type-aliases/EventHandler.md b/docs/api/eventhandling/type-aliases/EventHandler.md new file mode 100644 index 000000000..66aa9dee3 --- /dev/null +++ b/docs/api/eventhandling/type-aliases/EventHandler.md @@ -0,0 +1,15 @@ +# Type Alias: EventHandler() + +> **EventHandler**: (`event`?) => `void` + +## Parameters + +• **event?**: `unknown` + +## Returns + +`void` + +## Defined in + +[eventhandling.ts:5](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L5) diff --git a/docs/api/modules.md b/docs/api/modules.md new file mode 100644 index 000000000..b65d5e503 --- /dev/null +++ b/docs/api/modules.md @@ -0,0 +1,10 @@ +# remoteStorage.js v2.0.0-beta.6 + +## Modules + +- [access](access/index.md) +- [baseclient](baseclient/index.md) +- [caching](caching/index.md) +- [eventhandling](eventhandling/index.md) +- [remote](remote/index.md) +- [remotestorage](remotestorage/index.md) diff --git a/docs/api/remote/classes/RemoteBase.md b/docs/api/remote/classes/RemoteBase.md new file mode 100644 index 000000000..f94bc2193 --- /dev/null +++ b/docs/api/remote/classes/RemoteBase.md @@ -0,0 +1,101 @@ +# Class: RemoteBase + +The ancestor for WireClient, GoogleDrive & Dropbox + +## Extends + +- `EventHandling` + +## Methods + +### addEventListener() + +> **addEventListener**(`eventName`, `handler`): `void` + +Install an event handler for the given event name + +Usually called via [`on()`](#on) + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Inherited from + +`EventHandling.addEventListener` + +#### Defined in + +[eventhandling.ts:29](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L29) + +*** + +### on() + +> **on**(`eventName`, `handler`): `void` + +Register an event handler for the given event name + +Alias for [addEventListener](RemoteBase.md#addeventlistener) + +#### Parameters + +• **eventName**: `string` + +Name of the event + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +Function to handle the event + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.on('connected', function() { + console.log('storage account has been connected'); +}); +``` + +#### Inherited from + +`EventHandling.on` + +#### Defined in + +[eventhandling.ts:55](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L55) + +*** + +### removeEventListener() + +> **removeEventListener**(`eventName`, `handler`): `void` + +Remove a previously installed event handler + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Inherited from + +`EventHandling.removeEventListener` + +#### Defined in + +[eventhandling.ts:62](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L62) diff --git a/docs/api/remote/index.md b/docs/api/remote/index.md new file mode 100644 index 000000000..51518e58b --- /dev/null +++ b/docs/api/remote/index.md @@ -0,0 +1,11 @@ +# remote + +## Index + +### Classes + +- [RemoteBase](classes/RemoteBase.md) + +### Interfaces + +- [Remote](interfaces/Remote.md) diff --git a/docs/api/remote/interfaces/Remote.md b/docs/api/remote/interfaces/Remote.md new file mode 100644 index 000000000..96e4f3a8f --- /dev/null +++ b/docs/api/remote/interfaces/Remote.md @@ -0,0 +1,51 @@ +# Interface: Remote + +The public interface for WireClient, GoogleDrive & Dropbox + +## Properties + +### connected + +> **connected**: `boolean` + +Whether or not a remote store is connected + +#### Defined in + +[remote.ts:82](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remote.ts#L82) + +*** + +### online + +> **online**: `boolean` + +Whether last sync action was successful or not + +#### Defined in + +[remote.ts:87](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remote.ts#L87) + +*** + +### properties? + +> `optional` **properties**: `object` + +The JSON-parsed properties object from the user's WebFinger record + +#### Defined in + +[remote.ts:123](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remote.ts#L123) + +*** + +### userAddress + +> **userAddress**: `string` + +The user address of the connected user + +#### Defined in + +[remote.ts:92](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remote.ts#L92) diff --git a/docs/api/remotestorage/classes/RemoteStorage.md b/docs/api/remotestorage/classes/RemoteStorage.md new file mode 100644 index 000000000..0f1a3c245 --- /dev/null +++ b/docs/api/remotestorage/classes/RemoteStorage.md @@ -0,0 +1,876 @@ +# Class: RemoteStorage + +Create a `remoteStorage` class instance so: + +```js +const remoteStorage = new RemoteStorage(); +``` + +The constructor can optionally be called with a configuration object. This +example shows all default values: + +```js +const remoteStorage = new RemoteStorage({ + cache: true, + changeEvents: { + local: true, + window: false, + remote: true, + conflict: true + }, + cordovaRedirectUri: undefined, + logging: false, + modules: [] +}); +``` + +> [!NOTE] +> In the current version, it is only possible to use a single `remoteStorage` +> instance. You cannot connect to two different remotes in parallel yet. +> We intend to support this eventually. + +> [!TIP] +> For the change events configuration, you have to set all events +> explicitly. Otherwise it disables the unspecified ones. + +## Events + +You can add event handlers to your `remoteStorage` instance by using the +[on](RemoteStorage.md#on) function. For example: + +```js +remoteStorage.on('connected', function() { + // Storage account has been connected, let’s roll! +}); +``` + +### `ready` + +Emitted when all features are loaded and the RS instance is ready to be used +in your app + +### `not-connected` + +Emitted when ready, but no storage connected ("anonymous mode") + +### `connected` + +Emitted when a remote storage has been connected + +### `disconnected` + +Emitted after disconnect + +### `error` + +Emitted when an error occurs; receives an error object as argument + +There are a handful of known errors, which are identified by the `name` +property of the error object: + +* `Unauthorized` + + Emitted when a network request resulted in a 401 or 403 response. You can + use this event to handle invalid OAuth tokens in custom UI (i.e. when a + stored token has been revoked or expired by the RS server). + +* `DiscoveryError` + + A variety of storage discovery errors, e.g. from user address input + validation, or user address lookup issues + +#### Example + +```js +remoteStorage.on('error', err => console.log(err)); + +// { +// name: "Unauthorized", +// message: "App authorization expired or revoked.", +// stack: "Error↵ at new a.Unauthorized (vendor.js:65710:41870)" +// } +``` + +### `connecting` + +Emitted before webfinger lookup + +### `authing` + +Emitted before redirecting to the OAuth server + +### `wire-busy` + +Emitted when a network request starts + +### `wire-done` + +Emitted when a network request completes + +### `sync-req-done` + +Emitted when a single sync request has finished. Callback functions +receive an object as argument, informing the client of remaining items +in the current sync task queue. + +#### Example + +```js +remoteStorage.on('sync-req-done', result => console.log(result)); +// { tasksRemaining: 21 } +``` + +> [!NOTE] +> The internal task queue holds at most 100 items at the same time, +> regardless of the overall amount of items to sync. Therefore, this number +> is only an indicator of sync status, not a precise amount of items left +> to sync. It can be useful to determine if your app should display any +> kind of sync status/progress information for the cycle or not. + +### `sync-done` + +Emitted when a sync cycle has been completed and a new sync is scheduled. + +The callback function receives an object as argument, informing the client +if the sync process has completed successfully or not. + +#### Example + +```js +remoteStorage.on('sync-done', result => console.log(result)); +// { completed: true } +``` + +If `completed` is `false`, it means that some of the sync requests have +failed and will be retried in the next sync cycle (usually a few seconds +later in this case). This is not an unusual scenario on mobile networks or +when doing a large initial sync for example. + +For an app's user interface, you may want to consider the sync process as +ongoing in this case, and wait until your app sees a positive `completed` +status before updating the UI. + +### `network-offline` + +Emitted once when a wire request fails for the first time, and +`remote.online` is set to false + +### `network-online` + +Emitted once when a wire request succeeds for the first time after a failed +one, and `remote.online` is set back to true + +### `sync-interval-change` + +Emitted when the sync interval changes + +## Extends + +- `EventHandling` + +## Properties + +### access + +> **access**: [`Access`](../../access/classes/Access.md) + +#### Defined in + +[remotestorage.ts:295](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L295) + +*** + +### backend + +> **backend**: `"remotestorage"` \| `"googledrive"` \| `"dropbox"` + +#### Defined in + +[remotestorage.ts:326](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L326) + +*** + +### caching + +> **caching**: [`Caching`](../../caching/classes/Caching.md) + +#### Defined in + +[remotestorage.ts:301](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L301) + +*** + +### remote + +> **remote**: [`Remote`](../../remote/interfaces/Remote.md) + +Depending on the chosen backend, this is either an instance of `WireClient`, +`Dropbox` or `GoogleDrive`. + +See [Remote](../../remote/interfaces/Remote.md) for public API + +#### Example + +```ts +remoteStorage.remote.connected +// false +``` + +#### Defined in + +[remotestorage.ts:338](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L338) + +*** + +### sync + +> **sync**: `Sync` + +#### Defined in + +[remotestorage.ts:298](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L298) + +## Accessors + +### connected + +> `get` **connected**(): `boolean` + +Indicating if remoteStorage is currently connected. + +#### Returns + +`boolean` + +#### Defined in + +[remotestorage.ts:441](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L441) + +## Methods + +### addEventListener() + +> **addEventListener**(`eventName`, `handler`): `void` + +Install an event handler for the given event name + +Usually called via [`on()`](#on) + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Defined in + +[eventhandling.ts:29](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L29) + +*** + +### addModule() + +> **addModule**(`module`): `void` + +Add remoteStorage data module + +#### Parameters + +• **module**: [`RSModule`](../interfaces/RSModule.md) + +A data module object + +#### Returns + +`void` + +#### Example + +Usually, you will import your data module from either a package or a local path. +Let's say you want to use the +[bookmarks module](https://github.com/raucao/remotestorage-module-bookmarks) +in order to load data stored from [Webmarks](https://webmarks.5apps.com) for +example: + +```js +import Bookmarks from 'remotestorage-module-bookmarks'; + +remoteStorage.addModule(Bookmarks); +``` + +You can also forgo this function entirely and add modules when creating your +remoteStorage instance: + +```js +const remoteStorage = new RemoteStorage({ modules: [ Bookmarks ] }); +``` + +After the module has been added, it can be used like so: + +```js +remoteStorage.bookmarks.archive.getAll(false) + .then(bookmarks => console.log(bookmarks)); +``` + +#### Defined in + +[remotestorage.ts:1189](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1189) + +*** + +### connect() + +> **connect**(`userAddress`, `token`?): `void` + +Connect to a remoteStorage server. + +Discovers the WebFinger profile of the given user address and initiates +the OAuth dance. + +This method must be called *after* all required access has been claimed. +When using the connect widget, it will call this method when the user +clicks/taps the "connect" button. + +Special cases: + +1. If a bearer token is supplied as second argument, the OAuth dance + will be skipped and the supplied token be used instead. This is + useful outside of browser environments, where the token has been + acquired in a different way. + +2. If the Webfinger profile for the given user address doesn't contain + an auth URL, the library will assume that client and server have + established authorization among themselves, which will omit bearer + tokens in all requests later on. This is useful for example when using + Kerberos and similar protocols. + +#### Parameters + +• **userAddress**: `string` + +The user address (user@host) or URL to connect to. + +• **token?**: `string` + +(optional) A bearer token acquired beforehand + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.connect('user@example.com'); +``` + +#### Defined in + +[remotestorage.ts:543](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L543) + +*** + +### disableLog() + +> **disableLog**(): `void` + +Disable remoteStorage debug logging + +#### Returns + +`void` + +#### Defined in + +[remotestorage.ts:727](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L727) + +*** + +### disconnect() + +> **disconnect**(): `void` + +"Disconnect" from remote server to terminate current session. + +This method clears all stored settings and deletes the entire local +cache. + +#### Returns + +`void` + +#### Defined in + +[remotestorage.ts:629](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L629) + +*** + +### enableLog() + +> **enableLog**(): `void` + +Enable remoteStorage debug logging. + +Usually done when instantiating remoteStorage: + +```js +const remoteStorage = new RemoteStorage({ logging: true }); +``` + +#### Returns + +`void` + +#### Defined in + +[remotestorage.ts:720](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L720) + +*** + +### getBackgroundSyncInterval() + +> **getBackgroundSyncInterval**(): `number` + +Get the value of the sync interval when application is in the background + +#### Returns + +`number` + +A number of milliseconds + +#### Example + +```ts +remoteStorage.getBackgroundSyncInterval(); +// 60000 +``` + +#### Defined in + +[remotestorage.ts:1024](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1024) + +*** + +### getCurrentSyncInterval() + +> **getCurrentSyncInterval**(): `number` + +Get the value of the current sync interval. Can be background or +foreground, custom or default. + +#### Returns + +`number` + +number of milliseconds + +#### Example + +```ts +remoteStorage.getCurrentSyncInterval(); +// 15000 +``` + +#### Defined in + +[remotestorage.ts:1060](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1060) + +*** + +### getRequestTimeout() + +> **getRequestTimeout**(): `number` + +Get the value of the current network request timeout + +#### Returns + +`number` + +A number of milliseconds + +#### Example + +```ts +remoteStorage.getRequestTimeout(); +// 30000 +``` + +#### Defined in + +[remotestorage.ts:1073](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1073) + +*** + +### getSyncInterval() + +> **getSyncInterval**(): `number` + +Get the value of the sync interval when application is in the foreground + +#### Returns + +`number` + +A number of milliseconds + +#### Example + +```ts +remoteStorage.getSyncInterval(); +// 10000 +``` + +#### Defined in + +[remotestorage.ts:990](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L990) + +*** + +### on() + +> **on**(`eventName`, `handler`): `void` + +Register an event handler for the given event name + +Alias for [addEventListener](RemoteStorage.md#addeventlistener) + +#### Parameters + +• **eventName**: `string` + +Name of the event + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +Function to handle the event + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.on('connected', function() { + console.log('storage account has been connected'); +}); +``` + +#### Defined in + +[eventhandling.ts:55](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L55) + +*** + +### onChange() + +> **onChange**(`path`, `handler`): `void` + +Add a `change` event handler for the given path. Whenever a change +happens (as determined by the local backend, such as e.g. +`RemoteStorage.IndexedDB`), and the affected path is equal to or below the +given 'path', the given handler is called. + +> [!TIP] +> You should usually not use this method, but instead use the +> `change` events provided by [BaseClient](../../baseclient/classes/BaseClient.md). + +#### Parameters + +• **path**: `string` + +Absolute path to attach handler to + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +A function to handle the change + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.onChange('/bookmarks/', function() { + // your code here +}) +``` + +#### Defined in + +[remotestorage.ts:704](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L704) + +*** + +### reconnect() + +> **reconnect**(): `void` + +Reconnect the remote server to get a new authorization. + +Useful when not using the connect widget and encountering an +`Unauthorized` event. + +#### Returns + +`void` + +#### Defined in + +[remotestorage.ts:613](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L613) + +*** + +### removeEventListener() + +> **removeEventListener**(`eventName`, `handler`): `void` + +Remove a previously installed event handler + +#### Parameters + +• **eventName**: `string` + +• **handler**: [`EventHandler`](../../eventhandling/type-aliases/EventHandler.md) + +#### Returns + +`void` + +#### Defined in + +[eventhandling.ts:62](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/eventhandling.ts#L62) + +*** + +### scope() + +> **scope**(`path`): [`BaseClient`](../../baseclient/classes/BaseClient.md) + +This method allows you to quickly instantiate a BaseClient, which you can +use to directly read and manipulate data in the connected storage account. + +Please use this method only for debugging and development, and choose or +create a [data module](../../../data-modules/) for your app to use. + +#### Parameters + +• **path**: `string` + +The base directory of the BaseClient that will be returned + (with a leading and a trailing slash) + +#### Returns + +[`BaseClient`](../../baseclient/classes/BaseClient.md) + +A client with the specified scope (category/base directory) + +#### Example + +```ts +remoteStorage.scope('/pictures/').getListing(''); +remoteStorage.scope('/public/pictures/').getListing(''); +``` + +#### Defined in + +[remotestorage.ts:971](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L971) + +*** + +### setApiKeys() + +> **setApiKeys**(`apiKeys`): `boolean` \| `void` + +Set the OAuth key/ID for GoogleDrive and/or Dropbox backend support. + +#### Parameters + +• **apiKeys** + +A config object + +• **apiKeys.dropbox**: `string` + +• **apiKeys.googledrive**: `string` + +#### Returns + +`boolean` \| `void` + +#### Example + +```ts +remoteStorage.setApiKeys({ + dropbox: 'your-app-key', + googledrive: 'your-client-id' +}); +``` + +#### Defined in + +[remotestorage.ts:751](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L751) + +*** + +### setBackgroundSyncInterval() + +> **setBackgroundSyncInterval**(`interval`): `void` + +Set the value of the sync interval when the application is in the +background + +#### Parameters + +• **interval**: `number` + +Sync interval in milliseconds (between 2000 and 3600000 [1 hour]) + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.setBackgroundSyncInterval(90000); +``` + +#### Defined in + +[remotestorage.ts:1037](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1037) + +*** + +### setCordovaRedirectUri() + +> **setCordovaRedirectUri**(`uri`): `void` + +Set redirect URI to be used for the OAuth redirect within the +in-app-browser window in Cordova apps. See +[Usage in Cordova apps](../../../cordova) for details. + +#### Parameters + +• **uri**: `string` + +A valid HTTP(S) URI + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.setCordovaRedirectUri('https://app.example.com'); +``` + +#### Defined in + +[remotestorage.ts:797](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L797) + +*** + +### setRequestTimeout() + +> **setRequestTimeout**(`timeout`): `void` + +Set the timeout for network requests. + +#### Parameters + +• **timeout**: `number` + +Timeout in milliseconds + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.setRequestTimeout(30000); +``` + +#### Defined in + +[remotestorage.ts:1085](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1085) + +*** + +### setSyncInterval() + +> **setSyncInterval**(`interval`): `void` + +Set the value of the sync interval when application is in the foreground + +#### Parameters + +• **interval**: `number` + +Sync interval in milliseconds (between 2000 and 3600000 [1 hour]) + +#### Returns + +`void` + +#### Example + +```ts +remoteStorage.setSyncInterval(20000); +``` + +#### Defined in + +[remotestorage.ts:1002](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1002) + +*** + +### startSync() + +> **startSync**(): `Promise`\<`void`\> + +Start synchronization with remote storage, downloading and uploading any +changes within the cached paths. + +Please consider: local changes will attempt sync immediately, and remote +changes should also be synced timely when using library defaults. So +this is mostly useful for letting users sync manually, when pressing a +sync button for example. This might feel safer to them sometimes, esp. +when shifting between offline and online a lot. + +#### Returns + +`Promise`\<`void`\> + +A Promise which resolves when the sync has finished + +#### Defined in + +[remotestorage.ts:1126](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1126) + +*** + +### stopSync() + +> **stopSync**(): `void` + +Stop the periodic synchronization. + +#### Returns + +`void` + +#### Defined in + +[remotestorage.ts:1139](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L1139) diff --git a/docs/api/remotestorage/index.md b/docs/api/remotestorage/index.md new file mode 100644 index 000000000..8bb559074 --- /dev/null +++ b/docs/api/remotestorage/index.md @@ -0,0 +1,11 @@ +# remotestorage + +## Index + +### Classes + +- [RemoteStorage](classes/RemoteStorage.md) + +### Interfaces + +- [RSModule](interfaces/RSModule.md) diff --git a/docs/api/remotestorage/interfaces/RSModule.md b/docs/api/remotestorage/interfaces/RSModule.md new file mode 100644 index 000000000..92a7dd8c8 --- /dev/null +++ b/docs/api/remotestorage/interfaces/RSModule.md @@ -0,0 +1,66 @@ +# Interface: RSModule + +Represents a data module + +## Example + +```js +{ + name: 'examples', + builder: function(privateClient, publicClient) { + return { + exports: { + addItem(item): function() { + // Generate a random ID/path + const path = [...Array(10)].map(() => String.fromCharCode(Math.floor(Math.random() * 95) + 32)).join(''); + // Store the object, and ensure it conforms to the JSON Schema + // type `example-item` + privateClient.storeObject('example-item', path, item); + } + } + } + } +} +``` + +## Properties + +### builder() + +> **builder**: (`privateClient`, `publicClient`) => `object` + +A module builder function, which defines the actual module + +#### Parameters + +• **privateClient**: [`BaseClient`](../../baseclient/classes/BaseClient.md) + +• **publicClient**: [`BaseClient`](../../baseclient/classes/BaseClient.md) + +#### Returns + +`object` + +##### exports + +> **exports**: `object` + +###### Index Signature + + \[`key`: `string`\]: `any` + +#### Defined in + +[remotestorage.ts:91](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L91) + +*** + +### name + +> **name**: `string` + +The module's name, which is also the category (i.e. base folder) for document URLs on the remote storage + +#### Defined in + +[remotestorage.ts:87](https://github.com/remotestorage/remotestorage.js/blob/a199c15fb409a17fd444aa7fba846e7fecc5981d/src/remotestorage.ts#L87) diff --git a/docs/contributing/building.md b/docs/contributing/building.md new file mode 100644 index 000000000..0296fe58f --- /dev/null +++ b/docs/contributing/building.md @@ -0,0 +1,41 @@ +# Building + +::: tip +We\'re using npm scripts for all common tasks, so check out the +`scripts` section in `package.json` to learn about what they\'re doing +exactly and what else is available. +::: + +## Setup + +```sh +$ npm install +``` + +## Development + +```sh +$ npm run dev +``` + +This will watch `src/` for changes and build `remotestorage.js` in the +`release/` directory every time you save a source file. Useful for +testing rs.js changes with an app, for example by creating a symlink to +`release/remotestorage.js`. + +This build includes [source +maps](https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) +directly, so you can easily place `debugger` statements in the code and +step through the actual source code in your browser\'s debugger tool. + +## Production + +```sh +$ npm run build:release +``` + +This creates the minified production build in `release/`. + +It also creates a separate source maps file, which you can link to in +case you want to (e.g. to improve exception tracking/debugging in +production). diff --git a/docs/contributing/docs.md b/docs/contributing/docs.md new file mode 100644 index 000000000..4ab5733d1 --- /dev/null +++ b/docs/contributing/docs.md @@ -0,0 +1,112 @@ +::: danger DEPRECATED +Needs a complete rewrite for the new TypeDoc + VitePress setup +::: + +# Documentation + +The documentation for remoteStorage.js is generated from +[reStructuredText](http://docutils.sourceforge.net/rst.html) files in +the `doc/` folder, as well as [TypeDoc](https://typedoc.org/) code +comments, which are being pulled in via special declarations in those +files. + +We use [Sphinx](http://www.sphinx-doc.org/) to generate the +documentation website, and the +[sphinx-js](https://pypi.python.org/pypi/sphinx-js/) extension for +handling the TypeDoc part. + +## How to write reStructuredText and TypeDoc + +For learning both the basics and advances features of reStructuredText, +we highly recommend the [reStructuredText +Primer](http://www.sphinx-doc.org/en/stable/rest.html) on the Sphinx +website. + +For TypeDoc, you can find guides as well as a detailed reference [on the +project\'s website](https://typedoc.org/). + +## Automatic builds and publishing + +The documentation is published via [Read the +Docs](https://readthedocs.org/). Whenever the Git repository\'s `master` +branch is pushed to GitHub, RTD will automatically build a new version +of the site and publish it to +[remotestoragejs.readthedocs.io](https://remotestoragejs.readthedocs.io). + +This means that if you want to contribute to the documentation, you +don\'t necessarily have to set up Sphinx and sphinx-js locally +(especially for small changes). However, if you want to preview what +your local changes look like when they are rendered as HTML, you will +have to set up local builds first. + +## How to build the docs on your machine + +### Setup + +1. [Install Python and PIP](https://pip.pypa.io/en/stable/installing/) + (likely already installed) + +2. Install sphinx-js and extensions (from repository root): + + ```sh + $ pip install -r doc/requirements.txt + ``` + +3. Install TypeScript and TypeDoc globally (so Sphinx can use them): + + ```sh + $ npm -g install typescript typedoc + ``` + +### Build + +Run the following command to automatically watch and build the +documentation: + +```sh +$ npm run autobuild-docs +``` + +This will start a web server, serving rendered HTML docs on +. + +::: hint +::: title +Hint +::: + +The autobuild cannot watch for changes in TypeDoc comments as of now, so +you will need to re-run the command, or change something in a `.rst` +file in order for code documentation changes to be re-built. +::: + +## How to build the docs using ReadTheDocs\' Docker image + +This is useful for troubleshooting when the ReadTheDocs build is +failing. + +### Setup + +1. [Install Docker](https://docs.docker.com/get-docker/) + +2. Pull the latest version of `readthedocs/build` image with the + `latest` tag from Docker Hub: + + ```sh + $ docker pull readthedocs/build:latest + ``` + +### Build + +1. Enter a `bash` session while attaching this project as a volume: + + ```sh + $ docker run --rm -it -v ${PWD}:/app readthedocs/build:latest bash + ``` + +2. Run the `build-with-conda.sh` script to setup conda environment and + build the docs like ReadTheDocs: + + ```sh + $ /app/doc/build-with-conda.sh + ``` diff --git a/docs/contributing/github-flow.md b/docs/contributing/github-flow.md new file mode 100644 index 000000000..92a4e3f7b --- /dev/null +++ b/docs/contributing/github-flow.md @@ -0,0 +1,124 @@ +# GitHub workflow + +## General guidelines + +- When you start working on an existing GitHub issue (or you plan on + doing that in the immediate future), assign it to yourself, so that + others can see it and don't start working on it in parallel. +- When you create a branch to work on something, use the naming scheme + described further down in this document. +- Never push directly to the `master` branch for any changes to the + source code itself. +- As soon as you want others to review your changes, or even just + discuss them, create a pull request. Don't forget to explain + roughly what it is you're doing in that branch, i.e. what the + problem/idea is and what the result is supposed to be, when merging + the changes. If necessary or helpful, mention related discussions + from other issues. +- A pull request can be merged as soon as at least two people with + commit access to the repo have given a +1, meaning they reviewed and + tested the changes and have no further improvements to suggest. + +## Branch names + +Using common branch names, that include topics and issue IDs, makes +everyone's lives much easier, and keep the repo clean. Branches on our +organization repositories should be created using the following scheme: + +`[bugfix|feature|docs|refactor]/[issue id]-[description_with_underscores]` + +So for example, if you want to work on fixing a bug with let's say +initial sync, that is described in issue #423, the branch should look +something like: + +`bugfix/423-race_condition_on_initial_sync` + +And if it's an enhancement to the widget it could look like this e.g.: + +`feature/321-customizable_widget_content` + +If there's no issue yet, create one first! + +## Pulling changes + +Always use `--rebase` when pulling code from the remote repo. That way +your local changes are added on top of the current history, avoiding +merge commits and mixing up the commit history. You can set up Git to +use rebase on every pull by default by running +`git config --global pull.rebase true` once. + +If you also add the `autostash` option, Git will stash any changed files +before the pull and unstash them afterwards: +`git config --global rebase.autoStash true`. + +If you don't want to configure both options globally, or you prefer a +catchier command name for updating a repository with remote changes, we +recommend configuring an alias, like so: +`git config --global alias.up 'pull --rebase --autostash'`. Now you can +simply run `git up` in select repos, or everywhere. + +## Commit messages + +- The first line of the message (aptly called \"subject line\" in Git + terminology) should not be longer than 72 characters. +- If the subject line is not enough to describe the changes properly, + add a blank line after the subject line and then as much text as you + want, using normal language with capitalization, punctuation, etc. +- Always use messages that describe roughly *what* the change does + and, if not obvious, *why* this change leads to the desired result. +- Leave out any text that isn't directly associated with the changes, + that the commit introduces. Examples: \"as suggested by + \@chucknorris\", \"lol wtf was that\", \"not sure if this fixes + it\". +- Commit as much and often as possible locally (and with any message + that helps you during your work), and then clean up the history and + merge commits that belong together before pushing to the org repo. + You can do that with `git rebase -i [ref]` ([learn + more](http://www.reviewboard.org/docs/codebase/dev/git/clean-commits/#rewriting-history)). +- You can reference issues from commit messages by adding keywords + with issue numbers. Certain keywords will even close the issue + automatically, once a branch is merged into master. For example + `Fix widget flickering when opening bubble (fixes #423)` will close + issue #423 when appearing on the master branch at GitHub. + +## Reviewing pull requests + +- Check if it works, if it has unit tests, if the tests pass, and if + the linter is happy. +- Check if the code is understandable, with clear and unambiguous + names for functions and variables, and that it has TypeDoc comments + and a changelog entry. +- If the pull request was issued from a user's own repository, you + will have to fetch the code from there. If you haven't pulled from + their fork previously, you can add a new remote for it with + `git remote add [username] [repo-url]`. Then, `git fetch [username]` + will fetch code from this remote, so you can then check out their + branch using `git checkout [username]/branchname`. +- This will put you in a so-called 'detached HEAD' state, but don't + worry, everything is fine! If you want to work on that code, just + create a new branch from there with the command Git tells you then, + or just go back to your code with e.g. `git checkout master` later.) + +## Merging pull requests + +- Once a pull request has two +1s for the latest changes from + collaborators, you can either merge it yourself or wait for somebody + to do it for you (which will happen very soon). +- If the new commits and their commit messages in that branch all make + sense on their own, you can use the merge button on GitHub directly. +- If there are a lot of small commits, which might not make sense on + their own, or pollute the main project history (often the case with + long running pull requests with a lot of additions during their + lifetime), fetch the latest changes to your local machine, and + either do an interactive rebase to clean up branch and merge + normally, or use `git merge --squash` to squash them all into one + commit during the merge. +- Whenever you squash multiple commits with either `git rebase -i` or + `git merge --squash`, make sure to follow the commit message + guidelines above. Don't just leave all old commit messages in there + (which is the default), but delete them and create a new meaningful + message for the whole changeset. +- When squashing/editing/amending other peoples' commits, use + `--author` to set them as the original author. You don't need full + names for that, but just something that Git can find in the history. + It'll tell you if it can't find an author and let you do it again. diff --git a/docs/contributing/index.md b/docs/contributing/index.md new file mode 100644 index 000000000..34d3620e2 --- /dev/null +++ b/docs/contributing/index.md @@ -0,0 +1,6 @@ +# Contributing + +This section contains information and help for people wanting to contribute to +remoteStorage.js development, maintenance, or documentation. + +Every bit helps, even fixing a typo is worth a pull request! diff --git a/docs/contributing/internals/cache-data-format.md b/docs/contributing/internals/cache-data-format.md new file mode 100644 index 000000000..5ac983661 --- /dev/null +++ b/docs/contributing/internals/cache-data-format.md @@ -0,0 +1,206 @@ +# Data format of the local cache + +This section describes the structure and concepts of the local cache. + +## Storing up to 4 revisions of each node + +Each cache node represents the versioning state of either one document +or one folder. The versioning state is represented by one or more of the +`common`, `local`, `remote`, and `push` revisions. Local changes are +stored in `local`, and in `push` while an outgoing request is active. +Remote changes that have either not been fetched yet, or have not been +merged with local changes yet, are stored in `remote`. + +## autoMerge + +The `sync.autoMerge` function will try to merge local and remote changes +into the common revision of a node. It may emit change events with a +\'conflict\' origin to indicate that an unpushed local change was +overruled by a remote change. + +When consulting the base client about the current value of a node, you +will get either its \'local\' revision if it exists, or its \'common\' +revision otherwise. The following are versioning tree diagrams of how +local and remote revisions of a node can interact: + +``` text +//in sync: +1) . . . . [common] + +//dirty: +2) . . . . [common] + \ + \ . . . . [remote] + +//local change: +3) . . . . [common] . . . . [local] + +//conflict (should autoMerge): +4) . . . . [common] . . . . [local] + \ + \ . . . . [remote] + +//pushing: +5) . . . . [common] . . . . [push] . . . . [local] + +//pushing, and known dirty (should abort the push, or just wait for the conflict to occur): +6) . . . . [common] . . . . [push] . . . . [local] + \ + \ . . . . [remote] +``` + +Each of `local`, `push`, `remote`, and `common` can have have following +properties: + +- for documents: + - body + - contentType + - contentLength + - revision + - timestamp +- for folders: + - itemsMap (itemName -\> true, or itemName -\> false to indicate + an unmerged deletion) + - revision + - timestamp + +NB: The timestamp represents the last sync time, not the last modified +time. It is used by the `isOutdated` function in `src/cachinglayer.js` +to determine if the data needs to be fetched from remote again, or can +be served from cache. + +## \"keep/revert\" conflict resolution + +RemoteStorage implements a hub-and-spokes versioning system, with one +central remoteStorage server (the hub) and any number of clients (the +spokes). The clients will typically be unhosted web apps based on this +JS lib (remotestorage.js), but they don\'t have to be; they can also be +based on other client implementations, they can be hosted web apps, +desktop apps, native smartphone apps, etcetera. New versions of subtrees +always start at one of these clients. They are then sent to the server, +and from there to all the other clients. The server assigns the revision +numbers and sends them to the initiating client using HTTP ETag response +headers in response to PUT requests. remotestorage.js is a library that +attempts to make it easy to build remoteStorage applications, by hiding +both the push/pull synchronization and the version merging from the app +developer. Versioning conflicts between the local client and the remote +server are initially resolved as a \'remote wins\', to which the client +code may respond with an explicit revert (putting the old, local version +back), any type of custom merge (putting the result of the merge in +place), or by doing nothing (\"keep\"), and leaving the remote result in +place. This system is called \"keep/revert\", since the library takes a +pro-active action (\'remote wins\'), which the app can then either keep, +or revert. + +Sync is tree-based: syncing a node is equivalent to syncing all its +children. There are two parts at play, that interact: transporting the +diffs to and from the remote server, and merging the local and remote +versions into one common version. Each document starts out as +non-existing in both its local and remote versions. From there on, it +can be created, updated, and deleted any number of times throughout its +history, both locally and remotely. If at some point in time it either +does not exist neither locally nor remotely, or its body and +content-type are the same byte-for-byte on both sides, then the two +stores are in agreement. If the document exists in only one of the +stores, or the document\'s body or its content-type differs between the +two stores, then the document is in conflict. + +The library is always aware of the latest local version, but it may or +may not be aware of the latest remote version, and therefore of whether +a conflict or agreement exists for the document. Likewise, the server is +not necessarily aware of the latest local version, if there are changes +that haven\'t been pushed out yet; nor does it care, though, since the +server does not get involved in conflict resolution. It only serializes +conditional updates from all clients into one canonical versioning +history. + +The lack of sync between client and server can be fixed by doing a GET, +PUT, or DELETE. A GET will return the current remote version; a +conditional PUT or DELETE will push out the change, while at the same +time checking if any unfetched remote changes exist. If they do, then +the push will fail, and the library will fetch instead. After this, the +library has a latest known common revision of the document, possibly a +local version if it was changed since then, and possibly a remote +version if it was changed since then, but the newer version has yet to +be retrieved. + +Before resolving a conflict, both revision histories are squashed. This +means that creating+deleting a document becomes a noop, and +deleting+creating, or updating it multiple times, becomes one single +update. Then, if the document was changed in different ways locally and +remotely, it goes into conflict state; if it was changed only locally or +only remotely, then the change is automatically accepted by the other +store (whether client to server or server to client). Note that in the +case of a successful conditional push request, this will already have +happened. + +Conflicts that are discovered by a document fetch, fire their +\'keep/revert\' event immediately. Conflicts that are discovered through +a parent folder fetch, or through a conditional push, fire their +\'keep/revert\' event after the new remote version is fetched. + +The library\'s conflict resolution strategy is \'remote wins\'. This +means that the module will receive them in the form of change events +with origin \'conflict\'. When receiving such a change event, the module +can still decide to revert it explicitly. + +As noted before, merging a subtree is done by merging each document that +exists within that subtree, in either or both stores. When the library +fetches a folder listing, it can detect a remote child change, which +then may or may not result in a conflict. When a folder listing comes +in, which has changed since the last time it was retrieved, four types +of information may be discovered: + +- which of the documents directly within the folder changed their + remote revision since the last check (new ETag on a document item) +- in which previously empty subtrees at least one document was created + (new folder item) +- in which subtrees all previously existing documents were deleted + (folder item disappeared) +- in which subtrees at least one document was either created, updated, + or deleted (new ETag on a folder item) + +All of these can occur in a folder that was at the same time either +unchanged, updated, or deleted locally. When updated, it might be that +different items were changed locally and remotely, or that the same item +was changed on both sides, either in the same way, or in different ways. + +The library handles all these cases so the module developer does not +need to worry about them. + +## Implications for module design + +There are a number of important implications for module design: + +- First of all, this sync process follows the \'asynchronous + synchronization\' design principle + (). Don\'t wait + for it to finish. The module should work with the local copy of the + data, and handle incoming updates through evented programming. The + only exception to this is where a body of data is too big to cache + locally, and the module needs to expose on-demand access of remote + data to the app. In all other cases, the module should expose the + local version as \'the truth\'. +- Even then, IndexedDB is not fast enough to access from a button + click. Make sure to put an in-memory caching layer in the module, + and return control to the app immediately. An example of this + approach is the SyncedMap data structure used in + . +- Use folders and subfolders. This allows the tree-based sync + algorithm to shine and efficiently detect changes in any of + potentially thousands of documents by checking the ETag from one + single HTTP request to the root folder of the tree. +- Use meaningful collections. Multiple clients can each edit a + different document without ever entering in conflict with each + other. But editing the same document is interpreted as a conflict. + For instance, when two calendar apps both schedule an event on a + certain date, this would be a conflict if the module stores one + document per day. However, if the module stores one document per + event, and instead uses one /folder/ for each day, then the two + events can co-exist on the same day without generating a conflict. + Documents are a unit of conflict, but folders are not. Another + example is storing todo-list items with long UUID hashes instead of + their list index numbers as document names. Editing item \"5\" would + conflict with inserting a new item \"5\". But if both items have a + long unique name, then they don\'t clash with each other. So make + sure to choose unique item names for items that should not conflict. diff --git a/docs/contributing/internals/caching.md b/docs/contributing/internals/caching.md new file mode 100644 index 000000000..aa3501181 --- /dev/null +++ b/docs/contributing/internals/caching.md @@ -0,0 +1,25 @@ +# Caching + +The caching strategies are stored in `remoteStorage.caching._rootPaths`. +For instance, on , it has +the value `{ /myfavoritedrinks/: "ALL" }`. + +These rootPaths are not stored in localStorage. If you refresh the page, +it is up to the app to set all caching strategies again during the page +load. + +The effect of the caching strategy is basically achieved through three +paths: + +1. Setting caching strategy \'ALL\' for a path creates an empty node + for that path, unless it already exists. +2. The sync process will then do a GET request, and create new nodes + under any folder with an \'ALL\' strategy, when that folder is + fetched. +3. The sync process will create a new task for any node under an + \'ALL\' strategy, unless a task already exists for one of its + ancestors. + +The result is all paths with an explicit \'ALL\' strategy will get +fetched, and if they are folders, then in the next round, all its +children will also be fetched, etcetera. diff --git a/docs/contributing/internals/code-overview.md b/docs/contributing/internals/code-overview.md new file mode 100644 index 000000000..29d5ac02d --- /dev/null +++ b/docs/contributing/internals/code-overview.md @@ -0,0 +1,32 @@ +# Code overview + +The code of remoteStorage.js consists of files in the `src/` folder of +this repo. These are built into a single file in the `release/` folder +using [webpack](http://webpack.github.io/). Unit tests live in the +`test/` folder and are based on +[Jaribu](https://github.com/silverbucket/jaribu). + +The structure of the code is based around feature loading. Most files in +`src/` correspond to a feature, e.g. `discover.ts` to +`RemoteStorage.Discover` or `caching.ts` to `RemoteStorage.Caching`. + +The feature loading happens synchronously during the page load in +`src/remotestorage.ts` (just including this script in your app will lead +to executing the code that loads the features). + +Most features load under their own name, but for `remoteStorage.local` a +choice is made between `RemoteStorage.IndexedDB`, +`RemoteStorage.LocalStorage` and `RemoteStorage.InMemoryCaching`, +depending on what the environment (browser, node.js, Electron, WebView, +or other) supports. + +For `remoteStorage.local` we then also have a [special +mixin](https://github.com/remotestorage/remotestorage.js/issues/777#issuecomment-57392440) +called `src/cachinglayer.ts`, which mixes in some common functions into +the object. + +The `remoteStorage.remote` feature is not loaded immediately, but only +when `RemoteStorage.Discover` calls `remoteStorage.setBackend()`, at +which point a choice is made between `RemoteStorage.WireClient`, +`RemoteStorage.GoogleDrive`, `RemoteStorage.Dropbox` (or any other +future backend) to become the `remote`. diff --git a/docs/contributing/internals/discovery-bootstrap.md b/docs/contributing/internals/discovery-bootstrap.md new file mode 100644 index 000000000..399422e01 --- /dev/null +++ b/docs/contributing/internals/discovery-bootstrap.md @@ -0,0 +1,35 @@ +# Discovery bootstrap + +*This section describes how connecting to a storage works internally.* + +When the RemoteStorage instance is instantiated, it checks the fragment +of the URL to see if it contains an `access_token` or `remotestorage` +parameter. In the first case, the access token is given to the remote +using `remoteStorage.remote.configure()`. In the second case, WebFinger +discovery is triggered for the user address given (see [storage-first +section](https://tools.ietf.org/html/draft-dejong-remotestorage-09#section-11) +of the remoteStorage spec). + +The user can also set the user address through the widget, or the app +can call +`remoteStorage.remote.configure({userAddress: 'user@host.com'})` to set +the user address. + +When a user address is set, but no other remote parameters are known +yet, WebFinger discovery will be triggered. From the WebFinger response, +the library extract the storage base URL, the storage API, and the OAuth +dialog URL. + +If no OAuth URL is given, Implied Auth is triggered: + + +If an OAuth URL is known, but no token yet, the OAuth dance will be +started by setting the `location.href` of the window, redirecting the +user to that URL. When the dance comes back, the library will detect the +`access_token` from the window location during the page load, and from +that point onwards, the remote is connected. + +If the OAuth flow is PKCE, the window location will contain a `code` +parameter instead of `access_token`. RS then makes a fetch to +remote.TOKEN_URL with the code, to retrieve the access token, and +possibly a refresh token as well. diff --git a/docs/contributing/internals/index.md b/docs/contributing/internals/index.md new file mode 100644 index 000000000..961a1cf7d --- /dev/null +++ b/docs/contributing/internals/index.md @@ -0,0 +1,4 @@ +# Libary internals + +This section contains information about some of the internals and concepts of +the remoteStorage.js library. diff --git a/docs/contributing/release-checklist.md b/docs/contributing/release-checklist.md new file mode 100644 index 000000000..7205e6aa7 --- /dev/null +++ b/docs/contributing/release-checklist.md @@ -0,0 +1,56 @@ +# Release checklist + +- Build library and manually test all browsers you have access to, + including mobile devices and private browsing mode + +- Create changelog since last release + + - Collect and summarize changes using e.g.: + + ```sh + $ git log --no-merges ..HEAD + ``` + + - Add changes to `CHANGELOG.md` + + - Commit to Git + +- Run `npm version patch|minor|major|x.x.x-rc1`. This will + automatically: + + - run the test suite + - update the version in package.json + - create a release build + - commit everything using version as commit description + - create a Git tag for the version + - push the release commit and tag to GitHub + +- Publish release notes on GitHub + + - Go to https://github.com/remotestorage/remotestorage.js/tags + and click "Add release notes" + - Use version string as title and changelog items as description + - For RCs and betas, tick the "This is a pre-release" option + on the bottom + - Post release notes [to the community + forums](https://community.remotestorage.io/t/release-updates-for-rs-libraries/433) + +- Publish to npm (): + + ```sh + $ npm publish + ``` + +- Update to use + new release + + - Replace `remotestorage.js` file with new release build + - Check if everything is still working + - Commit + - `git push origin` + - `git push 5apps master` + +- Link release announcement on Mastodon. This will automatically cross-post + to Twitter. + +- If it's an important release, also notify the Unhosted mailing list diff --git a/docs/contributing/testing.md b/docs/contributing/testing.md new file mode 100644 index 000000000..5aadcd591 --- /dev/null +++ b/docs/contributing/testing.md @@ -0,0 +1,42 @@ +# Testing + +Before contributing to remoteStorage.js, make sure your patch passes the +test suite, and your code style passes the code linting suite. + +We use the [Jaribu](https://github.com/silverbucket/jaribu) framework +for our test suites and [JSHint](http://jshint.com/about/) for linting. +Both are set as dev dependencies in `package.json`, so after installing +those via `npm install`, you can use the following command to run +everything at once: + +```sh +$ npm run test +``` + +Or you can use the Jaribu executable directly in order to run the suite +for a single file: + +```sh +$ ./node_modules/.bin/jaribu test/unit/cachinglayer-suite.js +``` + +::: warning +We're in the process of porting the tests to Mocha/Chai. Also see `npm run +test:mocha`. +::: + +::: tip +If you add `./node_modules/.bin` to your `PATH`, you can call +executables in any npm project directly. For example in `~/.bashrc`, add +the line `export PATH=$PATH:./node_modules/.bin` (and run +`source ~/.bashrc` to load that change in open terminal sessions). Then +you can just run `jaribu test/unit/foo_suite.js`. +::: + +## Continous integration + +The rs.js test suite is run by GitHub Actions on every push to our repo +on GitHub. When you open a pull request, your code will be tested there, +too. You can check out the build status and history at +, and the CI +settings in `.github/workflows/test-and-lint.yml`. diff --git a/doc/cordova.rst b/docs/cordova.md similarity index 52% rename from doc/cordova.rst rename to docs/cordova.md index 511923ed4..e5c8269c5 100644 --- a/doc/cordova.rst +++ b/docs/cordova.md @@ -1,25 +1,28 @@ -Usage in Cordova apps -===================== +# Usage in Cordova apps -`Apache Cordova `_ is a mobile development +[Apache Cordova](https://cordova.apache.org) is a mobile development framework. It allows you to use standard web technologies - HTML5, CSS3, and JavaScript for cross-platform development. Applications execute within wrappers targeted to each platform, and rely on -standards-compliant API bindings to access each device's capabilities -such as sensors, data, network status, etc. [#f1]_ +standards-compliant API bindings to access each device\'s capabilities +such as sensors, data, network status, etc. -To use remoteStorage.js in a Cordova app, you need to have the `InAppBrowser plugin -`_ +To use remoteStorage.js in a Cordova app, you need to have the +[InAppBrowser +plugin](https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/) installed. Cordova apps are packaged for the different platforms and installed on -the device. The app doesn't need to be hosted as a web app (although it +the device. The app doesn\'t need to be hosted as a web app (although it can be as well). But for the remoteStorage connection to work, you need to provide a page that is accessible via a public URL. This will be used as the redirect URL during the OAuth flow. + + When a user connects their storage, the OAuth dialog will open in an -in-app browser window, set to show the address to prevent phishing attacks. +in-app browser window, set to show the address to prevent phishing +attacks. After the user authorizes the app, the server will redirect to the configured redirect URL with the authorization token added as a @@ -29,40 +32,36 @@ token from the URL and close the window. So the user doesn't actually see the page of the redirect URL and it does't need to have the remoteStorage.js library included or have any special logic at all. But you should still make sure that it can be -identified as belonging to your app. Storage providers will usually -show the URL in the OAuth dialog, and they may also link to it (e.g. -from the list of connected apps). +identified as belonging to your app. Storage providers will usually show +the URL in the OAuth dialog, and they may also link to it (e.g. from the +list of connected apps). You can configure the redirect URL for your app, either by calling -.. code:: javascript - - remoteStorage.setCordovaRedirectUri('https://myapp.example.com'); +``` javascript +remoteStorage.setCordovaRedirectUri('https://myapp.example.com'); +``` or as config when creating your rs instance: -.. code:: javascript - - const remoteStorage = new RemoteStorage({ - cordovaRedirectUri: 'https://myapp.example.com' - }); +``` javascript +const remoteStorage = new RemoteStorage({ + cordovaRedirectUri: 'https://myapp.example.com' +}); +``` No further action is needed and you can now use remoteStorage.js as with any other web app. -Google Drive config -------------------- - -If you wish to use the optional Google Drive adapter, you need to configure a -different user agent for your app. Otherwise the authorization page will show -an error to the user. - -In case you haven't set your own UA string already, here's how you can do it: - -.. code:: xml +## Google Drive config - +If you wish to use the optional Google Drive adapter, you need to +configure a different user agent for your app. Otherwise the +authorization page will show an error to the user. -.. rubric:: Footnotes +In case you haven\'t set your own UA string already, here\'s how you can +do it: -.. [#f1] Taken from https://cordova.apache.org/docs/en/latest/guide/overview/index.html +``` xml + +``` diff --git a/docs/data-modules/defining-a-module.md b/docs/data-modules/defining-a-module.md new file mode 100644 index 000000000..2f247f5ae --- /dev/null +++ b/docs/data-modules/defining-a-module.md @@ -0,0 +1,39 @@ +# Defining a module + +A data module is just a JavaScript object containing a module name and a +builder function. + +The builder function receives two [BaseClient][2] instances when loaded: one +for private data stored in `/my-module-name/` and one for public data stored in +`/public/my-module-name/`. A module must return an object, with the properties +and functions to be used in an app as `exports`: + +``` javascript +const Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { + return { + exports: { + addBookmark: function() {} + } + } +}}; +``` + +You can then load the module into your [RemoteStorage][1] instance, either on +initialization or later using the `addModule()` function: + +```js +const remoteStorage = new RemoteStorage({ modules: [ Bookmarks ] }); + +// or later: +remoteStorage.addModule(Bookmarks); +``` + +The module will then be accessible on the instance by its name, allowing +you to call the functions and properties that it exports: + +```js +remoteStorage.bookmarks.addBookmark() +``` + +[1]: ../api/remotestorage/classes/RemoteStorage.html +[2]: ../api/baseclient/classes/BaseClient.html diff --git a/docs/data-modules/defining-data-types.md b/docs/data-modules/defining-data-types.md new file mode 100644 index 000000000..1ec91b17a --- /dev/null +++ b/docs/data-modules/defining-data-types.md @@ -0,0 +1,83 @@ +# Defining data types + +Data types can be defined using the +[declareType()](../api/baseclient/classes/BaseClient.html#declaretype) method. +It expects a name (which you can later use with +[storeObject()](../api/baseclient/classes/BaseClient.html#storeobject), as well +as a [JSON Schema](http://json-schema.org) object defining the actual structure +and formatting of your data. + +Consider this simplified example of an archive bookmark: + +```js +const Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { + + privateClient.declareType('archive-bookmark', { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "tags": { + "type": "array", + "default": [] + }, + }, + "required": [ "title", "url" ] + }); + + // ... +}}; +``` + +Now that we have a basic data type in place for storing bookmarks, we +can add a function for storing them. This will actually validate the +incoming data against the type\'s schema, and reject the promise with +detailed validation errors in case the data format doesn\'t match: + +```js +const Bookmarks = { name: 'bookmarks', builder: function(privateClient, publicClient) { +// ... + +return { + exports: { + add: function (bookmark) { + bookmark.id = md5Hash(bookmark.url); // hash URL for nice ID + var path = "archive/" + bookmark.id; // use hashed URL as filename as well + + return privateClient.storeObject("archive-bookmark", path, bookmark). + then(function() { + return bookmark; // return bookmark with added ID property + }); + } + } +}; + +// and in your app: + +remoteStorage.bookmarks.add({ + title: 'Unhosted Web Apps', + url: 'https://unhosted.org', + tags: ['unhosted', 'remotestorage', 'offline-first'] +}) +.then(() => { + console.log('stored bookmark successfully'); +}) +.catch((err) => { + console.error('validation error:', err); +}); +``` + +::: tip +JSON Schema is rather powerful and flexible. If you want to learn more +about it, check out the free e-book [Understanding JSON +Schema](https://spacetelescope.github.io/understanding-json-schema/) for +example. The complete documentation can be found on [json-schema.org](https://json-schema.org) +::: diff --git a/docs/data-modules/index.md b/docs/data-modules/index.md new file mode 100644 index 000000000..ab3db7db7 --- /dev/null +++ b/docs/data-modules/index.md @@ -0,0 +1,29 @@ +# Data modules + +A core idea of the remoteStorage protocol is that the same data can be +used by multiple apps. Imagine creating a to-do in one app, and tracking +time on it in another. + +Traditional Web apps make this possible with custom, proprietary APIs, +which are dependent on the app provider. Third party developers usually +need to register an application with the original provider to access +that data via the API, and this access can be revoked at any time. + +remoteStorage apps (and [unhosted web apps](https://unhosted.org) in general) +give end users ultimate control over which apps have access to their data. This +makes users more independent from single app providers, and ensures that any +app developer can create new apps for users' existing data. + +In order to make it easy and safe for your app data to be compatible with other +apps, we created the concept of data modules for remoteStorage.js. They are +little add-on libraries, which can be shared between apps and developers, and +that ideally are developed collaboratively in the open. + +Data modules can contain as much or as little functionality as desired, +from defining data formats and types, to data validation, formatting, +processing, transformation, encryption, indexing, and other use cases. + +Data modules make your app and its data interoperable with other apps. +Sharing your modules is generally encouraged, but it's also possible to +encapsulate custom functionality in a custom module made just for your +app. diff --git a/docs/data-modules/publishing-and-finding-modules.md b/docs/data-modules/publishing-and-finding-modules.md new file mode 100644 index 000000000..d9259cd4c --- /dev/null +++ b/docs/data-modules/publishing-and-finding-modules.md @@ -0,0 +1,30 @@ +# Publishing and finding data modules + +## npm + +The recommended way for publishing data modules is as +[npm](https://www.npmjs.com/) packages. + +Our naming convention for rs.js modules is +`remotestorage-module-mymodulename`. Thus, you can also find them by +[searching npm for +"remotestorage-module"](https://www.npmjs.com/search?q=remotestorage-module). + +You can also add "remotestorage-module" and "remotestorage" to the +`keywords` property of your `package.json`. + +## GitHub & Co. + +If you use GitHub ‒ or any other code hosting/collaboration platform for +that matter ‒ for publishing your module's source code, please use the +same naming convention as for the npm module for the repo name. And +it's a good idea to add the topic/tag/label "remotestorage-module" +there as well, of course. + + + +::: tip +With npm, you can also install modules directly from a Git repository or +GitHub, pointing to just the repo or a branch name, tag, or commit: +https://docs.npmjs.com/cli/v10/configuring-npm/package-json#github-urls +::: diff --git a/docs/dropbox-and-google-drive.md b/docs/dropbox-and-google-drive.md new file mode 100644 index 000000000..5f36f47f7 --- /dev/null +++ b/docs/dropbox-and-google-drive.md @@ -0,0 +1,77 @@ +# Offering Dropbox and Google Drive storage options + +![Screenshot of the connect-widget choose-backend screen](./images/screenshot-widget-choose.png){width="50%"} + +rs.js has optional support for syncing data with Dropbox and Google +Drive instead of a RemoteStorage server. + +There are a few drawbacks, mostly sync performance and the lack of a +permission model. So apps can usually access all of a user's storage +with these backends (vs. only relevant parts of the storage with RS +accounts). However, while RS is not a widely known and deployed +protocol, we find it helpful to let users choose something they already +know, and potentially migrate to an RS account later on. + +For these additional backends to work, you will have to register your +app with Dropbox and/or Google first. Then you can configure your OAuth +app ID/key like so: + +```js +remoteStorage.setApiKeys({ + dropbox: 'your-app-key', + googledrive: 'your-client-id' +}); +``` + +::: info +The [Connect widget](getting-started/connect-widget) will automatically show +only the available storage options, based on the presence of the Dropbox and +Google Drive API keys. RemoteStorage is always enabled. +::: + +## Dropbox + +An app key can be obtained by [registering your +app](https://www.dropbox.com/developers/apps). + +Create a new "scoped" app for the "Dropbox API", with these scopes: + +- `account_info.read` +- `files.metadata.read` +- `files.metadata.write` +- `files.content.read` +- `files.content.write` + +You need to set one or more OAuth2 redirect URIs for all routes a user can +connect from, for example `http://localhost:8000` for an app you are developing +locally. If the path is '/', rs.js drops it. + +### Known issues + +- Storing files larger than 150MB is not yet supported +- Listing and deleting folders with more than 10000 files will cause + problems +- Content-Type is not fully supported due to limitations of the + Dropbox API +- Dropbox preserves cases but is not case-sensitive +- `getItemURL` is not implemented yet (see issue 1052) + +## Google Drive + +A client ID can be obtained by registering your app in the [Google Developers +Console](https://console.developers.google.com/flows/enableapi?apiid=drive). + +- Create an API, then add credentials for Google Drive API. Specify + you will be calling the API from a "Web browser (Javascript)" + project. Select that you want to access "User data". +- On the next screen, fill out the Authorized JavaScript origins and + Authorized redirect URIs for your app (for every route a user can + connect from, same as with Dropbox) +- Once your app is running in production, you will want to get + verified by Google to avoid a security warning when the user first + connects their account + +### Known issues + +- Sharing public files is not supported yet (see issue 1051) +- `getItemURL` is not implemented yet (see issue 1054) diff --git a/docs/getting-started/connect-widget.md b/docs/getting-started/connect-widget.md new file mode 100644 index 000000000..f64a614ee --- /dev/null +++ b/docs/getting-started/connect-widget.md @@ -0,0 +1,62 @@ +# Using the Connect Widget add-on + +The easiest option for letting people connect their storage to your app +is using the Connect Widget add-on library, which is written and +maintained by the rs.js core team. + +This is optional and an easy way to integrate all functionality into your own +UI. It's a great way to start with RS app development and can be replaced with +custom code later on. + +## Adding the library + +The Connect Widget library is distributed the same way as +remoteStorage.js itself: as a +UMD +build, compatible with all JavaScript module systems, or as a global +variable named `Widget`, when linked directly. + +You can find the connect widget as +[`remotestorage-widget`](https://www.npmjs.com/package/remotestorage-widget) on +npm, and its source code and usage instructions [on +GitHub](https://github.com/remotestorage/remotestorage-widget). + +Check out [Adding rs.js to an app](how-to-add) for examples of loading a UMD +module in your code. + +## Adding the widget + +With the `Widget` class loaded, create a new widget instance using the +[previously initialized](initialize-and-configure) `remoteStorage` instance, +like so: + +```js +const widget = new Widget(remoteStorage); +``` + +Then you can attach the widget to the +[DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model): + +```js +widget.attach(); +``` + +Or if you want to attach it to a specific parent element, you can also +hand it a DOM element ID: + +```js +widget.attach('my-parent-element-id'); +``` + +That's it. Now your users can use the widget in order to connect their storage, +and you can listen to the [RemoteStorage][1] instance's events in order to get +informed about connection status, sync progress, errors, and so on. + +::: tip +If you'd like to implement connect functionality in your own user +interface and code, the widget can serve as a useful source code +example. For everything it does, the Connect Widget only uses public +APIs and events of rs.js, which you can also use in your own code. +::: + +[1]: ../api/remotestorage/classes/RemoteStorage.html diff --git a/docs/getting-started/events.md b/docs/getting-started/events.md new file mode 100644 index 000000000..7a5710e24 --- /dev/null +++ b/docs/getting-started/events.md @@ -0,0 +1,33 @@ +# Handling events + +In order to get informed about users connecting their storage, data +being transferred, the library going into offline mode, errors being +thrown, and other such things, you can listen to the events emitted by +the [RemoteStorage][1] instance, as well as [BaseClient][2] instances. + +Simply register your event handler functions using the `.on()` method, +like so: + +```js +remoteStorage.on('connected', () => { + const userAddress = remoteStorage.remote.userAddress; + console.debug(`${userAddress} connected their remote storage.`); +}) + +remoteStorage.on('network-offline', () => { + console.debug(`We're offline now.`); +}) + +remoteStorage.on('network-online', () => { + console.debug(`Hooray, we're back online.`); +}) +``` + +Check out the [RemoteStorage API][1] reference for a complete list of events +and when they're emitted. + +Also see *change events* in the [BaseClient API][2] reference, which you can +use to handle incoming data and changes from the remote storage server. + +[1]: ../api/remotestorage/classes/RemoteStorage.html +[2]: ../api/baseclient/classes/BaseClient.html diff --git a/docs/getting-started/how-to-add.md b/docs/getting-started/how-to-add.md new file mode 100644 index 000000000..a69f8779b --- /dev/null +++ b/docs/getting-started/how-to-add.md @@ -0,0 +1,70 @@ +# Adding rs.js to an app + +rs.js is distributed as a single UMD build, which means it should work with all known +JavaScript module systems, as well as without one (using a global variable). + +We recommend adding the library from a JavaScript package manager, +although you may also just download the release build [from +GitHub](https://github.com/remotestorage/remotestorage.js/releases). + +The package is available on npm as +[remotestoragejs](https://www.npmjs.com/package/remotestoragejs): + +::: code-group + +```sh [npm] +$ npm add -D remotestoragejs +``` + +```sh [pnpm] +$ pnpm add -D remotestoragejs +``` + +```sh [yarn] +$ yarn add -D remotestoragejs +``` + +```sh [bun] +$ bun add -D remotestoragejs +``` +::: + +## Examples + +### ES6 module + +``` javascript +import RemoteStorage from 'remotestoragejs'; +``` + +### CommonJS module + +``` javascript +var RemoteStorage = require('remotestoragejs'); +``` + +### AMD module + +For example with [RequireJS](http://requirejs.org/): + +``` javascript +requirejs.config({ + paths: { + RemoteStorage: './lib/remotestorage' + } +}); + +requirejs(['RemoteStorage'], function(RemoteStorage) { + // Here goes my app +}); +``` + +### No module system + +If you just link the build from HTML, it will add `RemoteStorage` as a +global variable to `window`. + +``` html + +``` diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md new file mode 100644 index 000000000..7f4b04128 --- /dev/null +++ b/docs/getting-started/index.md @@ -0,0 +1,4 @@ +# Getting started + +This section contains introductory documentation for app developers who +are new to **remoteStorage.js**. diff --git a/docs/getting-started/initialize-and-configure.md b/docs/getting-started/initialize-and-configure.md new file mode 100644 index 000000000..f6742cb70 --- /dev/null +++ b/docs/getting-started/initialize-and-configure.md @@ -0,0 +1,76 @@ +# Initialization & configuration + +Now that you\'ve imported the `RemoteStorage` class, here\'s how you +typically set things up. + +Where and how you do this exactly will naturally depend on the rest of +your code, your JS framework, and personal preferences. + +## Initializing an instance + +First step is to initialize a [RemoteStorage][1] instance: + +```javascript +const remoteStorage = new RemoteStorage(); +``` + +The constructor optionally takes a configuration object. Let\'s say we +want to enable debug logging to see in the console what rs.js is doing +behind the scenes: + +```javascript +const remoteStorage = new RemoteStorage({logging: true}); +``` + +Or perhaps we\'re building an app that doesn\'t need local caching, but +only operates on the remote server/account: + +```javascript +const remoteStorage = new RemoteStorage({cache: false}); +``` + +See the [RemoteStorage API][1] documentation for details. + +## Claiming access + +Next, we need to tell rs.js which parts of the user\'s storage we want +to access. Let\'s say we want to read and write a user\'s favorite +drinks, which they might have added via the [My Favorite +Drinks](https://github.com/RemoteStorage/myfavoritedrinks) demo app: + +```javascript +remoteStorage.access.claim('myfavoritedrinks', 'rw'); +``` + +Now, when they connect their storage, users will be asked to give the +app read/write access to the `myfavoritedrinks/` folder. And that's +also what the OAuth token, which we receive from their storage server, +will be valid for, of course. + +If you want to build a special app, like for example a backup utility, +or a data browser, you can also claim access to the entire storage +(which is generally discouraged): + +```javascript +remoteStorage.access.claim('*', 'rw'); +``` + +See the [Access API][3] documentation for details. + +## Configuring caching + +Last but not least, we'll usually want to configure caching (and with it +automatic sync) for the data we're accessing. The +[caching.enable()](../api/caching/classes/Caching.html#enable) method will +activate full caching for the given path, meaning all of the items therein will +be automatically synced with the server: + +```javascript +remoteStorage.caching.enable('/myfavoritedrinks/') +``` + +See the [Caching API][4] documentation for details. + +[1]: ../api/remotestorage/classes/RemoteStorage.html +[3]: ../api/access/classes/Access.html +[4]: ../api/caching/classes/Caching.html diff --git a/docs/getting-started/loading-data.md b/docs/getting-started/loading-data.md new file mode 100644 index 000000000..aba524b4a --- /dev/null +++ b/docs/getting-started/loading-data.md @@ -0,0 +1,70 @@ +# Loading data on app launch/startup + +In order to load data into memory (or the DOM) during the startup of your app, +there are generally two different approaches with remoteStorage.js: + +## Option 1: Relying solely on events + +Upon initialization, remoteStorage.js will emit [change +events](../api/baseclient/classes/BaseClient.html#change-events) events with +the origin `local` for all documents found in the local cache. + +Consider for example the following [code from the example app My Favorite +Drinks](https://github.com/remotestorage/myfavoritedrinks/blob/master/app.js#L33-L37), +which uses them to display the stored items: + +```js +remoteStorage.myfavoritedrinks.on('change', function(event) { + if (event.newValue && (! event.oldValue)) { + console.log('Change from '+event.origin+' (add)', event); + displayDrink(event.relativePath, event.newValue.name); + } +}); +``` + +The benefit of this approach is that the app displays items loaded from the +local cache during app startup, as well as items synchronized from the remote +storage afterwards! "Feeding two birds with one scone", as they say. + +Depending on your use case and app architecture, this means that there is no +need to distinguish between `local` and `remote` changes per se. + +## Option 2: Use `getAll()`, then update via events + +The second approach is to use the +[getAll()][1] function to load +all relevant documents on startup, and then use only `remote` change events to +add, update, and remove items when updates are being received from the remote +storage. + +Consider this code example: + +```js +const items = await client.getAll("/my-sub-folder"); + +for (const path in items) { + renderItem(path, items[path]); +} + +client.on('change', event => { + if (event.newValue) { + renderItem(path, items[path]); + } +}); +``` + +The benefit of this approach is that you can render all items at once, instead +of potentially flooding the screen with hundreds of items being added one by one as +they are loaded. + +However, when doing it this way, you have to ensure to either only listen to +change events with origin `remote`, or to register the event listener _after_ +you have loaded all available items with [getAll()][1]. + +::: tip +If you want [getAll()][1] to immediately return all locally cached items, and not +wait to check the remote storage for potential updates, set the optional `maxAge` +argument to `false`. +::: + +[1]: ../api/baseclient/classes/BaseClient.html#getall diff --git a/docs/getting-started/read-and-write-data.md b/docs/getting-started/read-and-write-data.md new file mode 100644 index 000000000..d9cc83233 --- /dev/null +++ b/docs/getting-started/read-and-write-data.md @@ -0,0 +1,58 @@ +# Reading and writing data + +As soon as your [RemoteStorage][1] instance is ready for action (signaled by +the `ready` event), we can start reading and writing data. + +## Anonymous mode + +One of the unique features of rs.js is that users are not required to +have their storage connected in order to use the app; you can require +connecting storage if it fits your use case. Any data written locally is +automatically synced to the remote storage server when connecting an +account. + +## Using BaseClient + +A [BaseClient][2] instance is the main endpoint for interacting with +storage: listing, reading, creating, updating and deleting documents, as +well as handling change events. + +::: warning TODO +Update paragraph, link directly to relevant section +::: +Check out the [BaseClient][2] in order +to learn about all functions available for reading and writing data and how to +use them. + +There are two options for acquiring a [BaseClient][2] instance: + +### Quick and dirty: creating a client via `scope()` + +::: tip NOTE +This should mainly be used for manually exploring client functions and locally in +development. +::: + +Using the [scope](../api/baseclient/classes/BaseClient.html#scope) method, +you can create a new [BaseClient][2] scoped to a given path: + +```js +const client = remoteStorage.scope('/foo/'); + +// List all items in the "foo/" category/folder +client.getListing('').then(listing => console.log(listing)); + +// Write some text to "foo/bar.txt" +const content = 'The most simple things can bring the most happiness.'; +client.storeFile('text/plain', 'bar.txt', content) + .then(() => console.log("data has been saved")); +``` + +### The recommended way: using clients in data modules + +The recommended way is to use the private and public [BaseClient][2] instances, +which are available in so-called [data modules](../data-modules/). Continue to +the next section in order to learn about them. + +[1]: ../api/remotestorage/classes/RemoteStorage.html +[2]: ../api/baseclient/classes/BaseClient.html diff --git a/doc/_images/cordova_oauth.png b/docs/images/cordova_oauth.png similarity index 100% rename from doc/_images/cordova_oauth.png rename to docs/images/cordova_oauth.png diff --git a/doc/rs-icon.svg b/docs/images/logo.svg similarity index 100% rename from doc/rs-icon.svg rename to docs/images/logo.svg diff --git a/doc/_images/screenshot-widget-choose.png b/docs/images/screenshot-widget-choose.png similarity index 100% rename from doc/_images/screenshot-widget-choose.png rename to docs/images/screenshot-widget-choose.png diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..e59caa798 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,21 @@ +--- +title: Documentation +description: remoteStorage.js Guides & Reference +--- + + + +# remoteStorage.js + +Welcome to the remoteStorage.js documentation! + +remoteStorage.js is a JavaScript/TypeScript library for storing user data +locally in the browser, as well as connecting to [remoteStorage](/) servers and +syncing data across devices and applications. It is also capable of connecting +and syncing data with a person\'s Dropbox or Google Drive account (optional). + +> [!NOTE] +> For brevity\'s sake, we will also use the short name **rs.js** across these docs. + +See [Why use this?](why) if you just learned about remoteStorage, or head to +[Getting started](getting-started/) to jump right in. diff --git a/docs/nodejs.md b/docs/nodejs.md new file mode 100644 index 000000000..cd46472f0 --- /dev/null +++ b/docs/nodejs.md @@ -0,0 +1,83 @@ +# Usage with Node.js + +Although remoteStorage.js was initially written for being used in +browsers, we do support using it in a Node.js environment as well. + +The main difference between rs.js in a browser and using it on a server +or in a CLI program is how to connect a storage. The RS protocol uses +the OAuth Implicit Grant flow for clients to receive a bearer token, +which they can use in HTTP requests. This works by redirecting back to +the Web application with the token attached to the redirect URI as a URI +fragment. + +Now, with rs.js in a browser, calling +`remoteStorage.connect('user@example.com')` will take care of the entire +OAuth process, including the parsing of the URI after the redirect, +saving the token to localStorage and changing the library\'s state to +connected. But in a node.js program, that\'s obviously not possible, +because there\'s no browser that will open the OAuth dialog and receive +the redirect with the token attached to the redirect URI. + +## connect() with a token + +For this reason, among others, you can call the connect function with a +token that you acquired beforehand: + +```JavaScript +remoteStorage.connect('user@example.com', 'abcdef123456') +``` + +This will skip the entire OAuth process, because you did that before in +some other way, of course. + +## Obtaining a token + +For some programs, like e.g. a server daemon, you can usually acquire +the token from your server manually, and then just configure it for +example as environment variable, when running your program. + +For CLI programs, and if you actually want to integrate the OAuth flow +in your program, one possible solution is the following: + +1. Set up a simple Web site/app, which you publish under a fitting + domain/URI that you can use as the OAuth redirect URI. +2. Have the user enter their user address and do a Webfinger lookup for + auth URL etc., e.g. using + [webfinger.js](https://www.npmjs.com/package/webfinger.js). +3. Create the OAuth request URI with the correct scope etc., and open a + browser window with that URI from your program (or prompt the user + to open it). +4. Have the Web app, which the user is being redirected to, show the + token to the user, in order for them to copy and enter in your + program +5. Connect with that token. + +You can find a complete example for this process in +[rs-backup](https://github.com/skddc/rs-backup), a remoteStorage backup +CLI program. In particular [its code for connecting a +storage](https://github.com/skddc/rs-backup/blob/v1.5.0/backup.js#L137-L160) +and the [simple Web page](https://github.com/skddc/rs-backup-auth-page) +its using for the redirect. + +::: tip NOTE +rs-backup is not using remoteStorage.js at all, which you might also +want to consider as an option when writing non-browser applications. +::: + +## Caveats + +- IndexedDB and localStorage are not supported by default in Node.js, + so the library will fall back to in-memory storage for caching data + locally. This means that unsynchronized data will be lost between + sessions and program executions. +- Node 18 includes `fetch` natively, but earlier versions do not, and so + it may be necessary to set `global.fetch` with a polyfill such as + [node-fetch](https://www.npmjs.com/package/node-fetch). + +## Examples + +- [hubot-remotestorage-logger](https://github.com/67P/hubot-remotestorage-logger), + a Hubot script that logs chat messages to remoteStorage-enabled + accounts using the + [chat-messages](https://www.npmjs.com/package/remotestorage-module-chat-messages) + module diff --git a/docs/typescript.md b/docs/typescript.md new file mode 100644 index 000000000..59cb736c9 --- /dev/null +++ b/docs/typescript.md @@ -0,0 +1,12 @@ +# Usage with TypeScript + +Since version `2.0.0`, the source code of rs.js itself has been ported +to TypeScript, and release builds now ship with generated type +definitions. Thus, there is no extra type definitions package to +import/require, and everything should just work out of the box. + +::: tip NOTE +There\'s still a lot of room for improvement in our TypeScript usage. If +you\'re experienced with TypeScript, and interested in contributing to +rs.js, your help in this area would be most appreciated. +::: diff --git a/docs/why.md b/docs/why.md new file mode 100644 index 000000000..ef8351137 --- /dev/null +++ b/docs/why.md @@ -0,0 +1,65 @@ +# Why use this? + +## Offline-first design + +rs.js stores data locally first and syncs data with a remote storage +account second. This makes it a robust sync solution for mobile +applications, where slow and spotty network connections are a normal +situation. + +Apps and use cases that don\'t require caching can keep selective data +locally while not caching the rest. + +When a backend goes down, users can just keep using the app and have +their data automatically synced as soon as the server is back online. + +## Zero backend + +rs.js is built for creating [unhosted](/unhosted) apps. Users can connect their +own storage account to apps on their devices, without needing to trust app +developers with private data. Developers can rapidly build apps without +investing in integrating, managing, maintaining, or securing data. + +A nice side effect of this design is that your app can scale to millions +of users with literally *zero* cost for storage. + +Also, if an app goes offline or is abandoned, people can continue to use +it across devices until they switch to a new one at their own pace. If +an abandoned app comes back at some point, many active users may still +be there. + +## Data sharing + +Different apps can access the same data, so you can build an app that +uses and manipulates existing data, without building import/export +features or having users start over from scratch. + +Even better, you can get advanced capabilities for free by using shared, +open-source [data modules](./data-modules/), which you can cooperate on with +other developers. + +For example: enable the sharing of files by simply integrating the [shares +module](https://github.com/skddc/remotestorage-module-shares), giving you +client-side thumbnail generation for images as well as other features for free. + +## Reliability + +The first prototype of rs.js was written in November 2010. Since then, +it has been used, tested, stabilized, and improved in over 4000 commits. +The library has been used in commercial apps by hundreds of thousands of +users, and in countries across the globe. Bugs and issues have been +noted and fixed for virtually every device, browser, privacy setting and +network connection there is. + +In short: you can rely on rs.js to do its job. And if you do find a +critical bug, there\'s a team of people who will help with fixing it. + +## One JS API for multiple storage options + +rs.js optionally supports Dropbox and Google Drive as storage backends which +users can connect. Conveniently, as an app developer you don\'t have to +implement anything special in order for these backends to work with your code. +Just [configure OAuth app keys](./dropbox-and-google-drive), and +your users can choose between 3 different backends to connect. If you're not +using the [connect widget](./getting-started/connect-widget), you may need to +create additional UI for these alternate backends. diff --git a/package-lock.json b/package-lock.json index 35dcf83b4..8ca1cbec4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,8 @@ "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^9.1.1", "@types/tv4": "^1.2.31", - "@typescript-eslint/eslint-plugin": "^5.37.0", - "@typescript-eslint/parser": "^5.37.0", + "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/parser": "^7.17.0", "babel-loader": "^8.2.2", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -32,12 +32,15 @@ "eslint": "^8.23.1", "fetch-mock": "^9.11.0", "jaribu": "^2.2.3", - "mocha": "^10.0.0", + "mocha": "^10.7.0", "sinon": "^14.0.0", "ts-loader": "^8.4.0", - "ts-node": "^10.9.1", - "typedoc": "^0.19.2", - "typescript": "^4.8.3", + "ts-node": "^10.9.2", + "typedoc": "^0.26.5", + "typedoc-plugin-markdown": "^4.2.2", + "typedoc-vitepress-theme": "^1.0.1", + "typescript": "^5.4.5", + "vitepress": "^1.3.1", "webpack": "^5.92.0", "webpack-cli": "^5.1.4" }, @@ -45,6 +48,199 @@ "fsevents": "^2.3.2" } }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "dev": true, + "dependencies": { + "@algolia/autocomplete-shared": "1.9.3" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "dev": true, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==", + "dev": true + }, + "node_modules/@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3" + } + }, + "node_modules/@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==", + "dev": true + }, + "node_modules/@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "dev": true, + "dependencies": { + "@algolia/logger-common": "4.23.3" + } + }, + "node_modules/@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==", + "dev": true + }, + "node_modules/@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "dev": true, + "dependencies": { + "@algolia/requester-common": "4.23.3" + } + }, + "node_modules/@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "dev": true, + "dependencies": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -487,9 +683,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1683,178 +1879,619 @@ "node": ">=10.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "node_modules/@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", + "dev": true + }, + "node_modules/@docsearch/js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", + "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", "dev": true, "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "@docsearch/react": "3.6.0", + "preact": "^10.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "node_modules/@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": ">=10.10.0" + "node": ">=12" } }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=12" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { @@ -1863,11 +2500,237 @@ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.11.0.tgz", + "integrity": "sha512-VbEhDAhT/2ozO0TPr5/ZQBO/NWLqtk4ZiBf6NplYpF38mKjNfMMied5fNEfIfYfN+cdKvhDB4VMcKvG/g9c3zg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/transformers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.11.0.tgz", + "integrity": "sha512-RNEUyOxF1cPYVG2EvBv0CZeDU1Tp4fSxmsVD2Ofv+8h9hBqqgpq+l+7uyouyqV1JHNlqwRmUwAqrQU3GQQ3csQ==", + "dev": true, + "dependencies": { + "shiki": "1.11.0" } }, "node_modules/@sinonjs/commons": { @@ -1970,12 +2833,43 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "node_modules/@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -1996,31 +2890,44 @@ "integrity": "sha512-P97XU07fcpauSw3/fE2Q7eF6bHl4oHhwkikjnM7zlQLENrdC2rZuHSdNlMBhnW82NyBEsVJHII1Jk3d/MtQsQQ==", "dev": true }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz", - "integrity": "sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/type-utils": "5.38.1", - "@typescript-eslint/utils": "5.38.1", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", + "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/type-utils": "7.17.0", + "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -2028,59 +2935,71 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@typescript-eslint/parser": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", + "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.1.tgz", - "integrity": "sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", + "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/typescript-estree": "5.38.1", - "debug": "^4.3.4" + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/utils": "7.17.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -2088,170 +3007,454 @@ } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz", - "integrity": "sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==", + "node_modules/@typescript-eslint/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", + "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/visitor-keys": "5.38.1" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.38.1.tgz", - "integrity": "sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", + "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.38.1", - "@typescript-eslint/utils": "5.38.1", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "*" - }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, - "node_modules/@typescript-eslint/types": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.1.tgz", - "integrity": "sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", + "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", + "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.17.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz", - "integrity": "sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==", + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.33.tgz", + "integrity": "sha512-MoIREbkdPQlnGfSKDMgzTqzqx5nmEjIc0ydLVYlTACGBsfvOJ4tHSbZXKVF536n6fB+0eZaGEOqsGThPpdvF5A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.33", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.33.tgz", + "integrity": "sha512-GzB8fxEHKw0gGet5BKlpfXEqoBnzSVWwMnT+dc25wE7pFEfrU/QsvjZMP9rD4iVXHBBoemTct8mN0GJEI6ZX5A==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.33.tgz", + "integrity": "sha512-7rk7Vbkn21xMwIUpHQR4hCVejwE6nvhBOiDgoBcR03qvGqRKA7dCBSsHZhwhYUsmjlbJ7OtD5UFIyhP6BY+c8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.33", + "@vue/compiler-dom": "3.4.33", + "@vue/compiler-ssr": "3.4.33", + "@vue/shared": "3.4.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.39", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.33.tgz", + "integrity": "sha512-0WveC9Ai+eT/1b6LCV5IfsufBZ0HP7pSSTdDjcuW302tTEgoBw8rHVHKPbGUtzGReUFCRXbv6zQDDgucnV2WzQ==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.3.6.tgz", + "integrity": "sha512-z6cKyxdXrIGgA++eyGBfquj6dCplRdgjt+I18fJx8hjWTXDTIyeQvryyEBMchnfZVyvUTjK3QjGjDpLCnJxPjw==", + "dev": true, + "dependencies": { + "@vue/devtools-kit": "^7.3.6" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.6.tgz", + "integrity": "sha512-5Ym9V3fkJenEoptqKoo+cgY5RTVwrSssFdzRsuyIgaeiskCT+rRJeQdwoo81tyrQ1mfS7Er1rYZlSzr3Y3L/ew==", + "dev": true, + "dependencies": { + "@vue/devtools-shared": "^7.3.6", + "birpc": "^0.2.17", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.6.tgz", + "integrity": "sha512-R/FOmdJV+hhuwcNoxp6e87RRkEeDMVhWH+nOsnHUrwjjsyeXJ2W1475Ozmw+cbZhejWQzftkHVKO28Fuo1yqCw==", + "dev": true, + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.33.tgz", + "integrity": "sha512-B24QIelahDbyHipBgbUItQblbd4w5HpG3KccL+YkGyo3maXyS253FzcTR3pSz739OTphmzlxP7JxEMWBpewilA==", + "dev": true, + "dependencies": { + "@vue/shared": "3.4.33" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.33.tgz", + "integrity": "sha512-6wavthExzT4iAxpe8q37/rDmf44nyOJGISJPxCi9YsQO+8w9v0gLCFLfH5TzD1V1AYrTAdiF4Y1cgUmP68jP6w==", + "dev": true, + "dependencies": { + "@vue/reactivity": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.33.tgz", + "integrity": "sha512-iHsMCUSFJ+4z432Bn9kZzHX+zOXa6+iw36DaVRmKYZpPt9jW9riF32SxNwB124i61kp9+AZtheQ/mKoJLerAaQ==", + "dev": true, + "dependencies": { + "@vue/reactivity": "3.4.33", + "@vue/runtime-core": "3.4.33", + "@vue/shared": "3.4.33", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.33.tgz", + "integrity": "sha512-jTH0d6gQcaYideFP/k0WdEu8PpRS9MF8d0b6SfZzNi+ap972pZ0TNIeTaESwdOtdY0XPVj54XEJ6K0wXxir4fw==", + "dev": true, + "dependencies": { + "@vue/compiler-ssr": "3.4.33", + "@vue/shared": "3.4.33" + }, + "peerDependencies": { + "vue": "3.4.33" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.33.tgz", + "integrity": "sha512-aoRY0jQk3A/cuvdkodTrM4NMfxco8n55eG4H7ML/CRy7OryHfiqvug4xrCBBMbbN+dvXAetDDwZW9DXWWjBntA==", + "dev": true + }, + "node_modules/@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.11.0.tgz", + "integrity": "sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==", + "dev": true, + "dependencies": { + "@vueuse/core": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^4", + "drauu": "^0.3", + "focus-trap": "^7", + "fuse.js": "^6", + "idb-keyval": "^6", + "jwt-decode": "^3", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^6" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/integrations/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/visitor-keys": "5.38.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" }, "peerDependenciesMeta": { - "typescript": { + "@vue/composition-api": { "optional": true } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@vueuse/shared": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz", + "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "vue-demi": ">=0.14.8" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.38.1.tgz", - "integrity": "sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==", + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/typescript-estree": "5.38.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz", - "integrity": "sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.38.1", - "eslint-visitor-keys": "^3.3.0" + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -2537,6 +3740,29 @@ "ajv": "^6.9.1" } }, + "node_modules/algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dev": true, + "dependencies": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, "node_modules/amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -2547,9 +3773,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -2625,15 +3851,6 @@ "node": "*" } }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2887,6 +4104,15 @@ "node": ">=8" } }, + "node_modules/birpc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.17.tgz", + "integrity": "sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3304,6 +4530,21 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/core-js": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", @@ -3351,6 +4592,12 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -3362,9 +4609,9 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -3575,6 +4822,18 @@ "node": ">=6.9.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/envinfo": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", @@ -3641,6 +4900,44 @@ "ext": "^1.1.2" } }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -3734,49 +5031,48 @@ } }, "node_modules/eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -3802,40 +5098,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -3900,9 +5172,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -3910,6 +5182,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -3922,9 +5197,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3966,14 +5241,14 @@ } }, "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3996,9 +5271,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4046,6 +5321,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4178,9 +5459,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -4227,9 +5508,9 @@ } }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -4397,6 +5678,15 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dev": true, + "dependencies": { + "tabbable": "^6.2.0" + } + }, "node_modules/foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", @@ -4421,21 +5711,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4443,9 +5718,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -4575,33 +5850,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -4677,14 +5931,11 @@ "he": "bin/he" } }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, - "engines": { - "node": "*" - } + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true }, "node_modules/http-errors": { "version": "2.0.0", @@ -4715,9 +5966,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -4873,6 +6124,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -4918,6 +6178,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4997,12 +6269,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", - "dev": true - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5063,18 +6329,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -5103,6 +6357,15 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -5290,6 +6553,15 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5311,18 +6583,35 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/marked": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", - "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, - "bin": { - "marked": "bin/marked" + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, - "engines": { - "node": ">= 8.16.2" + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -5439,34 +6728,44 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "node_modules/minisearch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.0.tgz", + "integrity": "sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==", + "dev": true + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "node_modules/mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -5474,16 +6773,21 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -5502,37 +6806,25 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5543,9 +6835,9 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -5554,15 +6846,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5596,18 +6879,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5863,9 +7134,9 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -5873,7 +7144,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -6005,6 +7276,12 @@ "node": "*" } }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -6087,6 +7364,62 @@ "node": ">=8" } }, + "node_modules/postcss": { + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6102,15 +7435,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protochain": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/protochain/-/protochain-1.0.5.tgz", @@ -6145,6 +7469,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -6295,18 +7628,6 @@ "@babel/runtime": "^7.8.4" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", @@ -6439,6 +7760,12 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6568,6 +7895,41 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6621,6 +7983,13 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/search-insights": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "dev": true, + "peer": true + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6676,9 +8045,9 @@ "dev": true }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -6747,42 +8116,14 @@ "node": ">=8" } }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shelljs/node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/shelljs/node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "node_modules/shiki": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.11.0.tgz", + "integrity": "sha512-NqH/O1zRHvnuk/WfSL6b7+DtI7/kkMMSQGlZhm9DyzSU+SoIHhaw/fBZMr+zp9R8KjdIzkk3JKSC6hORuGDyng==", "dev": true, "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" + "@shikijs/core": "1.11.0", + "@types/hast": "^3.0.4" } }, "node_modules/side-channel": { @@ -6865,6 +8206,15 @@ "node": ">=0.10.0" } }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -6875,6 +8225,15 @@ "source-map": "^0.6.0" } }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -6937,6 +8296,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superjson": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", + "dev": true, + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -6961,6 +8332,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -7040,15 +8417,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -7094,6 +8462,18 @@ "punycode": "^2.1.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-loader": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.4.0.tgz", @@ -7218,9 +8598,9 @@ "dev": true }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -7260,27 +8640,6 @@ } } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/tv4": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", @@ -7351,79 +8710,90 @@ } }, "node_modules/typedoc": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.2.tgz", - "integrity": "sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==", + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz", + "integrity": "sha512-Vn9YKdjKtDZqSk+by7beZ+xzkkr8T8CYoiasqyt4TTRFy5+UHzL/mF/o4wGBjRF+rlWQHDb0t6xCpA3JNL5phg==", "dev": true, "dependencies": { - "fs-extra": "^9.0.1", - "handlebars": "^4.7.6", - "highlight.js": "^10.2.0", - "lodash": "^4.17.20", "lunr": "^2.3.9", - "marked": "^1.1.1", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "semver": "^7.3.2", - "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.11.4" + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "shiki": "^1.9.1", + "yaml": "^2.4.5" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 10.0.0" + "node": ">= 18" }, "peerDependencies": { - "typescript": "3.9.x || 4.0.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x" } }, - "node_modules/typedoc-default-themes": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz", - "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==", + "node_modules/typedoc-plugin-markdown": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.2.tgz", + "integrity": "sha512-4Amnhjiw4L9aN5yBn6Ryh5WZr+uW41e6IU3EuQCNcVWgHQC+tlNIbbQMKVYAb33ES7yaM01dAXGS4BdJtQi7mA==", "dev": true, "engines": { - "node": ">= 8" + "node": ">= 18" + }, + "peerDependencies": { + "typedoc": "0.26.x" } }, - "node_modules/typedoc/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "node_modules/typedoc-vitepress-theme": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typedoc-vitepress-theme/-/typedoc-vitepress-theme-1.0.1.tgz", + "integrity": "sha512-pnpgzSQRaR9QLMl3it/tjq7vlV+eeUzKa22w/xF6ZUdAcYdmeag13kuA6EKfU7/kkIkJ/qsu1GPd3OcIC36Hlw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "peerDependencies": { + "typedoc-plugin-markdown": ">=4.1.0" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, - "node_modules/uglify-js": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", - "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", - "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true }, "node_modules/undici-types": { "version": "5.26.5", @@ -7470,15 +8840,6 @@ "node": ">=4" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -7567,19 +8928,134 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", + "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.39", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.3.1.tgz", + "integrity": "sha512-soZDpg2rRVJNIM/IYMNDPPr+zTHDA5RbLDHAxacRu+Q9iZ2GwSR0QSUlLs+aEZTkG0SOX1dc8RmUYwyuxK8dfQ==", + "dev": true, + "dependencies": { + "@docsearch/css": "^3.6.0", + "@docsearch/js": "^3.6.0", + "@shikijs/core": "^1.10.3", + "@shikijs/transformers": "^1.10.3", + "@types/markdown-it": "^14.1.1", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/devtools-api": "^7.3.5", + "@vue/shared": "^3.4.31", + "@vueuse/core": "^10.11.0", + "@vueuse/integrations": "^10.11.0", + "focus-trap": "^7.5.4", + "mark.js": "8.11.1", + "minisearch": "^7.0.0", + "shiki": "^1.10.3", + "vite": "^5.3.3", + "vue": "^3.4.31" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.33.tgz", + "integrity": "sha512-VdMCWQOummbhctl4QFMcW6eNtXHsFyDlX60O/tsSQuCcuDOnJ1qPOhhVla65Niece7xq/P2zyZReIO5mP+LGTQ==", "dev": true, - "engines": { - "node": ">= 0.8" + "dependencies": { + "@vue/compiler-dom": "3.4.33", + "@vue/compiler-sfc": "3.4.33", + "@vue/runtime-dom": "3.4.33", + "@vue/server-renderer": "3.4.33", + "@vue/shared": "3.4.33" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/walk": { @@ -7860,16 +9336,10 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -7945,6 +9415,18 @@ "node": ">=0.10.32" } }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -7964,9 +9446,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" @@ -8019,6 +9501,189 @@ } }, "dependencies": { + "@algolia/autocomplete-core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", + "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "dev": true, + "requires": { + "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", + "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "dev": true, + "requires": { + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-preset-algolia": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", + "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "dev": true, + "requires": { + "@algolia/autocomplete-shared": "1.9.3" + } + }, + "@algolia/autocomplete-shared": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", + "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "dev": true, + "requires": {} + }, + "@algolia/cache-browser-local-storage": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", + "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "dev": true, + "requires": { + "@algolia/cache-common": "4.23.3" + } + }, + "@algolia/cache-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", + "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==", + "dev": true + }, + "@algolia/cache-in-memory": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", + "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "dev": true, + "requires": { + "@algolia/cache-common": "4.23.3" + } + }, + "@algolia/client-account": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", + "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "dev": true, + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-analytics": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", + "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "dev": true, + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", + "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "dev": true, + "requires": { + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-personalization": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", + "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "dev": true, + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/client-search": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", + "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "dev": true, + "requires": { + "@algolia/client-common": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/logger-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", + "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==", + "dev": true + }, + "@algolia/logger-console": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", + "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "dev": true, + "requires": { + "@algolia/logger-common": "4.23.3" + } + }, + "@algolia/recommend": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", + "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", + "dev": true, + "requires": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, + "@algolia/requester-browser-xhr": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", + "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "dev": true, + "requires": { + "@algolia/requester-common": "4.23.3" + } + }, + "@algolia/requester-common": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", + "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==", + "dev": true + }, + "@algolia/requester-node-http": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", + "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "dev": true, + "requires": { + "@algolia/requester-common": "4.23.3" + } + }, + "@algolia/transporter": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", + "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "dev": true, + "requires": { + "@algolia/cache-common": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/requester-common": "4.23.3" + } + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -8351,9 +10016,9 @@ } }, "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -9088,95 +10753,299 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true - } - } + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + }, + "@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@docsearch/css": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", + "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", + "dev": true + }, + "@docsearch/js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", + "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", + "dev": true, + "requires": { + "@docsearch/react": "3.6.0", + "preact": "^10.0.0" + } + }, + "@docsearch/react": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", + "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "dev": true, + "requires": { + "@algolia/autocomplete-core": "1.9.3", + "@algolia/autocomplete-preset-algolia": "1.9.3", + "@docsearch/css": "3.6.0", + "algoliasearch": "^4.19.1" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "dev": true, + "optional": true }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } + "optional": true }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } + "optional": true }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } + "optional": true }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } + "eslint-visitor-keys": "^3.3.0" } }, - "@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true }, "@eslint/eslintrc": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -9185,9 +11054,9 @@ }, "dependencies": { "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -9195,23 +11064,23 @@ } } }, + "@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", - "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" } }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true - }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -9219,9 +11088,9 @@ "dev": true }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "@jridgewell/gen-mapping": { @@ -9270,9 +11139,9 @@ } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { @@ -9311,6 +11180,136 @@ "fastq": "^1.6.0" } }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "dev": true, + "optional": true + }, + "@shikijs/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.11.0.tgz", + "integrity": "sha512-VbEhDAhT/2ozO0TPr5/ZQBO/NWLqtk4ZiBf6NplYpF38mKjNfMMied5fNEfIfYfN+cdKvhDB4VMcKvG/g9c3zg==", + "dev": true, + "requires": { + "@types/hast": "^3.0.4" + } + }, + "@shikijs/transformers": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.11.0.tgz", + "integrity": "sha512-RNEUyOxF1cPYVG2EvBv0CZeDU1Tp4fSxmsVD2Ofv+8h9hBqqgpq+l+7uyouyqV1JHNlqwRmUwAqrQU3GQQ3csQ==", + "dev": true, + "requires": { + "shiki": "1.11.0" + } + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -9411,12 +11410,43 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true + }, + "@types/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "dev": true, + "requires": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -9437,159 +11467,348 @@ "integrity": "sha512-P97XU07fcpauSw3/fE2Q7eF6bHl4oHhwkikjnM7zlQLENrdC2rZuHSdNlMBhnW82NyBEsVJHII1Jk3d/MtQsQQ==", "dev": true }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz", - "integrity": "sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/type-utils": "5.38.1", - "@typescript-eslint/utils": "5.38.1", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", + "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/type-utils": "7.17.0", + "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/parser": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.1.tgz", - "integrity": "sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/typescript-estree": "5.38.1", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz", - "integrity": "sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", + "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", "dev": true, "requires": { - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/visitor-keys": "5.38.1" + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0" } }, "@typescript-eslint/type-utils": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.38.1.tgz", - "integrity": "sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", + "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.38.1", - "@typescript-eslint/utils": "5.38.1", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/utils": "7.17.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.1.tgz", - "integrity": "sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", + "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz", - "integrity": "sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", + "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/visitor-keys": "5.38.1", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "yallist": "^4.0.0" + "balanced-match": "^1.0.0" } }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "brace-expansion": "^2.0.1" } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true } } }, - "@typescript-eslint/utils": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.38.1.tgz", - "integrity": "sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==", + "@typescript-eslint/utils": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", + "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", + "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "7.17.0", + "eslint-visitor-keys": "^3.4.3" + } + }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "@vitejs/plugin-vue": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "dev": true, + "requires": {} + }, + "@vue/compiler-core": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.33.tgz", + "integrity": "sha512-MoIREbkdPQlnGfSKDMgzTqzqx5nmEjIc0ydLVYlTACGBsfvOJ4tHSbZXKVF536n6fB+0eZaGEOqsGThPpdvF5A==", + "dev": true, + "requires": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.33", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-dom": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.33.tgz", + "integrity": "sha512-GzB8fxEHKw0gGet5BKlpfXEqoBnzSVWwMnT+dc25wE7pFEfrU/QsvjZMP9rD4iVXHBBoemTct8mN0GJEI6ZX5A==", + "dev": true, + "requires": { + "@vue/compiler-core": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "@vue/compiler-sfc": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.33.tgz", + "integrity": "sha512-7rk7Vbkn21xMwIUpHQR4hCVejwE6nvhBOiDgoBcR03qvGqRKA7dCBSsHZhwhYUsmjlbJ7OtD5UFIyhP6BY+c8A==", + "dev": true, + "requires": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.33", + "@vue/compiler-dom": "3.4.33", + "@vue/compiler-ssr": "3.4.33", + "@vue/shared": "3.4.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.39", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-ssr": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.33.tgz", + "integrity": "sha512-0WveC9Ai+eT/1b6LCV5IfsufBZ0HP7pSSTdDjcuW302tTEgoBw8rHVHKPbGUtzGReUFCRXbv6zQDDgucnV2WzQ==", + "dev": true, + "requires": { + "@vue/compiler-dom": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "@vue/devtools-api": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.3.6.tgz", + "integrity": "sha512-z6cKyxdXrIGgA++eyGBfquj6dCplRdgjt+I18fJx8hjWTXDTIyeQvryyEBMchnfZVyvUTjK3QjGjDpLCnJxPjw==", + "dev": true, + "requires": { + "@vue/devtools-kit": "^7.3.6" + } + }, + "@vue/devtools-kit": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.3.6.tgz", + "integrity": "sha512-5Ym9V3fkJenEoptqKoo+cgY5RTVwrSssFdzRsuyIgaeiskCT+rRJeQdwoo81tyrQ1mfS7Er1rYZlSzr3Y3L/ew==", + "dev": true, + "requires": { + "@vue/devtools-shared": "^7.3.6", + "birpc": "^0.2.17", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" + } + }, + "@vue/devtools-shared": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.3.6.tgz", + "integrity": "sha512-R/FOmdJV+hhuwcNoxp6e87RRkEeDMVhWH+nOsnHUrwjjsyeXJ2W1475Ozmw+cbZhejWQzftkHVKO28Fuo1yqCw==", + "dev": true, + "requires": { + "rfdc": "^1.4.1" + } + }, + "@vue/reactivity": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.33.tgz", + "integrity": "sha512-B24QIelahDbyHipBgbUItQblbd4w5HpG3KccL+YkGyo3maXyS253FzcTR3pSz739OTphmzlxP7JxEMWBpewilA==", + "dev": true, + "requires": { + "@vue/shared": "3.4.33" + } + }, + "@vue/runtime-core": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.33.tgz", + "integrity": "sha512-6wavthExzT4iAxpe8q37/rDmf44nyOJGISJPxCi9YsQO+8w9v0gLCFLfH5TzD1V1AYrTAdiF4Y1cgUmP68jP6w==", + "dev": true, + "requires": { + "@vue/reactivity": "3.4.33", + "@vue/shared": "3.4.33" + } + }, + "@vue/runtime-dom": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.33.tgz", + "integrity": "sha512-iHsMCUSFJ+4z432Bn9kZzHX+zOXa6+iw36DaVRmKYZpPt9jW9riF32SxNwB124i61kp9+AZtheQ/mKoJLerAaQ==", "dev": true, "requires": { - "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.38.1", - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/typescript-estree": "5.38.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@vue/reactivity": "3.4.33", + "@vue/runtime-core": "3.4.33", + "@vue/shared": "3.4.33", + "csstype": "^3.1.3" } }, - "@typescript-eslint/visitor-keys": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz", - "integrity": "sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==", + "@vue/server-renderer": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.33.tgz", + "integrity": "sha512-jTH0d6gQcaYideFP/k0WdEu8PpRS9MF8d0b6SfZzNi+ap972pZ0TNIeTaESwdOtdY0XPVj54XEJ6K0wXxir4fw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.38.1", - "eslint-visitor-keys": "^3.3.0" + "@vue/compiler-ssr": "3.4.33", + "@vue/shared": "3.4.33" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "@vue/shared": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.33.tgz", + "integrity": "sha512-aoRY0jQk3A/cuvdkodTrM4NMfxco8n55eG4H7ML/CRy7OryHfiqvug4xrCBBMbbN+dvXAetDDwZW9DXWWjBntA==", + "dev": true + }, + "@vueuse/core": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", + "dev": true, + "requires": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "requires": {} + } + } + }, + "@vueuse/integrations": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.11.0.tgz", + "integrity": "sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==", + "dev": true, + "requires": { + "@vueuse/core": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "requires": {} + } + } + }, + "@vueuse/metadata": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", "dev": true }, + "@vueuse/shared": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz", + "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==", + "dev": true, + "requires": { + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "dev": true, + "requires": {} + } + } + }, "@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -9830,6 +12049,29 @@ "dev": true, "requires": {} }, + "algoliasearch": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", + "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", + "dev": true, + "requires": { + "@algolia/cache-browser-local-storage": "4.23.3", + "@algolia/cache-common": "4.23.3", + "@algolia/cache-in-memory": "4.23.3", + "@algolia/client-account": "4.23.3", + "@algolia/client-analytics": "4.23.3", + "@algolia/client-common": "4.23.3", + "@algolia/client-personalization": "4.23.3", + "@algolia/client-search": "4.23.3", + "@algolia/logger-common": "4.23.3", + "@algolia/logger-console": "4.23.3", + "@algolia/recommend": "4.23.3", + "@algolia/requester-browser-xhr": "4.23.3", + "@algolia/requester-common": "4.23.3", + "@algolia/requester-node-http": "4.23.3", + "@algolia/transporter": "4.23.3" + } + }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -9837,9 +12079,9 @@ "dev": true }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-regex": { @@ -9897,12 +12139,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -10116,6 +12352,12 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "birpc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.17.tgz", + "integrity": "sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==", + "dev": true + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -10424,6 +12666,15 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "requires": { + "is-what": "^4.1.8" + } + }, "core-js": { "version": "2.6.12", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", @@ -10462,6 +12713,12 @@ "which": "^2.0.1" } }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -10473,9 +12730,9 @@ } }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, "requires": { "ms": "2.1.2" @@ -10625,6 +12882,12 @@ "tapable": "^1.0.0" } }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, "envinfo": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", @@ -10678,6 +12941,37 @@ "ext": "^1.1.2" } }, + "esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -10755,49 +13049,48 @@ } }, "eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -10842,9 +13135,9 @@ "dev": true }, "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -10858,9 +13151,9 @@ "dev": true }, "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -10893,27 +13186,10 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "esm": { @@ -10922,14 +13198,14 @@ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -10939,9 +13215,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -10978,6 +13254,12 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -11088,9 +13370,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -11130,9 +13412,9 @@ "dev": true }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -11257,6 +13539,15 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "dev": true, + "requires": { + "tabbable": "^6.2.0" + } + }, "foreachasync": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", @@ -11275,18 +13566,6 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -11294,9 +13573,9 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "function-bind": { @@ -11389,25 +13668,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -11461,10 +13727,10 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "dev": true }, "http-errors": { @@ -11490,9 +13756,9 @@ } }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true }, "import-fresh": { @@ -11603,6 +13869,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -11636,6 +13908,12 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -11699,12 +13977,6 @@ } } }, - "js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11750,16 +14022,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -11782,6 +14044,15 @@ "type-check": "~0.4.0" } }, + "linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "requires": { + "uc.micro": "^2.0.0" + } + }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -11929,6 +14200,15 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -11944,10 +14224,30 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "marked": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", - "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", + "mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true + }, + "markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "requires": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + } + }, + "mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true }, "media-typer": { @@ -12036,40 +14336,59 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "minisearch": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.0.tgz", + "integrity": "sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==", + "dev": true + }, + "mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", - "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", - "dev": true, - "requires": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "nanoid": "3.3.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "dependencies": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true }, "escape-string-regexp": { @@ -12079,28 +14398,16 @@ "dev": true }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^5.0.1", + "once": "^1.3.0" } }, "has-flag": { @@ -12110,23 +14417,12 @@ "dev": true }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } } }, "ms": { @@ -12158,12 +14454,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -12366,9 +14656,9 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "requires": { "deep-is": "^0.1.3", @@ -12376,7 +14666,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" } }, "os-homedir": { @@ -12466,6 +14756,12 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, + "perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true + }, "picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -12526,6 +14822,31 @@ } } }, + "postcss": { + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "dev": true, + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "dependencies": { + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true + } + } + }, + "preact": { + "version": "10.22.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.0.tgz", + "integrity": "sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -12538,12 +14859,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "protochain": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/protochain/-/protochain-1.0.5.tgz", @@ -12572,6 +14887,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -12683,12 +15004,6 @@ "@babel/runtime": "^7.8.4" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "regexpu-core": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", @@ -12784,6 +15099,12 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -12910,6 +15231,32 @@ } } }, + "rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12942,6 +15289,13 @@ "ajv-keywords": "^3.5.2" } }, + "search-insights": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.14.0.tgz", + "integrity": "sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==", + "dev": true, + "peer": true + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -12995,9 +15349,9 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -13054,32 +15408,14 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "shiki": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.11.0.tgz", + "integrity": "sha512-NqH/O1zRHvnuk/WfSL6b7+DtI7/kkMMSQGlZhm9DyzSU+SoIHhaw/fBZMr+zp9R8KjdIzkk3JKSC6hORuGDyng==", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "dependencies": { - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - } + "@shikijs/core": "1.11.0", + "@types/hast": "^3.0.4" } }, "side-channel": { @@ -13142,6 +15478,12 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "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==", + "dev": true + }, "source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -13152,6 +15494,12 @@ "source-map": "^0.6.0" } }, + "speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -13199,6 +15547,15 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "superjson": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", + "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", + "dev": true, + "requires": { + "copy-anything": "^3.0.2" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13214,6 +15571,12 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -13255,15 +15618,6 @@ "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } - }, - "serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } } } }, @@ -13303,6 +15657,13 @@ "punycode": "^2.1.0" } }, + "ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "requires": {} + }, "ts-loader": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.4.0.tgz", @@ -13392,9 +15753,9 @@ } }, "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", @@ -13412,21 +15773,6 @@ "yn": "3.1.1" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "tv4": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", @@ -13479,50 +15825,63 @@ } }, "typedoc": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.2.tgz", - "integrity": "sha512-oDEg1BLEzi1qvgdQXc658EYgJ5qJLVSeZ0hQ57Eq4JXy6Vj2VX4RVo18qYxRWz75ifAaYuYNBUCnbhjd37TfOg==", + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.5.tgz", + "integrity": "sha512-Vn9YKdjKtDZqSk+by7beZ+xzkkr8T8CYoiasqyt4TTRFy5+UHzL/mF/o4wGBjRF+rlWQHDb0t6xCpA3JNL5phg==", "dev": true, "requires": { - "fs-extra": "^9.0.1", - "handlebars": "^4.7.6", - "highlight.js": "^10.2.0", - "lodash": "^4.17.20", "lunr": "^2.3.9", - "marked": "^1.1.1", - "minimatch": "^3.0.0", - "progress": "^2.0.3", - "semver": "^7.3.2", - "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.11.4" + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "shiki": "^1.9.1", + "yaml": "^2.4.5" }, "dependencies": { - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } } } }, - "typedoc-default-themes": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz", - "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==", - "dev": true + "typedoc-plugin-markdown": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.2.2.tgz", + "integrity": "sha512-4Amnhjiw4L9aN5yBn6Ryh5WZr+uW41e6IU3EuQCNcVWgHQC+tlNIbbQMKVYAb33ES7yaM01dAXGS4BdJtQi7mA==", + "dev": true, + "requires": {} + }, + "typedoc-vitepress-theme": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typedoc-vitepress-theme/-/typedoc-vitepress-theme-1.0.1.tgz", + "integrity": "sha512-pnpgzSQRaR9QLMl3it/tjq7vlV+eeUzKa22w/xF6ZUdAcYdmeag13kuA6EKfU7/kkIkJ/qsu1GPd3OcIC36Hlw==", + "dev": true, + "requires": {} }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, - "uglify-js": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.18.0.tgz", - "integrity": "sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==", - "dev": true, - "optional": true + "uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true }, "undici-types": { "version": "5.26.5", @@ -13557,12 +15916,6 @@ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true }, - "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -13630,6 +15983,55 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true }, + "vite": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", + "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", + "dev": true, + "requires": { + "esbuild": "^0.21.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.39", + "rollup": "^4.13.0" + } + }, + "vitepress": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.3.1.tgz", + "integrity": "sha512-soZDpg2rRVJNIM/IYMNDPPr+zTHDA5RbLDHAxacRu+Q9iZ2GwSR0QSUlLs+aEZTkG0SOX1dc8RmUYwyuxK8dfQ==", + "dev": true, + "requires": { + "@docsearch/css": "^3.6.0", + "@docsearch/js": "^3.6.0", + "@shikijs/core": "^1.10.3", + "@shikijs/transformers": "^1.10.3", + "@types/markdown-it": "^14.1.1", + "@vitejs/plugin-vue": "^5.0.5", + "@vue/devtools-api": "^7.3.5", + "@vue/shared": "^3.4.31", + "@vueuse/core": "^10.11.0", + "@vueuse/integrations": "^10.11.0", + "focus-trap": "^7.5.4", + "mark.js": "8.11.1", + "minisearch": "^7.0.0", + "shiki": "^1.10.3", + "vite": "^5.3.3", + "vue": "^3.4.31" + } + }, + "vue": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.33.tgz", + "integrity": "sha512-VdMCWQOummbhctl4QFMcW6eNtXHsFyDlX60O/tsSQuCcuDOnJ1qPOhhVla65Niece7xq/P2zyZReIO5mP+LGTQ==", + "dev": true, + "requires": { + "@vue/compiler-dom": "3.4.33", + "@vue/compiler-sfc": "3.4.33", + "@vue/runtime-dom": "3.4.33", + "@vue/server-renderer": "3.4.33", + "@vue/shared": "3.4.33" + } + }, "walk": { "version": "2.3.15", "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.15.tgz", @@ -13838,16 +16240,10 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { @@ -13904,6 +16300,12 @@ "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", "dev": true }, + "yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -13928,9 +16330,9 @@ } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index dd4c42c8e..085b578ba 100644 --- a/package.json +++ b/package.json @@ -23,13 +23,13 @@ "build:js": "tsc -d --declarationDir release/types --declarationMap", "build:release": "NODE_ENV=production webpack --mode=production", "build:dev": "webpack --mode=development", - "build:docs": "cd doc && sphinx-build -v . _build/html", "dev": "webpack --mode=development -w", "postshrinkwrap": "if [ \"`uname`\" = \"Darwin\" ]; then sed -i '' -e 's/http:\\/\\//https:\\/\\//g' package-lock.json; else sed -i -e 's/http:\\/\\//https:\\/\\//g' package-lock.json; fi", "preversion": "npm test && npm run build:js", - "version": "npm run build:release && git add release/ && npm run update-doc-version && git add doc/version.py", - "update-doc-version": "bash scripts/update-doc-version.sh", - "autobuild-docs": "cd doc && sphinx-autobuild . _build/html" + "version": "npm run build:release && git add release/", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" }, "devDependencies": { "@babel/core": "^7.19.0", @@ -38,8 +38,8 @@ "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^9.1.1", "@types/tv4": "^1.2.31", - "@typescript-eslint/eslint-plugin": "^5.37.0", - "@typescript-eslint/parser": "^5.37.0", + "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/parser": "^7.17.0", "babel-loader": "^8.2.2", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -47,12 +47,15 @@ "eslint": "^8.23.1", "fetch-mock": "^9.11.0", "jaribu": "^2.2.3", - "mocha": "^10.0.0", + "mocha": "^10.7.0", "sinon": "^14.0.0", "ts-loader": "^8.4.0", - "ts-node": "^10.9.1", - "typedoc": "^0.19.2", - "typescript": "^4.8.3", + "ts-node": "^10.9.2", + "typedoc": "^0.26.5", + "typedoc-plugin-markdown": "^4.2.2", + "typedoc-vitepress-theme": "^1.0.1", + "typescript": "^5.4.5", + "vitepress": "^1.3.1", "webpack": "^5.92.0", "webpack-cli": "^5.1.4" }, diff --git a/scripts/update-doc-version.sh b/scripts/update-doc-version.sh deleted file mode 100755 index df8f18d66..000000000 --- a/scripts/update-doc-version.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -e - -echo "__version__ = '$(node -p "var config = require('./package.json'); config.version")'" > doc/version.py diff --git a/src/access.ts b/src/access.ts index 682f61023..47c69890e 100644 --- a/src/access.ts +++ b/src/access.ts @@ -1,7 +1,5 @@ -// TODO maybe move to common interfaces & types file -// also worth considering enums -type AccessMode = 'r' | 'rw'; -type AccessScope = string; +export type AccessMode = 'r' | 'rw'; +export type AccessScope = string; interface ScopeEntry { name: string; @@ -14,11 +12,12 @@ interface ScopeModeMap { } /** - * @class Access + * @class * - * Keeps track of claimed access and scopes. + * This class is for requesting and managing access to modules/folders on the + * remote. It gets initialized as `remoteStorage.access`. */ -class Access { +export class Access { scopeModeMap: ScopeModeMap; rootPaths: string[]; storageType: string; @@ -33,10 +32,13 @@ class Access { } /** - * Property: scopes + * Holds an array of claimed scopes: * - * Holds an array of claimed scopes in the form - * > { name: "", mode: "" } + * ```javascript + * [{ name: "", mode: "" }] + * ``` + * + * @ignore */ get scopes(): ScopeEntry[] { return Object.keys(this.scopeModeMap).map((key) => { @@ -53,8 +55,20 @@ class Access { /** * Claim access on a given scope with given mode. * - * @param {string} scope - An access scope, such as "contacts" or "calendar" - * @param {string} mode - Access mode. Either "r" for read-only or "rw" for read/write + * @param scope - An access scope, such as `contacts` or `calendar` + * @param mode - Access mode. Either `r` for read-only or `rw` for read/write + * + * @example + * ```javascript + * remoteStorage.access.claim('contacts', 'r'); + * remoteStorage.access.claim('pictures', 'rw'); + * ``` + * + * Claiming root access, meaning complete access to all files and folders of a storage, can be done using an asterisk for the scope: + * + * ```javascript + * remoteStorage.access.claim('*', 'rw'); + * ``` */ claim (scope: AccessScope, mode: AccessMode): void { if (typeof (scope) !== 'string' || scope.indexOf('/') !== -1 || scope.length === 0) { @@ -70,8 +84,9 @@ class Access { /** * Get the access mode for a given scope. * - * @param {string} scope - Access scope - * @returns {string} Access mode + * @param scope - Access scope + * @returns Access mode + * @ignore */ get (scope: AccessScope): AccessMode { return this.scopeModeMap[scope]; @@ -81,7 +96,8 @@ class Access { /** * Remove access for the given scope. * - * @param {string} scope - Access scope + * @param scope - Access scope + * @ignore */ remove (scope: AccessScope): void { const savedMap: ScopeModeMap = {}; @@ -98,9 +114,10 @@ class Access { /** * Verify permission for a given scope. * - * @param {string} scope - Access scope - * @param {string} mode - Access mode - * @returns {boolean} true if the requested access mode is active, false otherwise + * @param scope - Access scope + * @param mode - Access mode + * @returns `true` if the requested access mode is active, `false` otherwise + * @ignore */ checkPermission (scope: AccessScope, mode: AccessMode): boolean { const actualMode = this.get(scope); @@ -110,9 +127,10 @@ class Access { /** * Verify permission for a given path. * - * @param {string} path - Path - * @param {string} mode - Access mode - * @returns {boolean} true if the requested access mode is active, false otherwise + * @param path - Path + * @param mode - Access mode + * @returns true if the requested access mode is active, false otherwise + * @ignore */ checkPathPermission (path: string, mode: AccessMode): boolean { if (this.checkPermission('*', mode)) { @@ -125,6 +143,8 @@ class Access { /** * Reset all access permissions. + * + * @ignore */ reset(): void { this.rootPaths = []; @@ -171,11 +191,12 @@ class Access { /** * Set the storage type of the remote. * - * @param {string} type - Storage type + * @param type - Storage type + * @internal */ setStorageType (type: string): void { this.storageType = type; } } -export = Access; +export default Access; diff --git a/src/authorize.ts b/src/authorize.ts index 09de233c6..241952bb8 100644 --- a/src/authorize.ts +++ b/src/authorize.ts @@ -1,12 +1,10 @@ import log from './log'; import RemoteStorage from './remotestorage'; -import {localStorageAvailable, globalContext, toBase64} from './util'; +import { localStorageAvailable, globalContext, toBase64 } from './util'; import UnauthorizedError from './unauthorized-error'; -import { EventHandler } from './interfaces/event_handling'; -import {requestWithTimeout} from "./requests"; -import {AuthorizeOptions} from "./interfaces/authorize_options"; -import {Remote} from "./remote"; - +import { EventHandler } from './eventhandling'; +import { requestWithTimeout } from "./requests"; +import { Remote } from "./remote"; interface AuthResult { access_token?: string; @@ -26,6 +24,24 @@ interface InAppBrowserEvent extends Event { data?: string; } +export interface AuthorizeOptions { + /** URL of the authorization endpoint */ + authURL: string; + /** access scope */ + scope?: string; + redirectUri?: string; + /** + * client identifier + * @defaultValue Origin of the redirectUri + * */ + clientId?: string; + response_type?: 'token' | 'code'; + state?: string; + code_challenge?: string; + code_challenge_method?: 'S256' | 'plain'; + token_access_type?: 'online' | 'offline'; // Dropbox only +} + // This is set in _rs_init and needed for removal in _rs_cleanup let onFeaturesLoaded: EventHandler; @@ -98,7 +114,7 @@ function buildOAuthURL (options: AuthorizeOptions): string { return url.href; } -class Authorize { +export class Authorize { static IMPLIED_FAKE_TOKEN = false; /** @@ -352,4 +368,4 @@ class Authorize { } } -export = Authorize; +export default Authorize; diff --git a/src/baseclient.ts b/src/baseclient.ts index c69c57a95..af624ce76 100644 --- a/src/baseclient.ts +++ b/src/baseclient.ts @@ -15,19 +15,226 @@ function getModuleNameFromBase(path: string): string { } /** - * Provides a high-level interface to access data below a given root path. + * A `BaseClient` instance is the main endpoint you will use for interacting + * with a connected storage: listing, reading, creating, updating and deleting + * documents, as well as handling incoming changes. + * + * Base clients are usually used in [data modules](../../../data-modules/), + * which are loaded with two `BaseClient` instances by default: one for private + * and one for public documents. + * + * However, you can also instantiate a BaseClient outside of a data module using + * the `remoteStorage.scope()` function. Similarly, you can create a new scoped + * client within another client, using the `BaseClient`'s own {@link scope}. + * + * ## Read/write operations + * + * A `BaseClient` deals with three types of data: folders, objects and files. + * + * * {@link getListing} returns a mapping of all items within a folder. + * + * * {@link getObject} and {@link storeObject} operate on JSON objects. Each object + * has a type. + * + * * {@link getFile} and {@link storeFile} operates on files. Each file has a MIME + * type. + * + * * {@link getAll} returns all objects or files for the given folder path. + * + * * {@link remove} operates on either objects or files (but not folders; folders + * are created and removed implictly). + * + * ## Caching logic for read operations + * + * All functions requesting/reading data will immediately return data from the + * local store, *as long as it is reasonably up-to-date*. If data is assumed to be + * potentially outdated, they will check the remote storage for changes first, and then + * return the requested data. + * + * The default maximum age of requested data is two times the periodic sync + * interval (10 seconds by default). + * + * However, you can adjust this behavior by using the `maxAge` argument with any + * of these functions, thereby changing the maximum age or removing the + * requirement entirely. + * + * * If the `maxAge` requirement is set, and the last sync request for the path + * is further in the past than the maximum age given, the folder will first be + * checked for changes on the remote, and then the promise will be fulfilled + * with the up-to-date document or listing. + * + * * If the `maxAge` requirement is set, and cannot be met because of network + * problems, the promise will be rejected. + * + * * If the `maxAge` requirement is set to `false`, or the library is in + * offline mode, or no remote storage is connected (a.k.a. "anonymous mode"), + * the promise will always be fulfilled with data from the local store. + * + * > [!NOTE] + * > If {@link caching!Caching caching} for the folder is turned off, none of + * > this applies and data will always be requested from the remote store + * > directly. + * + * ## Change events + * + * A `BaseClient` emits only one type of event, named `change`, which you can add + * a handler for using the `.on()` function (same as with {@link RemoteStorage}): + * + * ```js + * client.on('change', function (evt) { + * console.log('data was added, updated, or removed:', evt) + * }); + * ``` + * + * Using this event, your app can stay informed about data changes, both remote + * (from other devices or browsers), as well as locally (e.g. other browser tabs). + * + * In order to determine where a change originated from, look at the `origin` + * property of the event. Possible values are `window`, `local`, `remote`, and + * `conflict`, explained in detail below. + * + * #### Example + * + * ```js + * { + * // Absolute path of the changed node, from the storage root + * path: path, + * // Path of the changed node, relative to this baseclient's scope root + * relativePath: relativePath, + * // See origin descriptions below + * origin: 'window|local|remote|conflict', + * // Old body of the changed node (local version in conflicts; undefined if creation) + * oldValue: oldBody, + * // New body of the changed node (remote version in conflicts; undefined if deletion) + * newValue: newBody, + * // Body when local and remote last agreed; only present in conflict events + * lastCommonValue: lastCommonBody, + * // Old contentType of the changed node (local version for conflicts; undefined if creation) + * oldContentType: oldContentType, + * // New contentType of the changed node (remote version for conflicts; undefined if deletion) + * newContentType: newContentType, + * // ContentType when local and remote last agreed; only present in conflict events + * lastCommonContentType: lastCommonContentType + * } + * ``` + * + * ### `local` + * + * Events with origin `local` are fired conveniently during the page load, so + * that you can fill your views when the page loads. + * + * Example: + * + * ```js + * { + * path: '/public/design/color.txt', + * relativePath: 'color.txt', + * origin: 'local', + * oldValue: undefined, + * newValue: 'white', + * oldContentType: undefined, + * newContentType: 'text/plain' + * } + * ``` + * + * > [!TIP] + * > You may also use for example {@link getAll} instead, and choose to + * > deactivate these. + * + * ### `remote` + * + * Events with origin `remote` are fired when remote changes are discovered + * during sync. + * + * > [!NOTE] + * > Automatically receiving remote changes depends on the {@link caching!Caching} settings + * > for your module/paths. + * + * ### `window` + * + * Events with origin `window` are fired whenever you change a value by calling a + * method on the `BaseClient`; these are disabled by default. + * + * > [!TIP] + * > You can enable them by configuring `changeEvents` for your + * > {@link RemoteStorage remoteStorage} instance. + * + * ### `conflict` + * + * Events with origin `conflict` are fired when a conflict occurs while pushing + * out your local changes to the remote store. + * + * Let's say you changed the content of `color.txt` from `white` to `blue`; if + * you have set `config.changeEvents.window` to `true` for your {@link + * RemoteStorage} instance, then you will receive: + * + * ```js + * { + * path: '/public/design/color.txt', + * relativePath: 'color.txt', + * origin: 'window', + * oldValue: 'white', + * newValue: 'blue', + * oldContentType: 'text/plain', + * newContentType: 'text/plain' + * } + * ``` + * + * But when this change is pushed out by asynchronous synchronization, this change + * may be rejected by the server, if the remote version has in the meantime changed + * from `white` to for instance `red`; this will then lead to a change event with + * origin `conflict` (usually a few seconds after the event with origin `window`, + * if you have those activated). Note that since you already changed it from + * `white` to `blue` in the local version a few seconds ago, `oldValue` is now + * your local value of `blue`: + * + * ```js + * { + * path: '/public/design/color.txt', + * relativePath: 'color.txt', + * origin: 'conflict', + * oldValue: 'blue', + * newValue: 'red', + * oldContentType: 'text/plain', + * newContentType: 'text/plain', + * // Most recent known common ancestor body of local and remote + * lastCommonValue: 'white', + * // Most recent known common ancestor contentType of local and remote + * lastCommonContentType: 'text/plain' + * } + * ``` + * + * #### Conflict Resolution + * + * Conflicts are resolved by calling {@link storeObject} or {@link storeFile} on + * the device where the conflict surfaced. Other devices are not aware of the + * conflict. + * + * If there is an algorithm to merge the differences between local and remote + * versions of the data, conflicts may be automatically resolved. + * {@link storeObject} or {@link storeFile} must not be called synchronously from + * the change event handler, nor by chaining Promises. {@link storeObject} or + * {@link storeFile} must not be called until the next iteration of the JavaScript + * Task Queue, using for example + * [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). + * + * If no algorithm exists, conflict resolution typically involves displaying local + * and remote versions to the user, and having the user merge them, or choose + * which version to keep. */ -class BaseClient { +export class BaseClient { /** - * The instance this operates on. + * The {@link RemoteStorage} instance this {@link BaseClient} operates on. + * + * @internal */ storage: RemoteStorage; /** - * Base path, which this operates on. + * Base path, which this {@link BaseClient} operates on. * - * For the module's privateClient this would be //, for the - * corresponding publicClient /public//. + * For the module's `privateClient` this would be the module name, and for the + * corresponding `publicClient` it is `/public//`. */ base: string; @@ -60,7 +267,7 @@ class BaseClient { * * @param path - The path to scope the new client to * - * @returns A new client operating on a subpath of the current base path + * @returns A new `BaseClient` operating on a subpath of the current base path */ scope (path: string): BaseClient { return new BaseClient(this.storage, this.makePath(path)); @@ -69,11 +276,51 @@ class BaseClient { /** * Get a list of child nodes below a given path. * - * @param {string} path - The path to query. It MUST end with a forward slash. - * @param {number} maxAge - (optional) Either ``false`` or the maximum age of - * cached listing in milliseconds. See :ref:`max-age`. - * - * @returns {Promise} A promise for an object representing child nodes + * @param path - The path to query. It must end with a forward slash. + * @param maxAge - (optional) Either `false` or the maximum age of cached + * listing in milliseconds. See [caching logic for read + * operations](#caching-logic-for-read-operations). + * + * @returns A promise for a folder listing object + * + * @example + * ```js + * client.getListing().then(listing => console.log(listing)); + * ``` + * + * The folder listing is returned as a JSON object, with the root keys + * representing the pathnames of child nodes. Keys ending in a forward slash + * represent _folder nodes_ (subdirectories), while all other keys represent + * _data nodes_ (files/objects). + * + * Data node information contains the item's ETag, content type and -length. + * + * Example of a listing object: + * + * ```js + * { + * "@context": "http://remotestorage.io/spec/folder-description", + * "items": { + * "thumbnails/": true, + * "screenshot-20170902-1913.png": { + * "ETag": "6749fcb9eef3f9e46bb537ed020aeece", + * "Content-Length": 53698, + * "Content-Type": "image/png;charset=binary" + * }, + * "screenshot-20170823-0142.png": { + * "ETag": "92ab84792ef3f9e46bb537edac9bc3a1", + * "Content-Length": 412401, + * "Content-Type": "image/png;charset=binary" + * } + * } + * } + * ``` + * + * > [!WARNING] + * > At the moment, this function only returns detailed metadata, when + * > caching is turned off. With caching turned on, it will only contain the + * > item names as properties with `true` as value. See issues 721 and 1108 — + * > contributions welcome! */ // TODO add real return type async getListing (path?: string, maxAge?: false | number): Promise { @@ -90,14 +337,62 @@ class BaseClient { /** * Get all objects directly below a given path. * - * @param {string} path - Path to the folder. Must end in a forward slash. - * @param {number} maxAge - (optional) Either ``false`` or the maximum age of - * cached objects in milliseconds. See :ref:`max-age`. + * @param path - (optional) Path to the folder. Must end in a forward slash. + * @param maxAge - (optional) Either `false` or the maximum age of cached + * objects in milliseconds. See [caching logic for read + * operations](#caching-logic-for-read-operations). + + * + * @returns A promise for a collection of items + * + * @example + * ```js + * client.getAll('example-subdirectory/').then(objects => { + * for (var path in objects) { + * console.log(path, objects[path]); + * } + * }); + * ``` + * + * Example response: + * + * ```js + * { + * "27b8dc16483734625fff9de653a14e03": { + * "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", + * "id": "27b8dc16483734625fff9de653a14e03", + * "url": "https://unhosted.org/", + * "title": "Unhosted Web Apps", + * "description": "Freedom from web 2.0's monopoly platforms", + * "tags": [ + * "unhosted", + * "remotestorage" + * ], + * "createdAt": "2017-11-02T15:22:25.289Z", + * "updatedAt": "2019-11-07T17:52:22.643Z" + * }, + * "900a5ca174bf57c56b79af0653053bdc": { + * "@context": "http://remotestorage.io/spec/modules/bookmarks/archive-bookmark", + * "id": "900a5ca174bf57c56b79af0653053bdc", + * "url": "https://remotestorage.io/", + * "title": "remoteStorage", + * "description": "An open protocol for per-user storage on the Web", + * "tags": [ + * "unhosted", + * "remotestorage" + * ], + * "createdAt": "2019-11-07T17:59:34.883Z" + * } + * } + * ``` + * > [!NOTE] + * > For items that are not JSON-stringified objects (for example stored using + * > {@link storeFile} instead of {@link storeObject}), the object's value is + * > filled in with `true`. * - * @returns {Promise} A promise for an object */ // TODO add real return type - async getAll (path: string, maxAge?: false | number): Promise { + async getAll (path?: string, maxAge?: false | number): Promise { if (typeof path !== 'string') { path = ''; } else if (path.length > 0 && !isFolder(path)) { return Promise.reject("Not a folder: " + path); @@ -131,14 +426,31 @@ class BaseClient { /** * Get the file at the given path. A file is raw data, as opposed to - * a JSON object (use :func:`getObject` for that). + * a JSON object (use {@link getObject} for that). + * + * + * @param path - Relative path from the module root (without leading slash). + * @param maxAge - (optional) Either ``false`` or the maximum age of + * the cached file in milliseconds. See [caching logic for read + * operations](#caching-logic-for-read-operations). * - * @param {string} path - Relative path from the module root (without leading - * slash). - * @param {number} maxAge - (optional) Either ``false`` or the maximum age of - * the cached file in milliseconds. See :ref:`max-age`. + * @returns An object containing the content type as well as the file's content: * - * @returns {Promise} A promise for an object + * * `mimeType`
+ * String representing the MIME Type of the document. + * * `data`
+ * Raw data of the document (either a string or an ArrayBuffer) + * + * @example + * Displaying an image: + * + * ```js + * client.getFile('path/to/some/image').then(file => { + * const blob = new Blob([file.data], { type: file.mimeType }); + * const targetElement = document.findElementById('my-image-element'); + * targetElement.src = window.URL.createObjectURL(blob); + * }); + * ``` */ // TODO add real return type async getFile (path: string, maxAge?: false | number): Promise { @@ -158,11 +470,34 @@ class BaseClient { /** * Store raw data at a given path. * - * @param {string} mimeType - MIME media type of the data being stored - * @param {string} path - Path relative to the module root - * @param {string|ArrayBuffer|ArrayBufferView} body - Raw data to store + * @param mimeType - MIME media type of the data being stored + * @param path - Path relative to the module root + * @param body - Raw data to store + * + * @returns A promise for the created/updated revision (ETag) + * + * @example + * UTF-8 data: * - * @returns {Promise} A promise for the created/updated revision (ETag) + * ```js + * client.storeFile('text/html', 'index.html', '

Hello World!

') + * .then(() => { console.log("File saved") }); + * ``` + * + * Binary data: + * + * ```js + * const input = document.querySelector('form#upload input[type=file]'); + * const file = input.files[0]; + * const fileReader = new FileReader(); + * + * fileReader.onload = function () { + * client.storeFile(file.type, file.name, fileReader.result) + * .then(() => { console.log("File saved") }); + * }; + * + * fileReader.readAsArrayBuffer(file); + * ``` */ async storeFile (mimeType: string, path: string, body: string | ArrayBuffer | ArrayBufferView): Promise { if (typeof mimeType !== 'string') { @@ -190,13 +525,15 @@ class BaseClient { /** * Get a JSON object from the given path. * - * @param {string} path - Relative path from the module root (without leading - * slash). - * @param {number} maxAge - (optional) Either ``false`` or the maximum age of - * cached object in milliseconds. See :ref:`max-age`. + * @param path - Relative path from the module root (without leading slash). + * @param maxAge - (optional) Either `false` or the maximum age of + * cached object in milliseconds. See [caching logic for read + * operations](#caching-logic-for-read-operations). * - * @returns {Promise} A promise, which resolves with the requested object (or ``null`` - * if non-existent) + * @returns A promise, resolving with the requested object, or `null` if non-existent + * + * @example + * client.getObject('/path/to/object').then(obj => console.log(obj)); */ // TODO add real return type @@ -221,23 +558,34 @@ class BaseClient { } /** - * Store object at given path. Triggers synchronization. - * - * See ``declareType()`` and :doc:`data types ` - * for an explanation of types - * - * For any given `path`, must not be called more frequently than once per second. - * - * @param {string} typeAlias - Unique type of this object within this module. - * @param {string} path - Path relative to the module root. - * @param {object} object - A JavaScript object to be stored at the given - * path. Must be serializable as JSON. - * - * @returns {Promise} Resolves with revision on success. Rejects with - * a ValidationError, if validations fail. + * Store an object at given path. Triggers synchronization. See {@link + * declareType} and + * [Defining data types](../../../data-modules/defining-data-types) + * for info on object types. + * + * Must not be called more than once per second for any given `path`. + * + * @param typeAlias - Unique type of this object within this module. + * @param path - Path relative to the module root. + * @param object - A JavaScript object to be stored at the given path. + * Must be serializable as JSON. + * + * @returns Resolves with revision on success. Rejects with an error object, + * if schema validations fail. + * + * @example + * const bookmark = { + * url: 'http://unhosted.org', + * description: 'Unhosted Adventures', + * tags: ['unhosted', 'remotestorage', 'no-backend'] + * } + * const path = MD5Hash(bookmark.url); + + * client.storeObject('bookmark', path, bookmark) + * .then(() => console.log('bookmark saved')) + * .catch((err) => console.log(err)); */ - // TODO add real return type - async storeObject (typeAlias: string, path: string, object: object): Promise { + async storeObject (typeAlias: string, path: string, object: object): Promise { if (typeof typeAlias !== 'string') { return Promise.reject('Argument \'typeAlias\' of baseClient.storeObject must be a string'); } @@ -271,10 +619,13 @@ class BaseClient { /** * Remove node at given path from storage. Triggers synchronization. * - * @param {string} path - Path relative to the module root. - * @returns {Promise} + * @param path - Path relative to the module root. + * + * @example + * client.remove('path/to/object').then(() => console.log('item deleted')); */ // TODO add real return type + // TODO Don't return the RemoteResponse directly, handle response properly remove (path: string): Promise { if (typeof path !== 'string') { return Promise.reject('Argument \'path\' of baseClient.remove must be a string'); @@ -289,12 +640,19 @@ class BaseClient { /** * Retrieve full URL of a document. Useful for example for sharing the public * URL of an item in the ``/public`` folder. - * TODO: refactor this into the Remote interface * - * @param {string} path - Path relative to the module root. - * @returns {string} The full URL of the item, including the storage origin + * @param path - Path relative to the module root. + * + * @returns The full URL of the item, including the storage origin, or `undefined` + * if no remote storage is connected + * + * > [!WARNING] + * > This method currently only works for remoteStorage + * > backends. The GitHub issues for implementing it for Dropbox and Google + * > are 1052 and 1054. */ - getItemURL (path: string): string { + // TODO refactor this into the Remote interface + getItemURL (path: string): string | undefined { if (typeof path !== 'string') { throw 'Argument \'path\' of baseClient.getItemURL must be a string'; } @@ -309,14 +667,18 @@ class BaseClient { /** * Set caching strategy for a given path and its children. * - * See :ref:`caching-strategies` for a detailed description of the available - * strategies. + * See [Caching strategies](../../caching/classes/Caching.html#caching-strategies) + * for a detailed description of the available strategies. + * + * @param path - Path to cache + * @param strategy - Caching strategy. One of 'ALL', 'SEEN', or FLUSH'. + * Defaults to 'ALL'. * - * @param {string} path - Path to cache - * @param {string} strategy - Caching strategy. One of 'ALL', 'SEEN', or - * 'FLUSH'. Defaults to 'ALL'. + * @returns The same `BaseClient` instance this method is called on to allow + * for method chaining * - * @returns {BaseClient} The same instance this is called on to allow for method chaining + * @example + * client.cache('lists/', 'SEEN'); */ cache (path: string, strategy: 'ALL' | 'SEEN' | 'FLUSH' = 'ALL'): BaseClient { if (typeof path !== 'string') { @@ -337,23 +699,36 @@ class BaseClient { } /** - * TODO: document - * - * @param {string} path - */ - // TODO add return type once known - flush (path: string): unknown { - return this.storage.local.flush(path); - } - - /** - * Declare a remoteStorage object type using a JSON schema. - * - * See :doc:`Defining data types ` for more info. - * - * @param {string} alias - A type alias/shortname - * @param {uri} uri - (optional) JSON-LD URI of the schema. Automatically generated if none given - * @param {object} schema - A JSON Schema object describing the object type + * Declare a remoteStorage object type using a JSON Schema. Visit + * [json-schema.org](http://json-schema.org) for details. + * + * See [Defining data types](../../../data-modules/defining-data-types) for more info. + * + * @param alias - A type alias/shortname + * @param uriOrSchema - JSON-LD URI of the schema, or a JSON Schema object. + * The URI is automatically generated if none given. + * @param schema - (optional) A JSON Schema object describing the object type + * + * @example + * client.declareType('todo-item', { + * "type": "object", + * "properties": { + * "id": { + * "type": "string" + * }, + * "title": { + * "type": "string" + * }, + * "finished": { + * "type": "boolean" + * "default": false + * }, + * "createdAt": { + * "type": "date" + * } + * }, + * "required": ["id", "title"] + * }) **/ declareType (alias: string, uriOrSchema: string|tv4.JsonSchema, schema?: tv4.JsonSchema): void { let uri: string; @@ -373,9 +748,19 @@ class BaseClient { /** * Validate an object against the associated schema. * - * @param {Object} object - JS object to validate. Must have a ``@context`` property. + * @param object - JS object to validate. Must have a `@context` property. + * + * @returns An object containing information about the validation result + * + * @example + * var result = client.validate(document); * - * @returns {Object} An object containing information about validation errors + * // result: + * // { + * // error: null, + * // missing: [], + * // valid: true + * // } **/ validate (object: {[key: string]: any}): {[key: string]: any} { const schema = BaseClient.Types.getSchema(object['@context']); @@ -409,7 +794,7 @@ class BaseClient { } /** - * Attaches the JSON-LD @content to an object + * Attaches the JSON-LD @context to an object * * @private */ @@ -456,7 +841,7 @@ class BaseClient { } } -interface BaseClient extends EventHandling {} +export interface BaseClient extends EventHandling {} applyMixins(BaseClient, [EventHandling]); -export = BaseClient; +export default BaseClient; diff --git a/src/caching.ts b/src/caching.ts index 0bb17f255..119b94757 100644 --- a/src/caching.ts +++ b/src/caching.ts @@ -2,11 +2,45 @@ import { containingFolder, isFolder } from './util'; import log from './log'; /** - * @class Caching + * @class * - * Holds/manages caching configuration. + * The caching class gets initialized as `remoteStorage.caching`, unless the + * {@link remotestorage!RemoteStorage RemoteStorage} instance is created with the option `cache: false`, disabling + * caching entirely. + * + * In case your app hasn't explictly configured caching, the default setting is to + * cache any documents that have been either created or requested since your app + * loaded. For offline-capable apps, it usually makes sense to enable full, + * automatic caching of all documents, which is what {@link enable} will do. + * + * Enabling full caching has several benefits: + * + * * Speed of access: locally cached data is available to the app a lot faster. + * * Offline mode: when all data is cached, it can also be read when your app + * starts while being offline. + * * Initial synchronization time: the amount of data your app caches can + * have a significant impact on its startup time. + * + * Caching can be configured on a per-path basis. When caching is enabled for a + * folder, it causes all subdirectories to be cached as well. + * + * ## Caching strategies + * + * For each subtree, you can set the caching strategy to ``ALL``, ``SEEN`` + * (default), and ``FLUSH``. + * + * * `ALL` means that once all outgoing changes have been pushed, sync will + * start retrieving nodes to cache pro-actively. If a local copy exists + * of everything, it will check on each sync whether the ETag of the root + * folder changed, and retrieve remote changes if they exist. + * * `SEEN` does this only for documents and folders that have been either + * read from or written to at least once since connecting to the current + * remote backend, plus their parent/ancestor folders up to the root (to + * make tree-based sync possible). + * * `FLUSH` will only cache outgoing changes, and forget them as soon as + * they have been saved to remote successfully. **/ -class Caching { +export class Caching { pendingActivations: string[] = []; // TODO add correct type activateHandler: (firstPending: string) => void; @@ -22,8 +56,13 @@ class Caching { * * Not needed when using ``enable``/``disable``. * - * @param {string} path - Path to cache - * @param {string} strategy - Caching strategy. One of 'ALL', 'SEEN', or 'FLUSH'. + * @param path - Path to cache + * @param strategy - Caching strategy. One of 'ALL', 'SEEN', or 'FLUSH'. + * + * @example + * ```js + * remoteStorage.caching.set('/bookmarks/archive/', 'SEEN'); + * ``` */ set (path: string, strategy: 'ALL' | 'SEEN' | 'FLUSH'): void { if (typeof path !== 'string') { @@ -57,7 +96,13 @@ class Caching { * * Uses caching strategy ``ALL``. * - * @param {string} path - Path to enable caching for + * @param path - Path to enable caching for + * @returns + * + * @example + * ```js + * remoteStorage.caching.enable('/bookmarks/'); + * ``` */ enable (path: string): void { this.set(path, 'ALL'); @@ -69,7 +114,12 @@ class Caching { * Uses caching strategy ``FLUSH`` (meaning items are only cached until * successfully pushed to the remote). * - * @param {string} path - Path to disable caching for + * @param path - Path to disable caching for + * + * @example + * ```js + * remoteStorage.caching.disable('/bookmarks/'); + * ``` */ disable (path: string): void { this.set(path, 'FLUSH'); @@ -78,7 +128,7 @@ class Caching { /** * Set a callback for when caching is activated for a path. * - * @param {function} cb - Callback function + * @param cb - Callback function */ onActivate (cb: (firstPending: string) => void): void { log('[Caching] Setting activate handler', cb, this.pendingActivations); @@ -93,8 +143,16 @@ class Caching { * Retrieve caching setting for a given path, or its next parent * with a caching strategy set. * - * @param {string} path - Path to retrieve setting for - * @returns {string} caching strategy for the path + * @param path - Path to retrieve setting for + * @returns caching strategy for the path + * + * @example + * ```js + * remoteStorage.caching.checkPath('documents/').then(strategy => { + * console.log(`caching strategy for 'documents/': ${strategy}`)); + * // "caching strategy for 'documents/': SEEN" + * }); + * ``` **/ checkPath (path: string): string { if (this._rootPaths[path] !== undefined) { @@ -108,6 +166,11 @@ class Caching { /** * Reset the state of caching by deleting all caching information. + * + * @example + * ```js + * remoteStorage.caching.reset(); + * ``` **/ reset (): void { this._rootPaths = {}; @@ -116,11 +179,11 @@ class Caching { /** * Setup function that is called on initialization. * - * @private + * @internal **/ static _rs_init (/*remoteStorage*/): void { return; } } -export = Caching; +export default Caching; diff --git a/src/eventhandling.ts b/src/eventhandling.ts index 35fbe934e..d168011fa 100644 --- a/src/eventhandling.ts +++ b/src/eventhandling.ts @@ -1,13 +1,21 @@ import log from './log'; -import { EventHandler } from './interfaces/event_handling'; -class EventHandling { +/** + */ +export type EventHandler = (event?: unknown) => void; + +export class EventHandling { + /** + * @internal + */ _handlers: { [key: string]: EventHandler[] }; /** * Register event names * * TODO see if necessary, or can be done on the fly in addEventListener + * + * @internal */ addEvents(additionalEvents: string[]): void { additionalEvents.forEach(evName => this._addEvent(evName)); @@ -15,6 +23,8 @@ class EventHandling { /** * Install an event handler for the given event name + * + * Usually called via [`on()`](#on) */ addEventListener (eventName: string, handler: EventHandler): void { // Check type for public consumption of API @@ -29,8 +39,18 @@ class EventHandling { this._handlers[eventName].push(handler); } - /* - * Alias for addEventListener + /** + * Register an event handler for the given event name + * + * Alias for {@link addEventListener} + * + * @param eventName - Name of the event + * @param handler - Function to handle the event + * + * @example + * remoteStorage.on('connected', function() { + * console.log('storage account has been connected'); + * }); */ on (eventName: string, handler: EventHandler): void { return this.addEventListener(eventName, handler); @@ -50,6 +70,9 @@ class EventHandling { } } + /** + * @internal + */ _emit (eventName: string, ...args: unknown[]): void { this._validateEvent(eventName); this._handlers[eventName].slice().forEach((handler) => { @@ -57,18 +80,27 @@ class EventHandling { }); } + /** + * @internal + */ _validateEvent (eventName: string): void { if (!(eventName in this._handlers)) { throw new Error("Unknown event: " + eventName); } } + /** + * @internal + */ _delegateEvent (eventName: string, target): void { target.on(eventName, (event) => { this._emit(eventName, event); }); } + /** + * @internal + */ _addEvent (eventName: string): void { if (typeof this._handlers === 'undefined') { this._handlers = {}; @@ -77,4 +109,4 @@ class EventHandling { } } -export = EventHandling; +export default EventHandling; diff --git a/src/interfaces/authorize_options.ts b/src/interfaces/authorize_options.ts deleted file mode 100644 index 7cf3e4f53..000000000 --- a/src/interfaces/authorize_options.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export interface AuthorizeOptions { - authURL: string; - scope?: string; - redirectUri?: string; - clientId?: string; - response_type?: 'token' | 'code'; - state?: string; - code_challenge?: string; - code_challenge_method?: 'S256' | 'plain'; - token_access_type?: 'online' | 'offline'; // Dropbox only -} diff --git a/src/remote.ts b/src/remote.ts index 4f07dc856..303a420b9 100644 --- a/src/remote.ts +++ b/src/remote.ts @@ -1,6 +1,6 @@ import RemoteStorage from "./remotestorage"; import EventHandling from "./eventhandling"; -import {isFolder} from "./util"; +import { isFolder } from "./util"; /** * The ancestor for WireClient, GoogleDrive & Dropbox @@ -76,44 +76,74 @@ export interface RemoteResponse { * The public interface for WireClient, GoogleDrive & Dropbox */ export interface Remote { + /** + * Whether or not a remote store is connected + */ connected: boolean; + + /** + * Whether last sync action was successful or not + */ online: boolean; + + /** + * The user address of the connected user + */ userAddress: string; /** * Holds the spec version the server claims to be compatible with * - * Example: - * (start code) + * @example + * ```js + * remoteStorage.remote.storageApi + * // 'draft-dejong-remotestorage-12' + * ``` * - * remoteStorage.remote.storageApi - * // -> 'draft-dejong-remotestorage-01' + * @internal */ storageApi: string; /** * Holds the server's base URL, as obtained in the Webfinger discovery * - * Example: - * (start code) + * @example + * ```js + * remoteStorage.remote.href + * // 'https://storage.example.com/users/jblogg/' + * ``` * - * remoteStorage.remote.href - * // -> 'https://storage.example.com/users/jblogg/' + * @internal */ href?: string; - /** the JSON-parsed properties object from the user's WebFinger record */ + /** + * The JSON-parsed properties object from the user's WebFinger record + */ properties?: object; - clientId?: string; // OAuth2 - TOKEN_URL?: string; // OAuth2 PKCE + /** + * OAuth2 client ID + * + * @internal + */ + clientId?: string; - configure (settings): void // TODO: harmonize settings & use type + /** + * OAuth2 PKCE + * + * @internal + */ + TOKEN_URL?: string; + + configure (settings): void // TODO: harmonize settings & use type configure (settings: RemoteSettings): void; /** * Initiate the authorization flow's OAuth dance. + * + * @internal */ connect? (): void; diff --git a/src/remotestorage.ts b/src/remotestorage.ts index 6b57cff62..dd17e3b5a 100644 --- a/src/remotestorage.ts +++ b/src/remotestorage.ts @@ -1,6 +1,5 @@ 'use strict'; -import type { StorageInfo } from './interfaces/storage_info'; import config from './config'; import log from './log'; import { @@ -18,23 +17,21 @@ import Caching from './caching'; import IndexedDB from './indexeddb'; import InMemoryStorage from './inmemorystorage'; import LocalStorage from './localstorage'; -import EventHandling from './eventhandling'; +import { EventHandling, EventHandler } from './eventhandling'; import GoogleDrive from './googledrive'; import Dropbox from './dropbox'; import Discover from './discover'; import SyncError from './sync-error'; import UnauthorizedError from './unauthorized-error'; import Features from './features'; -import {Remote} from "./remote"; +import { Remote } from "./remote"; + +import type { AuthorizeOptions } from './authorize'; +import type { StorageInfo } from './interfaces/storage_info'; +import type { Sync } from './sync'; // TODO this is assigned to RemoteStorage.util later; check if still needed import * as util from './util'; -import {AuthorizeOptions} from "./interfaces/authorize_options"; - -interface RSModule { - name: string; - builder; // TODO detailed type -} const globalContext = getGlobalContext(); // declare global { @@ -61,81 +58,309 @@ function isValidInterval(interval: unknown): interval is number { } /** - * Constructor for the remoteStorage object/instance + * Represents a data module + * + * @example + * ```js + * { + * name: 'examples', + * builder: function(privateClient, publicClient) { + * return { + * exports: { + * addItem(item): function() { + * // Generate a random ID/path + * const path = [...Array(10)].map(() => String.fromCharCode(Math.floor(Math.random() * 95) + 32)).join(''); + * // Store the object, and ensure it conforms to the JSON Schema + * // type `example-item` + * privateClient.storeObject('example-item', path, item); + * } + * } + * } + * } + * } + * ``` + */ +export interface RSModule { + /** + * The module's name, which is also the category (i.e. base folder) for document URLs on the remote storage + */ + name: string; + /** + * A module builder function, which defines the actual module + */ + builder: (privateClient: BaseClient, publicClient: BaseClient) => { + exports: { + [key: string]: any; + }; + }; +} + +enum ApiKeyType { + GOOGLE = 'googledrive', + DROPBOX = 'dropbox' +} + +/** + * Create a `remoteStorage` class instance so: + * + * ```js + * const remoteStorage = new RemoteStorage(); + * ``` + * + * The constructor can optionally be called with a configuration object. This + * example shows all default values: + * + * ```js + * const remoteStorage = new RemoteStorage({ + * cache: true, + * changeEvents: { + * local: true, + * window: false, + * remote: true, + * conflict: true + * }, + * cordovaRedirectUri: undefined, + * logging: false, + * modules: [] + * }); + * ``` + * + * > [!NOTE] + * > In the current version, it is only possible to use a single `remoteStorage` + * > instance. You cannot connect to two different remotes in parallel yet. + * > We intend to support this eventually. + * + * > [!TIP] + * > For the change events configuration, you have to set all events + * > explicitly. Otherwise it disables the unspecified ones. + * + * ## Events + * + * You can add event handlers to your `remoteStorage` instance by using the + * {@link on} function. For example: + * + * ```js + * remoteStorage.on('connected', function() { + * // Storage account has been connected, let’s roll! + * }); + * ``` + * + * ### `ready` + * + * Emitted when all features are loaded and the RS instance is ready to be used + * in your app + * + * ### `not-connected` + * + * Emitted when ready, but no storage connected ("anonymous mode") + * + * ### `connected` + * + * Emitted when a remote storage has been connected + * + * ### `disconnected` + * + * Emitted after disconnect + * + * ### `error` + * + * Emitted when an error occurs; receives an error object as argument + * + * There are a handful of known errors, which are identified by the `name` + * property of the error object: + * + * * `Unauthorized` + * + * Emitted when a network request resulted in a 401 or 403 response. You can + * use this event to handle invalid OAuth tokens in custom UI (i.e. when a + * stored token has been revoked or expired by the RS server). + * + * * `DiscoveryError` + * + * A variety of storage discovery errors, e.g. from user address input + * validation, or user address lookup issues + * + * #### Example + * + * ```js + * remoteStorage.on('error', err => console.log(err)); + * + * // { + * // name: "Unauthorized", + * // message: "App authorization expired or revoked.", + * // stack: "Error↵ at new a.Unauthorized (vendor.js:65710:41870)" + * // } + * ``` + * + * ### `connecting` * - * This class primarily contains feature detection code and convenience API. + * Emitted before webfinger lookup * - * Depending on which features are built in, it contains different attributes - * and functions. See the individual features for more information. + * ### `authing` * - * @param {object} config - an optional configuration object - * @class + * Emitted before redirecting to the OAuth server + * + * ### `wire-busy` + * + * Emitted when a network request starts + * + * ### `wire-done` + * + * Emitted when a network request completes + * + * ### `sync-req-done` + * + * Emitted when a single sync request has finished. Callback functions + * receive an object as argument, informing the client of remaining items + * in the current sync task queue. + * + * #### Example + * + * ```js + * remoteStorage.on('sync-req-done', result => console.log(result)); + * // { tasksRemaining: 21 } + * ``` + * + * > [!NOTE] + * > The internal task queue holds at most 100 items at the same time, + * > regardless of the overall amount of items to sync. Therefore, this number + * > is only an indicator of sync status, not a precise amount of items left + * > to sync. It can be useful to determine if your app should display any + * > kind of sync status/progress information for the cycle or not. + * + * ### `sync-done` + * + * Emitted when a sync cycle has been completed and a new sync is scheduled. + * + * The callback function receives an object as argument, informing the client + * if the sync process has completed successfully or not. + * + * #### Example + * + * ```js + * remoteStorage.on('sync-done', result => console.log(result)); + * // { completed: true } + * ``` + * + * If `completed` is `false`, it means that some of the sync requests have + * failed and will be retried in the next sync cycle (usually a few seconds + * later in this case). This is not an unusual scenario on mobile networks or + * when doing a large initial sync for example. + * + * For an app's user interface, you may want to consider the sync process as + * ongoing in this case, and wait until your app sees a positive `completed` + * status before updating the UI. + * + * ### `network-offline` + * + * Emitted once when a wire request fails for the first time, and + * `remote.online` is set to false + * + * ### `network-online` + * + * Emitted once when a wire request succeeds for the first time after a failed + * one, and `remote.online` is set back to true + * + * ### `sync-interval-change` + * + * Emitted when the sync interval changes */ -class RemoteStorage { +export class RemoteStorage { /** * Pending get/put/delete calls - * @private + * @internal */ _pending: {[key: string]: any}[] = []; /** * TODO: document + * @internal */ _cleanups: [] = []; /** * TODO: document + * @internal */ _pathHandlers: { [key: string]: any } = { change: {} }; /** * Holds OAuth app keys for Dropbox, Google Drive + * @internal */ apiKeys: {googledrive?: {clientId: string}; dropbox?: {appKey: string}} = {}; /** - * Holds the feature class instance, added by feature initialization - * TODO use type Access */ - access: any; + access: Access; /** - * Holds the feature class instance, added by feature initialization - * TODO use type Sync */ - sync: any; + sync: Sync; /** - * Holds the feature class instance, added by feature initialization */ caching: Caching; - // TODO use correct types, document + /** + * @internal + */ _syncTimer: any; - syncStopped: any; - get: any; - put: any; - delete: any; + /** + * @internal + */ + syncStopped: boolean; + /** + * @internal + */ + get: Function; + /** + * @internal + */ + put: Function; + /** + * @internal + */ + delete: Function; + /** + */ backend: 'remotestorage' | 'dropbox' | 'googledrive'; /** - * Holds a WireClient, GoogleDrive or Dropbox instance, added by feature initialization + * Depending on the chosen backend, this is either an instance of `WireClient`, + * `Dropbox` or `GoogleDrive`. + * + * See {@link Remote} for public API + * + * @example + * remoteStorage.remote.connected + * // false */ remote: Remote; - /* + /** * Access to the local caching backend used. Usually either a - * or instance. + * `RemoteStorage.IndexedDB` or `RemoteStorage.LocalStorage` instance. * * Not available, when caching is turned off. + * + * @internal */ local: IndexedDB | LocalStorage | InMemoryStorage; + /** + * @internal + */ dropbox: Dropbox; + /** + * @internal + */ googledrive: GoogleDrive; - fireInitial; + /** + * @internal + */ + fireInitial: Function; - on: any; constructor (cfg?: object) { // Initial configuration property settings. @@ -165,13 +390,7 @@ class RemoteStorage { // Keep a reference to the orginal `on` function const origOn = this.on; - /** - * Register an event handler. See :ref:`rs-events` for available event names. - * - * @param {string} eventName - Name of the event - * @param {function} handler - Event handler - */ - this.on = function (eventName: string, handler): void { + this.on = function (eventName: string, handler: Function): void { if (this._allLoaded) { // check if the handler should be called immediately, because the // event has happened already @@ -223,8 +442,12 @@ class RemoteStorage { return this.remote.connected; } - // FIXME: Instead of doing this, would be better to only - // export setAuthURL / getAuthURL from RemoteStorage prototype + /** + * FIXME: Instead of doing this, would be better to only + * export setAuthURL / getAuthURL from RemoteStorage prototype + * + * @ignore + */ static Authorize = Authorize; static SyncError = SyncError; @@ -234,7 +457,8 @@ class RemoteStorage { /** * Load all modules passed as arguments - * @private + * + * @internal */ loadModules(): void { config.modules.forEach(this.addModule.bind(this)); @@ -243,15 +467,7 @@ class RemoteStorage { /** * Initiate the OAuth authorization flow. * - * This function is called by custom storage backend implementations - * (e.g. Dropbox or Google Drive). - * - * @param {object} options - * @param {string} options.authURL - URL of the authorization endpoint - * @param {string} [options.scope] - access scope - * @param {string} [options.clientId] - client identifier (defaults to the - * origin of the redirectUri) - * @private + * @internal */ authorize (options: AuthorizeOptions): void { this.access.setStorageType(this.remote.storageApi); @@ -280,7 +496,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ impliedauth (storageApi?: string, redirectUri?: string): void { // TODO shouldn't these be default argument values? @@ -295,18 +511,6 @@ class RemoteStorage { document.location.href = redirectUri; } - /** - * @property {object} remote - * - * Depending on the chosen backend, this is either an instance of ``WireClient``, - * ``Dropbox`` or ``GoogleDrive``. - * - * @property {boolean} remote.connected - Whether or not a remote store is connected - * @property {boolean} remote.online - Whether last sync action was successful or not - * @property {string} remote.userAddress - The user address of the connected user - * @property {string} remote.properties - The properties of the WebFinger link - */ - /** * Connect to a remoteStorage server. * @@ -314,7 +518,8 @@ class RemoteStorage { * the OAuth dance. * * This method must be called *after* all required access has been claimed. - * When using the connect widget, it will call this method itself. + * When using the connect widget, it will call this method when the user + * clicks/taps the "connect" button. * * Special cases: * @@ -329,8 +534,11 @@ class RemoteStorage { * tokens in all requests later on. This is useful for example when using * Kerberos and similar protocols. * - * @param {string} userAddress - The user address (user@host) or URL to connect to. - * @param {string} token - (optional) A bearer token acquired beforehand + * @param userAddress - The user address (user@host) or URL to connect to. + * @param token - (optional) A bearer token acquired beforehand + * + * @example + * remoteStorage.connect('user@example.com'); */ connect (userAddress: string, token?: string): void { this.setBackend('remotestorage'); @@ -398,6 +606,9 @@ class RemoteStorage { /** * Reconnect the remote server to get a new authorization. + * + * Useful when not using the connect widget and encountering an + * `Unauthorized` event. */ reconnect (): void { this.remote.configure({ token: null }); @@ -459,7 +670,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ setBackend (what): void { this.backend = what; @@ -473,18 +684,24 @@ class RemoteStorage { } /** - * Add a "change" event handler to the given path. Whenever a "change" - * happens (as determined by the backend, such as e.g. - * ) and the affected path is equal to or below the + * Add a `change` event handler for the given path. Whenever a change + * happens (as determined by the local backend, such as e.g. + * `RemoteStorage.IndexedDB`), and the affected path is equal to or below the * given 'path', the given handler is called. * - * You should usually not use this method directly, but instead use the - * "change" events provided by :doc:`BaseClient ` + * > [!TIP] + * > You should usually not use this method, but instead use the + * > `change` events provided by {@link BaseClient}. + * + * @param path - Absolute path to attach handler to + * @param handler - A function to handle the change * - * @param {string} path - Absolute path to attach handler to - * @param {function} handler - Handler function + * @example + * remoteStorage.onChange('/bookmarks/', function() { + * // your code here + * }) */ - onChange (path: string, handler): void { + onChange (path: string, handler: EventHandler): void { if (! this._pathHandlers.change[path]) { this._pathHandlers.change[path] = []; } @@ -492,38 +709,44 @@ class RemoteStorage { } /** - * TODO: do we still need this, now that we always instantiate the prototype? + * Enable remoteStorage debug logging. + * + * Usually done when instantiating remoteStorage: * - * Enable remoteStorage logging. + * ```js + * const remoteStorage = new RemoteStorage({ logging: true }); + * ``` */ enableLog (): void { config.logging = true; } /** - * TODO: do we still need this, now that we always instantiate the prototype? - * - * Disable remoteStorage logging + * Disable remoteStorage debug logging */ disableLog (): void { config.logging = false; } /** - * log + * Log something to the debug log * - * The same as . + * @internal */ log (...args): void { log.apply(RemoteStorage, args); } /** - * Set the OAuth key/ID for either GoogleDrive or Dropbox backend support. + * Set the OAuth key/ID for GoogleDrive and/or Dropbox backend support. * - * @param {Object} apiKeys - A config object with these properties: - * @param {string} [apiKeys.type] - Backend type: 'googledrive' or 'dropbox' - * @param {string} [apiKeys.key] - Client ID for GoogleDrive, or app key for Dropbox + * @param apiKeys - A config object + * + * @example + * remoteStorage.setApiKeys({ + * dropbox: 'your-app-key', + * googledrive: 'your-client-id' + * }); */ setApiKeys (apiKeys: {[key in ApiKeyType]?: string}): void | boolean { const validTypes: string[] = [ApiKeyType.GOOGLE, ApiKeyType.DROPBOX]; @@ -563,9 +786,13 @@ class RemoteStorage { /** * Set redirect URI to be used for the OAuth redirect within the - * in-app-browser window in Cordova apps. + * in-app-browser window in Cordova apps. See + * [Usage in Cordova apps](../../../cordova) for details. * * @param uri - A valid HTTP(S) URI + * + * @example + * remoteStorage.setCordovaRedirectUri('https://app.example.com'); */ setCordovaRedirectUri (uri: string): void { if (typeof uri !== 'string' || !uri.match(/http(s)?:\/\//)) { @@ -578,19 +805,61 @@ class RemoteStorage { // FEATURES INITIALIZATION // + /** + * @internal + */ _init = Features.loadFeatures; + /** + * @internal + */ features = Features.features; + /** + * @internal + */ loadFeature = Features.loadFeature; + /** + * @internal + */ featureSupported = Features.featureSupported; + /** + * @internal + */ featureDone = Features.featureDone; + /** + * @internal + */ featuresDone = Features.featuresDone; + /** + * @internal + */ featuresLoaded = Features.featuresLoaded; + /** + * @internal + */ featureInitialized = Features.featureInitialized; + /** + * @internal + */ featureFailed = Features.featureFailed; + /** + * @internal + */ hasFeature = Features.hasFeature; + /** + * @internal + */ _setCachingModule = Features._setCachingModule; + /** + * @internal + */ _collectCleanupFunctions = Features._collectCleanupFunctions; + /** + * @internal + */ _fireReady = Features._fireReady; + /** + * @internal + */ initFeature = Features.initFeature; // @@ -599,7 +868,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ _setGPD (impl, context?) { function wrap(func) { @@ -615,7 +884,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ _pendingGPD (methodName): () => Promise { return (...args) => { @@ -635,7 +904,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ _processPending (): void { this._pending.forEach((pending) => { @@ -654,7 +923,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ _bindChange (object: { on }): void { object.on('change', this._dispatchEvent.bind(this, 'change')); @@ -662,7 +931,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ _dispatchEvent (eventName: string, event): void { Object.keys(this._pathHandlers[eventName]).forEach((path: string) => { @@ -684,16 +953,20 @@ class RemoteStorage { } /** - * This method enables you to quickly instantiate a BaseClient, which you can + * This method allows you to quickly instantiate a BaseClient, which you can * use to directly read and manipulate data in the connected storage account. * * Please use this method only for debugging and development, and choose or - * create a :doc:`data module ` for your app to use. + * create a [data module](../../../data-modules/) for your app to use. * * @param path - The base directory of the BaseClient that will be returned * (with a leading and a trailing slash) * * @returns A client with the specified scope (category/base directory) + * + * @example + * remoteStorage.scope('/pictures/').getListing(''); + * remoteStorage.scope('/public/pictures/').getListing(''); */ scope (path: string): BaseClient { if (typeof(path) !== 'string') { @@ -708,7 +981,11 @@ class RemoteStorage { /** * Get the value of the sync interval when application is in the foreground * - * @returns {number} A number of milliseconds + * @returns A number of milliseconds + * + * @example + * remoteStorage.getSyncInterval(); + * // 10000 */ getSyncInterval (): number { return config.syncInterval; @@ -718,6 +995,9 @@ class RemoteStorage { * Set the value of the sync interval when application is in the foreground * * @param interval - Sync interval in milliseconds (between 2000 and 3600000 [1 hour]) + * + * @example + remoteStorage.setSyncInterval(20000); */ setSyncInterval (interval: number): void { if (!isValidInterval(interval)) { @@ -725,13 +1005,21 @@ class RemoteStorage { } const oldValue = config.syncInterval; config.syncInterval = interval; - this._emit('sync-interval-change', {oldValue: oldValue, newValue: interval}); + + this._emit('sync-interval-change', { + oldValue: oldValue, + newValue: interval + }); } /** * Get the value of the sync interval when application is in the background * * @returns A number of milliseconds + * + * @example + * remoteStorage.getBackgroundSyncInterval(); + * // 60000 */ getBackgroundSyncInterval (): number { return config.backgroundSyncInterval; @@ -742,6 +1030,9 @@ class RemoteStorage { * background * * @param interval - Sync interval in milliseconds (between 2000 and 3600000 [1 hour]) + * + * @example + * remoteStorage.setBackgroundSyncInterval(90000); */ setBackgroundSyncInterval (interval: number): void { if (!isValidInterval(interval)) { @@ -749,14 +1040,22 @@ class RemoteStorage { } const oldValue = config.backgroundSyncInterval; config.backgroundSyncInterval = interval; - this._emit('sync-interval-change', {oldValue: oldValue, newValue: interval}); + + this._emit('sync-interval-change', { + oldValue: oldValue, + newValue: interval + }); } /** * Get the value of the current sync interval. Can be background or * foreground, custom or default. * - * @returns {number} A number of milliseconds + * @returns number of milliseconds + * + * @example + * remoteStorage.getCurrentSyncInterval(); + * // 15000 */ getCurrentSyncInterval (): number { return config.isBackground ? config.backgroundSyncInterval : config.syncInterval; @@ -765,7 +1064,11 @@ class RemoteStorage { /** * Get the value of the current network request timeout * - * @returns {number} A number of milliseconds + * @returns A number of milliseconds + * + * @example + * remoteStorage.getRequestTimeout(); + * // 30000 */ getRequestTimeout (): number { return config.requestTimeout; @@ -775,6 +1078,9 @@ class RemoteStorage { * Set the timeout for network requests. * * @param timeout - Timeout in milliseconds + * + * @example + * remoteStorage.setRequestTimeout(30000); */ setRequestTimeout (timeout: number): void { if (typeof timeout !== 'number') { @@ -785,7 +1091,7 @@ class RemoteStorage { /** * TODO: document - * @private + * @internal */ syncCycle (): void { if (!this.sync || this.sync.stopped) { return; } @@ -815,7 +1121,7 @@ class RemoteStorage { * sync button for example. This might feel safer to them sometimes, esp. * when shifting between offline and online a lot. * - * @returns {Promise} A Promise which resolves when the sync has finished + * @returns A Promise which resolves when the sync has finished */ startSync (): Promise { if (!config.cache) { @@ -847,35 +1153,39 @@ class RemoteStorage { } } - /* + /** * Add remoteStorage data module * - * @param {Object} module - module object needs following properies: - * @param {string} [module.name] - Name of the module - * @param {function} [module.builder] - Builder function defining the module + * @param module - A data module object * - * The module builder function should return an object containing another - * object called exports, which will be exported to this - * instance under the module's name. So when defining a locations module, - * like in the example below, it would be accessible via - * `remoteStorage.locations`, which would in turn have a `features` and a - * `collections` property. + * @example * - * The function receives a private and a public client, which are both - * instances of . In the following example, the - * scope of privateClient is `/locations` and the scope of publicClient is - * `/public/locations`. + * Usually, you will import your data module from either a package or a local path. + * Let's say you want to use the + * [bookmarks module](https://github.com/raucao/remotestorage-module-bookmarks) + * in order to load data stored from [Webmarks](https://webmarks.5apps.com) for + * example: * - * @example - * RemoteStorage.addModule({name: 'locations', builder: function (privateClient, publicClient) { - * return { - * exports: { - * features: privateClient.scope('features/').defaultType('feature'), - * collections: privateClient.scope('collections/').defaultType('feature-collection') - * } - * }; - * }}); - */ + * ```js + * import Bookmarks from 'remotestorage-module-bookmarks'; + * + * remoteStorage.addModule(Bookmarks); + * ``` + * + * You can also forgo this function entirely and add modules when creating your + * remoteStorage instance: + * + * ```js + * const remoteStorage = new RemoteStorage({ modules: [ Bookmarks ] }); + * ``` + * + * After the module has been added, it can be used like so: + * + * ```js + * remoteStorage.bookmarks.archive.getAll(false) + * .then(bookmarks => console.log(bookmarks)); + * ``` + */ addModule (module: RSModule): void { const moduleName = module.name; const moduleBuilder = module.builder; @@ -906,6 +1216,7 @@ class RemoteStorage { /** * Load module + * * @private */ _loadModule (moduleName: string, moduleBuilder): { [key: string]: unknown } { @@ -960,12 +1271,7 @@ Object.defineProperty(RemoteStorage.prototype, 'caching', { } }); -interface RemoteStorage extends EventHandling {} +export interface RemoteStorage extends EventHandling {} applyMixins(RemoteStorage, [EventHandling]); -enum ApiKeyType { - GOOGLE = 'googledrive', - DROPBOX = 'dropbox' -} - -export = RemoteStorage; +export default RemoteStorage; diff --git a/src/sync.ts b/src/sync.ts index f1f540249..ee1d736c4 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -80,8 +80,6 @@ function handleVisibility (env, rs): void { } /** - * Class: RemoteStorage.Sync - * * This class basically does six things: * * - retrieve the remote version of relevant documents and folders @@ -99,7 +97,7 @@ function handleVisibility (env, rs): void { * comes in, it gives information about all the documents it contains (this is * the `markChildren` function). **/ -class Sync { +export class Sync { // TODO remove when RS is defined, or if unnecessary rs: { [propName: string]: any }; @@ -1109,7 +1107,7 @@ class Sync { } } -interface Sync extends EventHandling {} +export interface Sync extends EventHandling {} applyMixins(Sync, [EventHandling]); -export = Sync; +export default Sync; diff --git a/src/util.ts b/src/util.ts index 3bae5bd17..e4129e70a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -333,14 +333,17 @@ function base64Urlencode(str) { * * https://www.typescriptlang.org/docs/handbook/mixins.html * - * @param {object} Parent object - * @param {Array} Mixins to apply methods from + * @param derivedConstructor Parent object + * @param constructors Mixins to apply methods from */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function applyMixins(derivedCtor: any, baseCtors: any[]): void { - baseCtors.forEach(baseCtor => { +export function applyMixins(derivedCtor: any, constructors: any[]): void { + constructors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { - Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); + Object.defineProperty( + derivedCtor.prototype, + name, + Object.getOwnPropertyDescriptor(baseCtor.prototype, name)); }); }); } diff --git a/test/unit/access-suite.js b/test/unit/access-suite.js index bcacbec40..bd4143dc1 100644 --- a/test/unit/access-suite.js +++ b/test/unit/access-suite.js @@ -9,7 +9,7 @@ define(['require', 'fs'], function( require, fs, undefined) { setup: function(env, test) { global.RemoteStorage = function() {}; RemoteStorage.log = function() {}; - RemoteStorage.Access = require('../../build/access'); + RemoteStorage.Access = require('../../build/access').Access; env.Access = RemoteStorage.Access; diff --git a/test/unit/authorize-suite.js b/test/unit/authorize-suite.js index 6cae72b35..8364aae31 100644 --- a/test/unit/authorize-suite.js +++ b/test/unit/authorize-suite.js @@ -10,7 +10,7 @@ define([ 'require', './build/authorize', './build/unauthorized-error', 'test/hel setup: function(env, test) { global.RemoteStorage = function() {}; RemoteStorage.log = function() {}; - env.Authorize = Authorize; + env.Authorize = Authorize.Authorize; env.UnauthorizedError = UnauthorizedError; test.done(); }, diff --git a/test/unit/baseclient-suite.js b/test/unit/baseclient-suite.js index 102444fac..50fafc307 100644 --- a/test/unit/baseclient-suite.js +++ b/test/unit/baseclient-suite.js @@ -2,7 +2,9 @@ if (typeof define !== 'function') { var define = require('amdefine')(module); } define(['./build/config', './build/baseclient', 'test/helpers/mocks', 'tv4'], - function (config, BaseClient, mocks, tv4) { + function (config, baseClientModule, mocks, tv4) { + + const BaseClient = baseClientModule.BaseClient; var suites = []; diff --git a/test/unit/baseclient/types-suite.js b/test/unit/baseclient/types-suite.js index b7e567943..cca6b51c7 100644 --- a/test/unit/baseclient/types-suite.js +++ b/test/unit/baseclient/types-suite.js @@ -8,7 +8,7 @@ define(['require'], function(require) { name: "BaseClient.Types", desc: "Type and schema handling", setup: function(env, test) { - global.BaseClient = require('../build/baseclient'); + global.BaseClient = require('../build/baseclient').BaseClient; global.RemoteStorage = function() {}; RemoteStorage.log = function() {}; RemoteStorage.prototype.onChange = function() {}; diff --git a/test/unit/caching-suite.js b/test/unit/caching-suite.js index a83a10257..cbd39661f 100644 --- a/test/unit/caching-suite.js +++ b/test/unit/caching-suite.js @@ -9,7 +9,7 @@ define(['require'], function(require) { name: "caching", desc: "Caching stores settings about which paths to cache locally", setup: function(env, test) { - env.Caching = require('./build/caching'); + env.Caching = require('./build/caching').Caching; test.result(true); }, diff --git a/test/unit/dropbox-suite.js b/test/unit/dropbox-suite.js index 5c8099adb..0ca449502 100644 --- a/test/unit/dropbox-suite.js +++ b/test/unit/dropbox-suite.js @@ -14,7 +14,7 @@ define(['require', './build/util', './build/dropbox', './build/wireclient', setBackend (b) { this.backend = b; } static log () {} } - buildUtil.applyMixins(RemoteStorage, [EventHandling]); + buildUtil.applyMixins(RemoteStorage, [EventHandling.default]); global.RemoteStorage = RemoteStorage; global.localStorage = { diff --git a/test/unit/dropbox.test.ts b/test/unit/dropbox.test.ts index 4cd2d3665..e9a86d208 100644 --- a/test/unit/dropbox.test.ts +++ b/test/unit/dropbox.test.ts @@ -5,9 +5,10 @@ import chaiAsPromised from 'chai-as-promised'; import sinon from 'sinon'; import fetchMock from 'fetch-mock'; import config from "../../src/config"; -import RemoteStorage from '../../src/remotestorage'; import Dropbox from "../../src/dropbox"; - +import InMemoryStorage from '../../src/inmemorystorage'; +import RemoteStorage from '../../src/remotestorage'; +import Sync from '../../src/sync'; const SETTINGS_KEY = 'remotestorage:dropbox'; const ACCOUNT_URL = 'https://api.dropboxapi.com/2/users/get_current_account'; @@ -205,7 +206,9 @@ describe('Dropbox backend', () => { const rs2 = new RemoteStorage(); rs2.setApiKeys({dropbox: 'swcj8jbc9i1jf1m'}); // an app would do this rs2.backend = 'dropbox'; - rs2.sync = { sync: sinon.spy() }; + rs2.local = new InMemoryStorage(); + rs2.sync = new Sync(rs2); + rs2.sync.sync = sinon.spy(); Dropbox._rs_init(rs2); const fetchDeltaSpy = sandbox.spy(rs2.dropbox, 'fetchDelta'); diff --git a/test/unit/googledrive-suite.js b/test/unit/googledrive-suite.js index 13b434959..863abdd28 100644 --- a/test/unit/googledrive-suite.js +++ b/test/unit/googledrive-suite.js @@ -18,7 +18,7 @@ define(['util', 'require', './build/eventhandling', './build/googledrive', class RemoteStorage { setBackend (b) { this.backend = b; } } - buildUtil.applyMixins(RemoteStorage, [EventHandling]); + buildUtil.applyMixins(RemoteStorage, [EventHandling.default]); global.RemoteStorage = RemoteStorage; global.Authorize = require('./build/authorize'); diff --git a/test/unit/modules-suite.js b/test/unit/modules-suite.js index f6831d461..00fdcf1b5 100644 --- a/test/unit/modules-suite.js +++ b/test/unit/modules-suite.js @@ -1,7 +1,8 @@ if (typeof(define) !== 'function') { var define = require('amdefine'); } -define(['./build/remotestorage'], function(RemoteStorage) { +define(['./build/remotestorage'], function(rsModule) { + const RemoteStorage = rsModule.RemoteStorage; var suites = []; suites.push({ diff --git a/test/unit/node-wireclient-suite.js b/test/unit/node-wireclient-suite.js index 46035b431..19799a436 100644 --- a/test/unit/node-wireclient-suite.js +++ b/test/unit/node-wireclient-suite.js @@ -5,7 +5,7 @@ define(['./build/wireclient', './build/remotestorage'], function (WireClient, Re var suites = []; function setup(env, test) { - test.assertType(RemoteStorage, 'function'); + test.assertType(RemoteStorage.RemoteStorage, 'function'); } function takedown(env, test) { @@ -13,7 +13,7 @@ define(['./build/wireclient', './build/remotestorage'], function (WireClient, Re } function beforeEach(env, test) { - env.rs = new RemoteStorage(); + env.rs = new RemoteStorage.RemoteStorage(); env.connectedClient = new WireClient(env.rs); env.baseURI = 'https://example.com/storage/test'; env.token = 'foobarbaz'; diff --git a/test/unit/remotestorage-suite.js b/test/unit/remotestorage-suite.js index 26c8a6a7c..4b209cd51 100644 --- a/test/unit/remotestorage-suite.js +++ b/test/unit/remotestorage-suite.js @@ -30,7 +30,7 @@ define(['require', 'tv4', './build/eventhandling', './build/util'], FakeRemote.prototype.get = fakeRequest; FakeRemote.prototype.put = fakeRequest; FakeRemote.prototype.delete = fakeRequest; - util.applyMixins(FakeRemote, [EventHandling]); + util.applyMixins(FakeRemote, [EventHandling.EventHandling]); function FakeLocal() {} @@ -68,13 +68,13 @@ define(['require', 'tv4', './build/eventhandling', './build/util'], setup: function(env, test) { global.XMLHttpRequest = require('xhr2').XMLHttpRequest; global.SyncedGetPutDelete = require('./build/syncedgetputdelete'); - global.Authorize = require('./build/authorize'); + global.Authorize = require('./build/authorize').Authorize; global.UnauthorizedError = require('./build/unauthorized-error'); - global.Sync = require('./build/sync'); + global.Sync = require('./build/sync').Sync; global.config = require('./build/config'); global.log = require('./build/log'); global.Dropbox = require('./build/dropbox'); - global.RemoteStorage = require('./build/remotestorage'); + global.RemoteStorage = require('./build/remotestorage').RemoteStorage; global.Discover = function(userAddress) { var pending = Promise.defer(); diff --git a/test/unit/sync-suite.js b/test/unit/sync-suite.js index 94c6b8b4d..7f6c093e4 100644 --- a/test/unit/sync-suite.js +++ b/test/unit/sync-suite.js @@ -15,15 +15,15 @@ define(['./build/util', 'require', 'test/helpers/mocks'], function(util, require global.Authorize = require('./build/authorize'); global.UnauthorizedError = require('./build/unauthorized-error'); global.config = require('./build/config'); - global.EventHandling = require('./build/eventhandling'); - global.Sync = require('./build/sync'); + global.EventHandling = require('./build/eventhandling').EventHandling; + global.Sync = require('./build/sync').Sync; global.InMemoryStorage = require('./build/inmemorystorage'); class RemoteStorage { static log () {} } util.applyMixins(RemoteStorage, [EventHandling]); global.RemoteStorage = RemoteStorage; - var RS = require('./build/remotestorage'); + var RS = require('./build/remotestorage').RemoteStorage; RemoteStorage.prototype.stopSync = RS.prototype.stopSync; RemoteStorage.prototype.startSync = RS.prototype.startSync; RemoteStorage.prototype.getSyncInterval = RS.prototype.getSyncInterval; diff --git a/test/unit/versioning-suite.js b/test/unit/versioning-suite.js index af33c523c..5cc4fa4c5 100644 --- a/test/unit/versioning-suite.js +++ b/test/unit/versioning-suite.js @@ -25,7 +25,7 @@ define(['./build/config', './build/eventhandling', './build/inmemorystorage', setup: function(env, test){ class RemoteStorage { static log () {} } - util.applyMixins(RemoteStorage, [EventHandling]); + util.applyMixins(RemoteStorage, [EventHandling.default]); global.RemoteStorage = RemoteStorage; config.changeEvents = { local: true, window: false, remote: true, conflict: true }; @@ -363,7 +363,7 @@ define(['./build/config', './build/eventhandling', './build/inmemorystorage', env.rs.remote = new FakeRemote(); env.rs.access = new FakeAccess(); env.rs.caching = new FakeCaching(); - env.rs.sync = new Sync(env.rs, env.rs.local, env.rs.remote, env.rs.access, env.rs.caching); + env.rs.sync = new Sync.Sync(env.rs, env.rs.local, env.rs.remote, env.rs.access, env.rs.caching); global.remoteStorage = env.rs; test.done(); }, diff --git a/test/unit/wireclient-suite.js b/test/unit/wireclient-suite.js index 1dacc41f3..f68bc17ea 100644 --- a/test/unit/wireclient-suite.js +++ b/test/unit/wireclient-suite.js @@ -14,7 +14,7 @@ define(['./build/sync', './build/sync-error', './build/requests', './build/wirec localStorageAvailable() { return false; } static log () {} } - util.applyMixins(RemoteStorage, [EventHandling]); + util.applyMixins(RemoteStorage, [EventHandling.default]); global.RemoteStorage = RemoteStorage; test.done(); @@ -97,7 +97,7 @@ define(['./build/sync', './build/sync-error', './build/requests', './build/wirec desc: "check that instance initialization works without errors", setup: function (env, test) { class RemoteStorage {} - util.applyMixins(RemoteStorage, [EventHandling]); + util.applyMixins(RemoteStorage, [EventHandling.default]); global.RemoteStorage = RemoteStorage; test.done(); }, diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 000000000..b353445bc --- /dev/null +++ b/typedoc.json @@ -0,0 +1,23 @@ +{ + "name": "remoteStorage.js", + "entryPoints": [ + "./src/access.ts", + "./src/baseclient.ts", + "./src/caching.ts", + "./src/eventhandling.ts", + "./src/remote.ts", + "./src/remotestorage.ts" + ], + "out": "./docs/api", + "includeVersion": true, + "excludeNotDocumented": true, + "excludePrivate": true, + "excludeInternal": true, + "excludeProtected": true, + "excludeReferences": true, + "plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"], + "hideBreadcrumbs": true, + "sidebar": { + "autoConfiguration": false + } +} diff --git a/webpack.config.js b/webpack.config.js index 1a3b80f46..06c4263f8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -35,10 +35,12 @@ module.exports = { output: { path: path.resolve(__dirname, 'release'), filename: 'remotestorage.js', - // global export name if needed - library: 'RemoteStorage', - libraryTarget: 'umd', - umdNamedDefine: true, + library: { + name: 'RemoteStorage', + type: 'umd', + export: 'default', + umdNamedDefine: true, + }, globalObject: 'this' } };