diff --git a/sdks/typescript/.changeset/README.md b/sdks/typescript/.changeset/README.md
new file mode 100644
index 00000000000..e5b6d8d6a67
--- /dev/null
+++ b/sdks/typescript/.changeset/README.md
@@ -0,0 +1,8 @@
+# Changesets
+
+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
+with multi-package repos, or single-package repos to help you version and publish your code. You can
+find the full documentation for it [in our repository](https://github.com/changesets/changesets)
+
+We have a quick list of common questions to get you started engaging with this project in
+[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
diff --git a/sdks/typescript/.changeset/config.json b/sdks/typescript/.changeset/config.json
new file mode 100644
index 00000000000..362ba234a2e
--- /dev/null
+++ b/sdks/typescript/.changeset/config.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
+ "changelog": [
+ "@changesets/changelog-github",
+ { "repo": "clockworklabs/spacetimedb-typescript-sdk" }
+ ],
+ "commit": false,
+ "fixed": [],
+ "linked": [],
+ "access": "public",
+ "baseBranch": "main",
+ "updateInternalDependencies": "patch",
+ "ignore": []
+}
diff --git a/sdks/typescript/.gitattributes b/sdks/typescript/.gitattributes
new file mode 100644
index 00000000000..0046e615f49
--- /dev/null
+++ b/sdks/typescript/.gitattributes
@@ -0,0 +1,2 @@
+src/client_api/*.ts linguist-generated=true
+examples/quickstart/client/src/module_bindings/*.ts linguist-generated=true
diff --git a/sdks/typescript/.github/pull_request_template.md b/sdks/typescript/.github/pull_request_template.md
new file mode 100644
index 00000000000..4b7720bd2d4
--- /dev/null
+++ b/sdks/typescript/.github/pull_request_template.md
@@ -0,0 +1,19 @@
+## Description of Changes
+
+_Describe what has been changed, any new features or bug fixes_
+
+## API
+
+- [ ] This is an API breaking change to the SDK
+
+_If the API is breaking, please state below what will break_
+
+## Requires SpacetimeDB PRs
+
+_List any PRs here that are required for this SDK change to work_
+
+## Testing
+
+_Write instructions for a test that you performed for this PR_
+
+- [ ] Describe a test for this PR that you have completed
diff --git a/sdks/typescript/.github/spacetimedb-version.txt b/sdks/typescript/.github/spacetimedb-version.txt
new file mode 100644
index 00000000000..1f7391f92b6
--- /dev/null
+++ b/sdks/typescript/.github/spacetimedb-version.txt
@@ -0,0 +1 @@
+master
diff --git a/sdks/typescript/.github/workflows/cr.yml b/sdks/typescript/.github/workflows/cr.yml
new file mode 100644
index 00000000000..01353168c40
--- /dev/null
+++ b/sdks/typescript/.github/workflows/cr.yml
@@ -0,0 +1,24 @@
+name: Continuous Releases
+
+on: [push, pull_request]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 9.7
+ run_install: true
+
+ - name: Build
+ run: pnpm compile
+
+ - name: Release
+ run: cd packages/sdk && pnpm dlx pkg-pr-new publish --compact --pnpm
diff --git a/sdks/typescript/.github/workflows/lint.yml b/sdks/typescript/.github/workflows/lint.yml
new file mode 100644
index 00000000000..8adf4aa22aa
--- /dev/null
+++ b/sdks/typescript/.github/workflows/lint.yml
@@ -0,0 +1,41 @@
+name: Lint
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 9.7
+ run_install: true
+
+ - name: Get pnpm store directory
+ shell: bash
+ run: |
+ echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
+
+ - uses: actions/cache@v4
+ name: Setup pnpm cache
+ with:
+ path: ${{ env.STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Lint
+ run: pnpm lint
diff --git a/sdks/typescript/.github/workflows/release.yml b/sdks/typescript/.github/workflows/release.yml
new file mode 100644
index 00000000000..5f97085ad9f
--- /dev/null
+++ b/sdks/typescript/.github/workflows/release.yml
@@ -0,0 +1,40 @@
+name: Publish
+on:
+ push:
+ branches:
+ - 'main'
+
+concurrency: ${{ github.workflow }}-${{ github.ref }}
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ contents: write
+ packages: write
+ pull-requests: write
+ issues: read
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 9.7
+ run_install: true
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18.x
+ cache: 'pnpm'
+
+ - run: pnpm compile
+
+ - name: Create Release Pull Request or Publish
+ id: changesets
+ uses: changesets/action@v1
+ with:
+ publish: pnpm run ci:release
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/sdks/typescript/.github/workflows/test.yml b/sdks/typescript/.github/workflows/test.yml
new file mode 100644
index 00000000000..4a55076fcb0
--- /dev/null
+++ b/sdks/typescript/.github/workflows/test.yml
@@ -0,0 +1,146 @@
+name: Tests
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ pull_request:
+
+jobs:
+ compile-and-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 18
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: 9.7
+ run_install: true
+
+ - name: Get pnpm store directory
+ shell: bash
+ run: |
+ echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
+
+ - uses: actions/cache@v4
+ name: Setup pnpm cache
+ with:
+ path: ${{ env.STORE_PATH }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Compile
+ run: pnpm compile
+
+ - name: Run sdk tests
+ working-directory: packages/sdk
+ run: pnpm test
+
+ # - name: Extract SpacetimeDB branch name from file
+ # id: extract-branch
+ # run: |
+ # # Define the path to the branch file
+ # BRANCH_FILE=".github/spacetimedb-branch.txt"
+
+ # # Default to master if file doesn't exist
+ # if [ ! -f "$BRANCH_FILE" ]; then
+ # echo "::notice::No SpacetimeDB branch file found, using 'master'"
+ # echo "branch=master" >> $GITHUB_OUTPUT
+ # exit 0
+ # fi
+
+ # # Read and trim whitespace from the file
+ # branch=$(cat "$BRANCH_FILE" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+
+ # # Fallback to master if empty
+ # if [ -z "$branch" ]; then
+ # echo "::warning::SpacetimeDB branch file is empty, using 'master'"
+ # branch="master"
+ # fi
+
+ # echo "branch=$branch" >> $GITHUB_OUTPUT
+ # echo "Using SpacetimeDB branch from file: $branch"
+
+ - name: Checkout SpacetimeDB
+ uses: actions/checkout@v4
+ with:
+ repository: clockworklabs/SpacetimeDB
+ # ref: ${{ steps.extract-branch.outputs.branch }}
+ path: SpacetimeDB
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Cache Rust dependencies
+ uses: Swatinem/rust-cache@v2
+ with:
+ workspaces: SpacetimeDB/modules/quickstart-chat
+ shared-key: quickstart-chat-test
+
+ - name: Install SpacetimeDB CLI from the local checkout
+ run: |
+ cargo install --force --path SpacetimeDB/crates/cli --locked --message-format=short
+ cargo install --force --path SpacetimeDB/crates/standalone --locked --message-format=short
+ # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules).
+ rm -f $HOME/.cargo/bin/spacetime
+ ln -s $HOME/.cargo/bin/spacetimedb-cli $HOME/.cargo/bin/spacetime
+ # Clear any existing information
+ spacetime server clear -y
+ env:
+ # Share the target directory with our local project to avoid rebuilding same SpacetimeDB crates twice.
+ CARGO_TARGET_DIR: SpacetimeDB/modules/quickstart-chat/target
+
+ - name: Generate client bindings
+ working-directory: SpacetimeDB/modules/quickstart-chat
+ run: |
+ spacetime generate --lang typescript --out-dir ../../../examples/quickstart-chat/src/module_bindings
+ pnpm lint --write
+
+ - name: Check for changes
+ run: |
+ # This was copied from SpacetimeDB/tools/check-diff.sh.
+ # It's required because `spacetime generate` creates lines with the SpacetimeDB commit
+ # version, which would make this `git diff` check very brittle if included.
+ PATTERN='^// This was generated using spacetimedb cli version.*'
+ if ! git diff --exit-code --ignore-matching-lines="$PATTERN" -- examples/quickstart-chat/src/module_bindings; then
+ echo "Error: Bindings are dirty. Please generate bindings again and commit them to this branch."
+ exit 1
+ fi
+
+ # - name: Start SpacetimeDB
+ # run: |
+ # spacetime start &
+ # disown
+
+ # - name: Publish module to SpacetimeDB
+ # working-directory: SpacetimeDB/modules/quickstart-chat
+ # run: |
+ # spacetime logout && spacetime login --server-issued-login local
+ # spacetime publish -s local quickstart-chat -c -y
+
+ # - name: Publish module to SpacetimeDB
+ # working-directory: SpacetimeDB/modules/quickstart-chat
+ # run: |
+ # spacetime logs quickstart-chat
+
+ - name: Check that quickstart-chat builds
+ working-directory: examples/quickstart-chat
+ run: pnpm build
+
+ # - name: Run quickstart-chat tests
+ # working-directory: examples/quickstart-chat
+ # run: pnpm test
+ #
+ # # Run this step always, even if the previous steps fail
+ # - name: Print rows in the user table
+ # if: always()
+ # run: spacetime sql quickstart-chat "SELECT * FROM user"
diff --git a/sdks/typescript/.gitignore b/sdks/typescript/.gitignore
new file mode 100644
index 00000000000..87e920e5947
--- /dev/null
+++ b/sdks/typescript/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+.envrc
+dist/
+.DS_Store
\ No newline at end of file
diff --git a/sdks/typescript/.npmignore b/sdks/typescript/.npmignore
new file mode 100644
index 00000000000..f7f8970df46
--- /dev/null
+++ b/sdks/typescript/.npmignore
@@ -0,0 +1,3 @@
+tests
+.envrc
+node_modules
diff --git a/sdks/typescript/.prettierignore b/sdks/typescript/.prettierignore
new file mode 100644
index 00000000000..3592bad75bc
--- /dev/null
+++ b/sdks/typescript/.prettierignore
@@ -0,0 +1,5 @@
+node_modules
+pnpm-lock.yaml
+dist
+target
+/.github
diff --git a/sdks/typescript/.prettierrc b/sdks/typescript/.prettierrc
new file mode 100644
index 00000000000..2921455b325
--- /dev/null
+++ b/sdks/typescript/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": true,
+ "arrowParens": "avoid",
+ "jsxSingleQuote": false,
+ "trailingComma": "es5",
+ "endOfLine": "auto",
+ "printWidth": 80
+}
diff --git a/sdks/typescript/.vscode/settings.json b/sdks/typescript/.vscode/settings.json
new file mode 100644
index 00000000000..d9546cfe0b8
--- /dev/null
+++ b/sdks/typescript/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "workbench.colorCustomizations": {
+ "[Material Theme Darker]": {},
+ "minimap.background": "#00000000",
+ "scrollbar.shadow": "#00000000"
+ }
+}
diff --git a/sdks/typescript/DEVELOP.md b/sdks/typescript/DEVELOP.md
new file mode 100644
index 00000000000..f01377803f1
--- /dev/null
+++ b/sdks/typescript/DEVELOP.md
@@ -0,0 +1,43 @@
+# Notes for maintainers
+
+The directory `packages/sdk/src/client_api` is generated from [the SpacetimeDB client-api-messages](https://github.com/clockworklabs/SpacetimeDB/tree/master/crates/client-api-messages).
+This is not automated.
+Whenever the `client-api-messages` crate changes, you'll have to manually re-generate the definitions.
+See that crate's DEVELOP.md for how to do this.
+
+The generated files must be manually modified to fix their imports from the rest of the SDK.
+
+Within each generated file:
+
+- Change the import from `"@clockworklabs/spacetimedb-sdk"` to `"../index"`.
+
+On a mac, you can do that by running this in the directory: `find . -type f -exec sed -i '' 's/"@clockworklabs\/spacetimedb-sdk"/"..\/index"/g' {} \;`.
+
+## Releases and publishing
+
+Every Pull Request with a public-facing change (Bug fix, perf, feature etc) must be accompanied by a changeset. Any person working on a patch or feature needs to run `pnpm -w changeset` command, which will prompt them to select packages changed. Choose `@clockworklabs/spacetimedb-sdk`
+
+
+
+Next it will ask whether you'd like to add a Major tag to it. Hit enter to go to minor tag. If its a minor change(In our case, minor is major until v1 comes out, as in every minor can have breaking changes). If its a patch change(Or minor for prerelease time), then again hit enter
+
+After selecting the correct tag, it will ask you for a message
+
+
+
+
+
+Once that is done, hit enter. It will generate a `.md` file which you can then push to github. This all has to be done in the PR with the feature/fix in it.
+
+We can merge it instantly to do a release, or we can merge PRs with their own Changesets. E.g. Any new feature or patch we work on for 1.0 now, should have a Changeset in it. All of these will accumulate in the "Version Packages" PR. Once all these are satisfactorily done, we merge this PR, which will
+
+- Release the package on npm
+- Release on Github tags
+- Update CHANGELOG.md
+
+**NOTE: It is very important that no one manually runs `npm publish`. We have provenance enabled on this package, means each version will be signed by github and traceable to the very commit associated to it**
+
+Publishing manually will breach the provenance contract, and alert security servcies like Snyk into investigating the package or issuing a warning. npm install of our package will also warn them of potential compromise to the package
+
+
+
diff --git a/sdks/typescript/LICENSE b/sdks/typescript/LICENSE
new file mode 100644
index 00000000000..982d9c5e676
--- /dev/null
+++ b/sdks/typescript/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2025 Clockwork Labs, Inc
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/sdks/typescript/README.md b/sdks/typescript/README.md
new file mode 100644
index 00000000000..23a253890e0
--- /dev/null
+++ b/sdks/typescript/README.md
@@ -0,0 +1,3 @@
+`@clockworklabs/spacetimedb-sdk` is a TypeScript SDK for SpacetimeDB.
+
+Source code can be found here on [GitHub](https://github.com/clockworklabs/spacetimedb-typescript-sdk/blob/main/packages/sdk).
diff --git a/sdks/typescript/examples/quickstart-chat/.gitignore b/sdks/typescript/examples/quickstart-chat/.gitignore
new file mode 100644
index 00000000000..a547bf36d8d
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/sdks/typescript/examples/quickstart-chat/CHANGELOG.md b/sdks/typescript/examples/quickstart-chat/CHANGELOG.md
new file mode 100644
index 00000000000..ecc61b68404
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/CHANGELOG.md
@@ -0,0 +1,29 @@
+# quickstart-chat
+
+## 0.0.1
+
+### Patch Changes
+
+- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
+ - @clockworklabs/spacetimedb-sdk@1.2.0
+
+## 0.0.3-rc1.0
+
+### Patch Changes
+
+- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
+ - @clockworklabs/spacetimedb-sdk@1.0.0-rc1.0
+
+## 0.0.2
+
+### Patch Changes
+
+- Updated dependencies [[`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d), [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf), [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912)]:
+ - @clockworklabs/spacetimedb-sdk@0.12.1
+
+## 0.0.1
+
+### Patch Changes
+
+- Updated dependencies [[`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8), [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1), [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213), [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a)]:
+ - @clockworklabs/spacetimedb-sdk@0.12.0
diff --git a/sdks/typescript/examples/quickstart-chat/README.md b/sdks/typescript/examples/quickstart-chat/README.md
new file mode 100644
index 00000000000..195abec4590
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/README.md
@@ -0,0 +1,67 @@
+# SpacetimeDB TypeScript Quickstart Chat
+
+This is a simple chat application that demonstrates how to use SpacetimeDB with TypeScript and React. The chat application is a simple chat room where users can send messages to each other. The chat application uses SpacetimeDB to store the chat messages.
+
+It is based directly on the plain React + TypeScript + Vite template. You can follow the quickstart guide for how creating this project from scratch at [SpacetimeDB TypeScript Quickstart](https://spacetimedb.com/docs/sdks/typescript/quickstart).
+
+You can follow the instructions for creating your own SpacetimeDB module here: [SpacetimeDB Rust Module Quickstart](https://spacetimedb.com/docs/modules/rust/quickstart). Place the module in the `quickstart-chat/server` directory for compability with this project.
+
+In order to run this example, you need to:
+
+- `pnpm compile` in the root directory (`spacetimedb-typescriptsdk`)
+- `pnpm install` in this directory
+- `pnpm run build` in this directory
+- `pnpm run dev` in this directory to run the example
+
+Below is copied from the original template README:
+
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
+
+- Configure the top-level `parserOptions` property like this:
+
+```js
+export default tseslint.config({
+ languageOptions: {
+ // other options...
+ parserOptions: {
+ project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: import.meta.dirname,
+ },
+ },
+});
+```
+
+- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
+- Optionally add `...tseslint.configs.stylisticTypeChecked`
+- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
+
+```js
+// eslint.config.js
+import react from 'eslint-plugin-react';
+
+export default tseslint.config({
+ // Set the react version
+ settings: { react: { version: '18.3' } },
+ plugins: {
+ // Add the react plugin
+ react,
+ },
+ rules: {
+ // other rules...
+ // Enable its recommended rules
+ ...react.configs.recommended.rules,
+ ...react.configs['jsx-runtime'].rules,
+ },
+});
+```
diff --git a/sdks/typescript/examples/quickstart-chat/eslint.config.js b/sdks/typescript/examples/quickstart-chat/eslint.config.js
new file mode 100644
index 00000000000..82c2e20ccc2
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/eslint.config.js
@@ -0,0 +1,28 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import reactHooks from 'eslint-plugin-react-hooks';
+import reactRefresh from 'eslint-plugin-react-refresh';
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config(
+ { ignores: ['dist'] },
+ {
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
+ files: ['**/*.{ts,tsx}'],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
+ rules: {
+ ...reactHooks.configs.recommended.rules,
+ 'react-refresh/only-export-components': [
+ 'warn',
+ { allowConstantExport: true },
+ ],
+ },
+ }
+);
diff --git a/sdks/typescript/examples/quickstart-chat/index.html b/sdks/typescript/examples/quickstart-chat/index.html
new file mode 100644
index 00000000000..e4b78eae123
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite + React + TS
+
+
+
+
+
+
diff --git a/sdks/typescript/examples/quickstart-chat/package.json b/sdks/typescript/examples/quickstart-chat/package.json
new file mode 100644
index 00000000000..bf138510c87
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "client",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview",
+ "test": "vitest",
+ "spacetime:generate-bindings": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path server",
+ "spacetime:publish:local": "spacetime publish chat --project-path server --server local",
+ "spacetime:publish": "spacetime publish chat --project-path server --server testnet"
+ },
+ "dependencies": {
+ "@clockworklabs/spacetimedb-sdk": "workspace:*",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.17.0",
+ "@testing-library/jest-dom": "^6.6.3",
+ "@testing-library/react": "^16.2.0",
+ "@testing-library/user-event": "^14.6.1",
+ "@types/jest": "^29.5.14",
+ "@types/react": "^18.3.18",
+ "@types/react-dom": "^18.3.5",
+ "@vitejs/plugin-react": "^4.3.4",
+ "eslint": "^9.17.0",
+ "eslint-plugin-react-hooks": "^5.0.0",
+ "eslint-plugin-react-refresh": "^0.4.16",
+ "globals": "^15.14.0",
+ "jsdom": "^26.0.0",
+ "typescript": "~5.6.2",
+ "typescript-eslint": "^8.18.2",
+ "vite": "^6.0.5"
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/public/vite.svg b/sdks/typescript/examples/quickstart-chat/public/vite.svg
new file mode 100644
index 00000000000..e7b8dfb1b2a
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/sdks/typescript/examples/quickstart-chat/src/.gitattributes b/sdks/typescript/examples/quickstart-chat/src/.gitattributes
new file mode 100644
index 00000000000..f4d6534ab2c
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/.gitattributes
@@ -0,0 +1 @@
+/module_bindings/** linguist-generated=true
diff --git a/sdks/typescript/examples/quickstart-chat/src/App.css b/sdks/typescript/examples/quickstart-chat/src/App.css
new file mode 100644
index 00000000000..aea01ce5358
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/App.css
@@ -0,0 +1,116 @@
+.App {
+ display: grid;
+ /*
+ 3 rows:
+ 1) Profile
+ 2) Main content (left = message, right = system)
+ 3) New message
+ */
+ grid-template-rows: auto 1fr auto;
+ /* 2 columns: left for chat, right for system */
+ grid-template-columns: 2fr 1fr;
+
+ height: 100vh; /* fill viewport height */
+ width: clamp(300px, 100%, 1200px);
+ margin: 0 auto;
+}
+
+/* ----- Profile (Row 1, spans both columns) ----- */
+.profile {
+ grid-column: 1 / 3;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem;
+ border-bottom: 1px solid var(--theme-color);
+}
+
+.profile h1 {
+ margin-right: auto; /* pushes name/edit form to the right */
+}
+
+.profile form {
+ display: flex;
+ flex-grow: 1;
+ align-items: center;
+ gap: 0.5rem;
+ max-width: 300px;
+}
+
+.profile form input {
+ background-color: var(--textbox-color);
+}
+
+/* ----- Chat Messages (Row 2, Col 1) ----- */
+.message {
+ grid-row: 2 / 3;
+ grid-column: 1 / 2;
+
+ /* Ensure this section scrolls if content is long */
+ overflow-y: auto;
+ padding: 1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.message h1 {
+ margin-right: 0.5rem;
+}
+
+/* ----- System Panel (Row 2, Col 2) ----- */
+.system {
+ grid-row: 2 / 3;
+ grid-column: 2 / 3;
+
+ /* Also scroll independently if needed */
+ overflow-y: auto;
+ padding: 1rem;
+ border-left: 1px solid var(--theme-color);
+ white-space: pre-wrap;
+ font-family: monospace;
+}
+
+/* ----- New Message (Row 3, spans columns 1-2) ----- */
+.new-message {
+ grid-column: 1 / 3;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 1rem;
+ border-top: 1px solid var(--theme-color);
+}
+
+.new-message form {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ width: 100%;
+ max-width: 600px;
+}
+
+.new-message form h3 {
+ margin-bottom: 0.25rem;
+}
+
+/* Distinct background for the textarea */
+.new-message form textarea {
+ font-family: monospace;
+ font-weight: 400;
+ font-size: 1rem;
+ resize: vertical;
+ min-height: 80px;
+ background-color: var(--textbox-color);
+ color: inherit;
+
+ /* Subtle shadow for visibility */
+ box-shadow:
+ 0 1px 3px rgba(0, 0, 0, 0.12),
+ 0 1px 2px rgba(0, 0, 0, 0.24);
+}
+
+@media (prefers-color-scheme: dark) {
+ .new-message form textarea {
+ box-shadow: 0 0 0 1px #17492b;
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/App.integration.test.tsx b/sdks/typescript/examples/quickstart-chat/src/App.integration.test.tsx
new file mode 100644
index 00000000000..7b567204fe3
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/App.integration.test.tsx
@@ -0,0 +1,62 @@
+// src/App.integration.test.tsx
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import App from './App';
+
+describe('App Integration Test', () => {
+ it('connects to the DB, allows name change and message sending', async () => {
+ render( );
+
+ // Initially, we should see "Connecting..."
+ expect(screen.getByText(/Connecting.../i)).toBeInTheDocument();
+
+ // Wait until "Connecting..." is gone (meaning we've connected)
+ // This might require the actual DB to accept the connection
+ await waitFor(
+ () =>
+ expect(screen.queryByText(/Connecting.../i)).not.toBeInTheDocument(),
+ { timeout: 10000 }
+ );
+
+ // The profile section should show the default name or truncated identity
+ // For example, you can check if the text is rendered.
+ // If your default identity is something like 'abcdef12' or 'Unknown'
+ // we do a generic check:
+ expect(
+ screen.getByRole('heading', { name: /profile/i })
+ ).toBeInTheDocument();
+
+ // Let's change the user's name
+ const editNameButton = screen.getByText(/Edit Name/i);
+ await userEvent.click(editNameButton);
+
+ const nameInput = screen.getByRole('textbox', { name: /name input/i });
+ await userEvent.clear(nameInput);
+ await userEvent.type(nameInput, 'TestUser');
+ const submitNameButton = screen.getByRole('button', { name: /submit/i });
+ await userEvent.click(submitNameButton);
+
+ // If your DB or UI updates instantly, we can check that the new name shows up
+ await waitFor(
+ () => {
+ expect(screen.getByText('TestUser')).toBeInTheDocument();
+ },
+ { timeout: 10000 }
+ );
+
+ // Now let's send a message
+ const textarea = screen.getByRole('textbox', { name: /message input/i });
+ await userEvent.type(textarea, 'Hello from GH Actions!');
+
+ const sendButton = screen.getByRole('button', { name: /send/i });
+ await userEvent.click(sendButton);
+
+ // Wait for message to appear in the UI
+ await waitFor(
+ () => {
+ expect(screen.getByText('Hello from GH Actions!')).toBeInTheDocument();
+ },
+ { timeout: 10000 }
+ );
+ });
+});
diff --git a/sdks/typescript/examples/quickstart-chat/src/App.tsx b/sdks/typescript/examples/quickstart-chat/src/App.tsx
new file mode 100644
index 00000000000..fa5998cd1eb
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/App.tsx
@@ -0,0 +1,270 @@
+import React, { useEffect, useState } from 'react';
+import './App.css';
+import {
+ DbConnection,
+ ErrorContext,
+ EventContext,
+ Message,
+ User,
+} from './module_bindings';
+import { Identity } from '@clockworklabs/spacetimedb-sdk';
+
+export type PrettyMessage = {
+ senderName: string;
+ text: string;
+};
+
+function useMessages(conn: DbConnection | null): Message[] {
+ const [messages, setMessages] = useState([]);
+
+ useEffect(() => {
+ if (!conn) return;
+ const onInsert = (_ctx: EventContext, message: Message) => {
+ setMessages(prev => [...prev, message]);
+ };
+ conn.db.message.onInsert(onInsert);
+
+ const onDelete = (_ctx: EventContext, message: Message) => {
+ setMessages(prev =>
+ prev.filter(
+ m =>
+ m.text !== message.text &&
+ m.sent !== message.sent &&
+ m.sender !== message.sender
+ )
+ );
+ };
+ conn.db.message.onDelete(onDelete);
+
+ return () => {
+ conn.db.message.removeOnInsert(onInsert);
+ conn.db.message.removeOnDelete(onDelete);
+ };
+ }, [conn]);
+
+ return messages;
+}
+
+function useUsers(conn: DbConnection | null): Map {
+ const [users, setUsers] = useState>(new Map());
+
+ useEffect(() => {
+ if (!conn) return;
+ const onInsert = (_ctx: EventContext, user: User) => {
+ setUsers(prev => new Map(prev.set(user.identity.toHexString(), user)));
+ };
+ conn.db.user.onInsert(onInsert);
+
+ const onUpdate = (_ctx: EventContext, oldUser: User, newUser: User) => {
+ setUsers(prev => {
+ prev.delete(oldUser.identity.toHexString());
+ return new Map(prev.set(newUser.identity.toHexString(), newUser));
+ });
+ };
+ conn.db.user.onUpdate(onUpdate);
+
+ const onDelete = (_ctx: EventContext, user: User) => {
+ setUsers(prev => {
+ prev.delete(user.identity.toHexString());
+ return new Map(prev);
+ });
+ };
+ conn.db.user.onDelete(onDelete);
+
+ return () => {
+ conn.db.user.removeOnInsert(onInsert);
+ conn.db.user.removeOnUpdate(onUpdate);
+ conn.db.user.removeOnDelete(onDelete);
+ };
+ }, [conn]);
+
+ return users;
+}
+
+function App() {
+ const [newName, setNewName] = useState('');
+ const [settingName, setSettingName] = useState(false);
+ const [systemMessage, setSystemMessage] = useState('');
+ const [newMessage, setNewMessage] = useState('');
+ const [connected, setConnected] = useState(false);
+ const [identity, setIdentity] = useState(null);
+ const [conn, setConn] = useState(null);
+
+ useEffect(() => {
+ const subscribeToQueries = (conn: DbConnection, queries: string[]) => {
+ conn
+ ?.subscriptionBuilder()
+ .onApplied(() => {
+ console.log('SDK client cache initialized.');
+ })
+ .subscribe(queries);
+ };
+
+ const onConnect = (
+ conn: DbConnection,
+ identity: Identity,
+ token: string
+ ) => {
+ setIdentity(identity);
+ setConnected(true);
+ localStorage.setItem('auth_token', token);
+ console.log(
+ 'Connected to SpacetimeDB with identity:',
+ identity.toHexString()
+ );
+ conn.reducers.onSendMessage(() => {
+ console.log('Message sent.');
+ });
+
+ subscribeToQueries(conn, ['SELECT * FROM message', 'SELECT * FROM user']);
+ };
+
+ const onDisconnect = () => {
+ console.log('Disconnected from SpacetimeDB');
+ setConnected(false);
+ };
+
+ const onConnectError = (_ctx: ErrorContext, err: Error) => {
+ console.log('Error connecting to SpacetimeDB:', err);
+ };
+
+ setConn(
+ DbConnection.builder()
+ .withUri('ws://localhost:3000')
+ .withModuleName('quickstart-chat')
+ .withToken(localStorage.getItem('auth_token') || '')
+ .onConnect(onConnect)
+ .onDisconnect(onDisconnect)
+ .onConnectError(onConnectError)
+ .build()
+ );
+ }, []);
+
+ useEffect(() => {
+ if (!conn) return;
+ conn.db.user.onInsert((_ctx, user) => {
+ if (user.online) {
+ const name = user.name || user.identity.toHexString().substring(0, 8);
+ setSystemMessage(prev => prev + `\n${name} has connected.`);
+ }
+ });
+ conn.db.user.onUpdate((_ctx, oldUser, newUser) => {
+ const name =
+ newUser.name || newUser.identity.toHexString().substring(0, 8);
+ if (oldUser.online === false && newUser.online === true) {
+ setSystemMessage(prev => prev + `\n${name} has connected.`);
+ } else if (oldUser.online === true && newUser.online === false) {
+ setSystemMessage(prev => prev + `\n${name} has disconnected.`);
+ }
+ });
+ }, [conn]);
+
+ const messages = useMessages(conn);
+ const users = useUsers(conn);
+
+ const prettyMessages: PrettyMessage[] = messages
+ .sort((a, b) => (a.sent > b.sent ? 1 : -1))
+ .map(message => ({
+ senderName:
+ users.get(message.sender.toHexString())?.name ||
+ message.sender.toHexString().substring(0, 8),
+ text: message.text,
+ }));
+
+ if (!conn || !connected || !identity) {
+ return (
+
+
Connecting...
+
+ );
+ }
+
+ const name =
+ users.get(identity?.toHexString())?.name ||
+ identity?.toHexString().substring(0, 8) ||
+ '';
+
+ const onSubmitNewName = (e: React.FormEvent) => {
+ e.preventDefault();
+ setSettingName(false);
+ conn.reducers.setName(newName);
+ };
+
+ const onMessageSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ setNewMessage('');
+ conn.reducers.sendMessage(newMessage);
+ };
+
+ return (
+
+
+
Profile
+ {!settingName ? (
+ <>
+
{name}
+
{
+ setSettingName(true);
+ setNewName(name);
+ }}
+ >
+ Edit Name
+
+ >
+ ) : (
+
+ )}
+
+
+
Messages
+ {prettyMessages.length < 1 &&
No messages
}
+
+ {prettyMessages.map((message, key) => (
+
+
+ {message.senderName}
+
+
{message.text}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/sdks/typescript/examples/quickstart-chat/src/assets/react.svg b/sdks/typescript/examples/quickstart-chat/src/assets/react.svg
new file mode 100644
index 00000000000..6c87de9bb33
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/sdks/typescript/examples/quickstart-chat/src/index.css b/sdks/typescript/examples/quickstart-chat/src/index.css
new file mode 100644
index 00000000000..9390800cc7e
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/index.css
@@ -0,0 +1,76 @@
+/* ----- CSS Reset & Global Settings ----- */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+/* ----- Color Variables ----- */
+:root {
+ --theme-color: #3dc373;
+ --theme-color-contrast: #08180e;
+ --textbox-color: #edfef4;
+ color-scheme: light dark;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --theme-color: #4cf490;
+ --theme-color-contrast: #132219;
+ --textbox-color: #0f311d;
+ }
+}
+
+/* ----- Page Setup ----- */
+html,
+body,
+#root {
+ height: 100%;
+ margin: 0;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+/* ----- Buttons ----- */
+button {
+ padding: 0.5rem 0.75rem;
+ border: none;
+ border-radius: 0.375rem;
+ background-color: var(--theme-color);
+ color: var(--theme-color-contrast);
+ cursor: pointer;
+ font-weight: 600;
+ letter-spacing: 0.1px;
+ font-family: monospace;
+}
+
+/* ----- Inputs & Textareas ----- */
+input,
+textarea {
+ border: none;
+ border-radius: 0.375rem;
+ caret-color: var(--theme-color);
+ font-family: monospace;
+ font-weight: 600;
+ letter-spacing: 0.1px;
+ padding: 0.5rem 0.75rem;
+}
+
+input:focus,
+textarea:focus {
+ outline: none;
+ box-shadow: 0 0 0 2px var(--theme-color);
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/main.tsx b/sdks/typescript/examples/quickstart-chat/src/main.tsx
new file mode 100644
index 00000000000..df655eaec4e
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App.tsx';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_connected_reducer.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_connected_reducer.ts
new file mode 100644
index 00000000000..fedf44c4e10
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_connected_reducer.ts
@@ -0,0 +1,60 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+export type IdentityConnected = {};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace IdentityConnected {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: IdentityConnected
+ ): void {
+ IdentityConnected.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): IdentityConnected {
+ return IdentityConnected.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_disconnected_reducer.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_disconnected_reducer.ts
new file mode 100644
index 00000000000..b958d1f82d5
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/identity_disconnected_reducer.ts
@@ -0,0 +1,62 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+export type IdentityDisconnected = {};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace IdentityDisconnected {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: IdentityDisconnected
+ ): void {
+ IdentityDisconnected.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): IdentityDisconnected {
+ return IdentityDisconnected.getTypeScriptAlgebraicType().deserialize(
+ reader
+ );
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/index.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/index.ts
new file mode 100644
index 00000000000..8e267b61763
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/index.ts
@@ -0,0 +1,276 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+// Import and reexport all reducer arg types
+import { IdentityConnected } from './identity_connected_reducer.ts';
+export { IdentityConnected };
+import { IdentityDisconnected } from './identity_disconnected_reducer.ts';
+export { IdentityDisconnected };
+import { SendMessage } from './send_message_reducer.ts';
+export { SendMessage };
+import { SetName } from './set_name_reducer.ts';
+export { SetName };
+
+// Import and reexport all table handle types
+import { MessageTableHandle } from './message_table.ts';
+export { MessageTableHandle };
+import { UserTableHandle } from './user_table.ts';
+export { UserTableHandle };
+
+// Import and reexport all types
+import { Message } from './message_type.ts';
+export { Message };
+import { User } from './user_type.ts';
+export { User };
+
+const REMOTE_MODULE = {
+ tables: {
+ message: {
+ tableName: 'message',
+ rowType: Message.getTypeScriptAlgebraicType(),
+ },
+ user: {
+ tableName: 'user',
+ rowType: User.getTypeScriptAlgebraicType(),
+ primaryKey: 'identity',
+ primaryKeyInfo: {
+ colName: 'identity',
+ colType:
+ User.getTypeScriptAlgebraicType().product.elements[0].algebraicType,
+ },
+ },
+ },
+ reducers: {
+ identity_connected: {
+ reducerName: 'identity_connected',
+ argsType: IdentityConnected.getTypeScriptAlgebraicType(),
+ },
+ identity_disconnected: {
+ reducerName: 'identity_disconnected',
+ argsType: IdentityDisconnected.getTypeScriptAlgebraicType(),
+ },
+ send_message: {
+ reducerName: 'send_message',
+ argsType: SendMessage.getTypeScriptAlgebraicType(),
+ },
+ set_name: {
+ reducerName: 'set_name',
+ argsType: SetName.getTypeScriptAlgebraicType(),
+ },
+ },
+ versionInfo: {
+ cliVersion: '1.2.0',
+ },
+ // Constructors which are used by the DbConnectionImpl to
+ // extract type information from the generated RemoteModule.
+ //
+ // NOTE: This is not strictly necessary for `eventContextConstructor` because
+ // all we do is build a TypeScript object which we could have done inside the
+ // SDK, but if in the future we wanted to create a class this would be
+ // necessary because classes have methods, so we'll keep it.
+ eventContextConstructor: (imp: DbConnectionImpl, event: Event) => {
+ return {
+ ...(imp as DbConnection),
+ event,
+ };
+ },
+ dbViewConstructor: (imp: DbConnectionImpl) => {
+ return new RemoteTables(imp);
+ },
+ reducersConstructor: (
+ imp: DbConnectionImpl,
+ setReducerFlags: SetReducerFlags
+ ) => {
+ return new RemoteReducers(imp, setReducerFlags);
+ },
+ setReducerFlagsConstructor: () => {
+ return new SetReducerFlags();
+ },
+};
+
+// A type representing all the possible variants of a reducer.
+export type Reducer =
+ | never
+ | { name: 'IdentityConnected'; args: IdentityConnected }
+ | { name: 'IdentityDisconnected'; args: IdentityDisconnected }
+ | { name: 'SendMessage'; args: SendMessage }
+ | { name: 'SetName'; args: SetName };
+
+export class RemoteReducers {
+ constructor(
+ private connection: DbConnectionImpl,
+ private setCallReducerFlags: SetReducerFlags
+ ) {}
+
+ onIdentityConnected(callback: (ctx: ReducerEventContext) => void) {
+ this.connection.onReducer('identity_connected', callback);
+ }
+
+ removeOnIdentityConnected(callback: (ctx: ReducerEventContext) => void) {
+ this.connection.offReducer('identity_connected', callback);
+ }
+
+ onIdentityDisconnected(callback: (ctx: ReducerEventContext) => void) {
+ this.connection.onReducer('identity_disconnected', callback);
+ }
+
+ removeOnIdentityDisconnected(callback: (ctx: ReducerEventContext) => void) {
+ this.connection.offReducer('identity_disconnected', callback);
+ }
+
+ sendMessage(text: string) {
+ const __args = { text };
+ let __writer = new BinaryWriter(1024);
+ SendMessage.getTypeScriptAlgebraicType().serialize(__writer, __args);
+ let __argsBuffer = __writer.getBuffer();
+ this.connection.callReducer(
+ 'send_message',
+ __argsBuffer,
+ this.setCallReducerFlags.sendMessageFlags
+ );
+ }
+
+ onSendMessage(callback: (ctx: ReducerEventContext, text: string) => void) {
+ this.connection.onReducer('send_message', callback);
+ }
+
+ removeOnSendMessage(
+ callback: (ctx: ReducerEventContext, text: string) => void
+ ) {
+ this.connection.offReducer('send_message', callback);
+ }
+
+ setName(name: string) {
+ const __args = { name };
+ let __writer = new BinaryWriter(1024);
+ SetName.getTypeScriptAlgebraicType().serialize(__writer, __args);
+ let __argsBuffer = __writer.getBuffer();
+ this.connection.callReducer(
+ 'set_name',
+ __argsBuffer,
+ this.setCallReducerFlags.setNameFlags
+ );
+ }
+
+ onSetName(callback: (ctx: ReducerEventContext, name: string) => void) {
+ this.connection.onReducer('set_name', callback);
+ }
+
+ removeOnSetName(callback: (ctx: ReducerEventContext, name: string) => void) {
+ this.connection.offReducer('set_name', callback);
+ }
+}
+
+export class SetReducerFlags {
+ sendMessageFlags: CallReducerFlags = 'FullUpdate';
+ sendMessage(flags: CallReducerFlags) {
+ this.sendMessageFlags = flags;
+ }
+
+ setNameFlags: CallReducerFlags = 'FullUpdate';
+ setName(flags: CallReducerFlags) {
+ this.setNameFlags = flags;
+ }
+}
+
+export class RemoteTables {
+ constructor(private connection: DbConnectionImpl) {}
+
+ get message(): MessageTableHandle {
+ return new MessageTableHandle(
+ this.connection.clientCache.getOrCreateTable(
+ REMOTE_MODULE.tables.message
+ )
+ );
+ }
+
+ get user(): UserTableHandle {
+ return new UserTableHandle(
+ this.connection.clientCache.getOrCreateTable(
+ REMOTE_MODULE.tables.user
+ )
+ );
+ }
+}
+
+export class SubscriptionBuilder extends SubscriptionBuilderImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {}
+
+export class DbConnection extends DbConnectionImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {
+ static builder = (): DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ > => {
+ return new DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ >(REMOTE_MODULE, (imp: DbConnectionImpl) => imp as DbConnection);
+ };
+ subscriptionBuilder = (): SubscriptionBuilder => {
+ return new SubscriptionBuilder(this);
+ };
+}
+
+export type EventContext = EventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type ReducerEventContext = ReducerEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type SubscriptionEventContext = SubscriptionEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
+export type ErrorContext = ErrorContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_table.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_table.ts
new file mode 100644
index 00000000000..bd410af5126
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_table.ts
@@ -0,0 +1,83 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { Message } from './message_type';
+import {
+ type EventContext,
+ type Reducer,
+ RemoteReducers,
+ RemoteTables,
+} from '.';
+
+/**
+ * Table handle for the table `message`.
+ *
+ * Obtain a handle from the [`message`] property on [`RemoteTables`],
+ * like `ctx.db.message`.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.message.on_insert(...)`.
+ */
+export class MessageTableHandle {
+ tableCache: TableCache;
+
+ constructor(tableCache: TableCache) {
+ this.tableCache = tableCache;
+ }
+
+ count(): number {
+ return this.tableCache.count();
+ }
+
+ iter(): Iterable {
+ return this.tableCache.iter();
+ }
+
+ onInsert = (cb: (ctx: EventContext, row: Message) => void) => {
+ return this.tableCache.onInsert(cb);
+ };
+
+ removeOnInsert = (cb: (ctx: EventContext, row: Message) => void) => {
+ return this.tableCache.removeOnInsert(cb);
+ };
+
+ onDelete = (cb: (ctx: EventContext, row: Message) => void) => {
+ return this.tableCache.onDelete(cb);
+ };
+
+ removeOnDelete = (cb: (ctx: EventContext, row: Message) => void) => {
+ return this.tableCache.removeOnDelete(cb);
+ };
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_type.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_type.ts
new file mode 100644
index 00000000000..c8c0cc87394
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/message_type.ts
@@ -0,0 +1,64 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+export type Message = {
+ sender: Identity;
+ sent: Timestamp;
+ text: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace Message {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('sender', AlgebraicType.createIdentityType()),
+ new ProductTypeElement('sent', AlgebraicType.createTimestampType()),
+ new ProductTypeElement('text', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: Message): void {
+ Message.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): Message {
+ return Message.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/send_message_reducer.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/send_message_reducer.ts
new file mode 100644
index 00000000000..988206739fe
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/send_message_reducer.ts
@@ -0,0 +1,61 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+export type SendMessage = {
+ text: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SendMessage {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('text', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: SendMessage): void {
+ SendMessage.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SendMessage {
+ return SendMessage.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/set_name_reducer.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/set_name_reducer.ts
new file mode 100644
index 00000000000..e1a587cd708
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/set_name_reducer.ts
@@ -0,0 +1,61 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+export type SetName = {
+ name: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SetName {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: SetName): void {
+ SetName.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SetName {
+ return SetName.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_table.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_table.ts
new file mode 100644
index 00000000000..605a14bc304
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_table.ts
@@ -0,0 +1,116 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { User } from './user_type';
+import {
+ type EventContext,
+ type Reducer,
+ RemoteReducers,
+ RemoteTables,
+} from '.';
+
+/**
+ * Table handle for the table `user`.
+ *
+ * Obtain a handle from the [`user`] property on [`RemoteTables`],
+ * like `ctx.db.user`.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.user.on_insert(...)`.
+ */
+export class UserTableHandle {
+ tableCache: TableCache;
+
+ constructor(tableCache: TableCache) {
+ this.tableCache = tableCache;
+ }
+
+ count(): number {
+ return this.tableCache.count();
+ }
+
+ iter(): Iterable {
+ return this.tableCache.iter();
+ }
+ /**
+ * Access to the `identity` unique index on the table `user`,
+ * which allows point queries on the field of the same name
+ * via the [`UserIdentityUnique.find`] method.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.user.identity().find(...)`.
+ *
+ * Get a handle on the `identity` unique index on the table `user`.
+ */
+ identity = {
+ // Find the subscribed row whose `identity` column value is equal to `col_val`,
+ // if such a row is present in the client cache.
+ find: (col_val: Identity): User | undefined => {
+ for (let row of this.tableCache.iter()) {
+ if (deepEqual(row.identity, col_val)) {
+ return row;
+ }
+ }
+ },
+ };
+
+ onInsert = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.onInsert(cb);
+ };
+
+ removeOnInsert = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.removeOnInsert(cb);
+ };
+
+ onDelete = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.onDelete(cb);
+ };
+
+ removeOnDelete = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.removeOnDelete(cb);
+ };
+
+ // Updates are only defined for tables with primary keys.
+ onUpdate = (cb: (ctx: EventContext, oldRow: User, newRow: User) => void) => {
+ return this.tableCache.onUpdate(cb);
+ };
+
+ removeOnUpdate = (
+ cb: (ctx: EventContext, onRow: User, newRow: User) => void
+ ) => {
+ return this.tableCache.removeOnUpdate(cb);
+ };
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_type.ts b/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_type.ts
new file mode 100644
index 00000000000..6f11bff36d6
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/module_bindings/user_type.ts
@@ -0,0 +1,67 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+export type User = {
+ identity: Identity;
+ name: string | undefined;
+ online: boolean;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace User {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('identity', AlgebraicType.createIdentityType()),
+ new ProductTypeElement(
+ 'name',
+ AlgebraicType.createOptionType(AlgebraicType.createStringType())
+ ),
+ new ProductTypeElement('online', AlgebraicType.createBoolType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: User): void {
+ User.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): User {
+ return User.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/examples/quickstart-chat/src/setupTests.ts b/sdks/typescript/examples/quickstart-chat/src/setupTests.ts
new file mode 100644
index 00000000000..7b0828bfa80
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/setupTests.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom';
diff --git a/sdks/typescript/examples/quickstart-chat/src/vite-env.d.ts b/sdks/typescript/examples/quickstart-chat/src/vite-env.d.ts
new file mode 100644
index 00000000000..11f02fe2a00
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/sdks/typescript/examples/quickstart-chat/tsconfig.app.json b/sdks/typescript/examples/quickstart-chat/tsconfig.app.json
new file mode 100644
index 00000000000..358ca9ba93f
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/tsconfig.app.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/sdks/typescript/examples/quickstart-chat/tsconfig.json b/sdks/typescript/examples/quickstart-chat/tsconfig.json
new file mode 100644
index 00000000000..1ffef600d95
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/sdks/typescript/examples/quickstart-chat/tsconfig.node.json b/sdks/typescript/examples/quickstart-chat/tsconfig.node.json
new file mode 100644
index 00000000000..db0becc8b03
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/tsconfig.node.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/sdks/typescript/examples/quickstart-chat/vite.config.ts b/sdks/typescript/examples/quickstart-chat/vite.config.ts
new file mode 100644
index 00000000000..4a5def4c3d7
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+});
diff --git a/sdks/typescript/examples/quickstart-chat/vitest.config.ts b/sdks/typescript/examples/quickstart-chat/vitest.config.ts
new file mode 100644
index 00000000000..150380175e1
--- /dev/null
+++ b/sdks/typescript/examples/quickstart-chat/vitest.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vitest/config';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ globals: true,
+ environment: 'jsdom', // or "node" if you're not testing DOM
+ setupFiles: './src/setupTests.ts',
+ testTimeout: 30_000, // give extra time for real connections
+ hookTimeout: 30_000,
+ },
+});
diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json
new file mode 100644
index 00000000000..85428c188b4
--- /dev/null
+++ b/sdks/typescript/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "spacetimedb-ts-sdk",
+ "private": true,
+ "engines": {
+ "node": ">=18.0.0",
+ "pnpm": ">=9.0.0"
+ },
+ "scripts": {
+ "compile": "cd packages/sdk && pnpm compile",
+ "changeset": "changeset",
+ "ci:release": "changeset publish",
+ "ci:version": "changeset version",
+ "format": "prettier --write .",
+ "lint": "prettier . --check",
+ "test": "pnpm -r test"
+ },
+ "devDependencies": {
+ "@changesets/changelog-github": "^0.5.0",
+ "@changesets/cli": "^2.27.7",
+ "brotli-size-cli": "^1.0.0",
+ "prettier": "^3.3.3",
+ "terser": "^5.31.2",
+ "tsup": "^8.1.0",
+ "tsx": "^4.17.0",
+ "typescript": "^5.5.3",
+ "vitest": "^2.0.3"
+ }
+}
diff --git a/sdks/typescript/packages/sdk/CHANGELOG.md b/sdks/typescript/packages/sdk/CHANGELOG.md
new file mode 100644
index 00000000000..787f1c64640
--- /dev/null
+++ b/sdks/typescript/packages/sdk/CHANGELOG.md
@@ -0,0 +1,90 @@
+# Changelog
+
+## 1.2.0
+
+### Patch Changes
+
+- [#176](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/176) [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673) Thanks [@BastianGanze](https://github.com/BastianGanze)! - Make ws connection fail when token is invalid
+
+## 1.0.0-rc1.0
+
+### Major Changes
+
+- [#116](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/116) [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114) Thanks [@PuruVJ](https://github.com/PuruVJ)! - Enter RC
+
+- [#117](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/117) [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179) Thanks [@PuruVJ](https://github.com/PuruVJ)! - feat: Switch to GZIP compression by default
+
+### Minor Changes
+
+- [#110](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/110) [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e) Thanks [@Centril](https://github.com/Centril)! - Support light tx updates via builder.with*light_mode(*) and the call flag NoSuccessNotify
+
+- [#119](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/119) [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff) Thanks [@kazimuth](https://github.com/kazimuth)! - Update Identity and Address to use bigints rather than byte arrays (see https://github.com/clockworklabs/SpacetimeDB/pull/1616)
+
+### Patch Changes
+
+- [#109](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/109) [`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: websocket message handling, Buffer, onConnect
+
+## 0.12.1
+
+### Patch Changes
+
+- [#107](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/107) [`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: websocket message handling, Buffer, onConnect
+
+- [#108](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/108) [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf) Thanks [@PuruVJ](https://github.com/PuruVJ)! - docs: Public facing docs for 0.12
+
+- [#105](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/105) [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912) Thanks [@PuruVJ](https://github.com/PuruVJ)! - fix: temporary token path invocation
+
+## 0.12.0
+
+### Minor Changes
+
+- [#92](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/92) [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1) Thanks [@PuruVJ](https://github.com/PuruVJ)! - breaking: Flatten AlgebraicType & Simplify some codegen
+
+- [#102](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/102) [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213) Thanks [@cloutiertyler](https://github.com/cloutiertyler)! - internal: Remove global instance, allow multiple connections
+
+### Patch Changes
+
+- [#91](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/91) [`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8) Thanks [@PuruVJ](https://github.com/PuruVJ)! - types: Allow autocomplete in .on and .off types
+
+- [#96](https://github.com/clockworklabs/spacetimedb-typescript-sdk/pull/96) [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a) Thanks [@PuruVJ](https://github.com/PuruVJ)! - (fix) Synchronous WS Processing
+
+## [0.8.0](https://github.com/clockworklabs/spacetimedb-typescript-sdk/compare/0.7.2...0.8.0) (2023-12-11)
+
+### Bug Fixes
+
+- Properly use BigInt for any numbers bigger than 32 bits
+- Fix generating primary key names to be camel case
+
+### Features
+
+- Added ability to start multiple SpacetimeDB clients. New clients will have a separate ClientDB
+- Changed the return type of functions returning table records - now they are arras instead of iterators
+- Reducer callbacks have args passed in separately, which makes it easier to know what types they are
+ For example a reducer taking a single string argument will have a callback signature like `(reducerEvent: ReducerEvent, name: string)`
+ instead of `(reducerEvent: ReducerEvent, args: any[])`
+- We now require explicitly registering any tables or reducers with `SpacetimeDBClient.registerReducers()` and `SpacetimeDBClient.registerTables()`.
+ This also allows to register child classes, which in turn allows to use customized table classes. We will add more info
+ on how to do it in the future. This makes it also harder to run into weird issues. If you only import a reducer, but not use
+ it to set any callbacks, Node.js will filter out the import. If you then subscribe to a table SpacetimeDBClient will be unable
+ to find the reducer. To ensure this is not happening people were adding a `console.log` statement listing and used classes to
+ stop Node.js from filtering out any imports, like `console.log(SayHelloReducer)`. Now with the reducer call it's more explicit
+- In this release we have also moved some methods from generated types into the SDK, which should result in a smaller footprint from
+ generated classes
+- Generated sum types are now easier to use. For sum types without any values you can use their type name as value, for example given an
+ enum in Rust:
+
+ ```rust
+ enum UserRole {
+ Admin,
+ Moderator,
+ User,
+ Other(String)
+ }
+ ```
+
+ you can now use types itself as values. For example given a reducer for setting a role you could now do the following in TypeScript:
+
+ ```typescript
+ SetRoleReducer.call(UserRole.Admin);
+ SetRoleReducer.call(UserRole.Other('another role'));
+ ```
diff --git a/sdks/typescript/packages/sdk/LICENSE.txt b/sdks/typescript/packages/sdk/LICENSE.txt
new file mode 100644
index 00000000000..4a459866a57
--- /dev/null
+++ b/sdks/typescript/packages/sdk/LICENSE.txt
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/sdks/typescript/packages/sdk/README.md b/sdks/typescript/packages/sdk/README.md
new file mode 100644
index 00000000000..884c2a92623
--- /dev/null
+++ b/sdks/typescript/packages/sdk/README.md
@@ -0,0 +1,73 @@
+## SpacetimeDB SDK
+
+### Overview
+
+This repository contains the TypeScript SDK for SpacetimeDB. The SDK allows to interact with the database server and is prepared to work with code generated from a SpacetimeDB backend code.
+
+### Installation
+
+The SDK is an NPM package, thus you can use your package manager of choice like NPM or Yarn, for example:
+
+```
+npm install --save @clockworklabs/spacetimedb-sdk
+```
+
+You can use the package in the browser, using a bundler like vite/parcel/rsbuild, in server-side applications like NodeJS, Deno, Bun and in Cloudflare Workers.
+
+> NOTE: For usage in NodeJS 18-21, you need to install the `undici` package as a peer dependency: `npm install @clockworklabs/spacetimedb-sdk undici`. Node 22 and later are supported out of the box.
+
+### Usage
+
+In order to connect to a database you have to generate module bindings for your database.
+
+```ts
+import { DbConnection } from './module_bindings';
+
+const connection = DbConnection.builder()
+ .withUri('ws://localhost:3000')
+ .withModuleName('MODULE_NAME')
+ .onDisconnect(() => {
+ console.log('disconnected');
+ })
+ .onConnectError(() => {
+ console.log('client_error');
+ })
+ .onConnect((connection, identity, _token) => {
+ console.log(
+ 'Connected to SpacetimeDB with identity:',
+ identity.toHexString()
+ );
+
+ connection.subscriptionBuilder().subscribe('SELECT * FROM player');
+ })
+ .withToken('TOKEN')
+ .build();
+```
+
+If for some reason you need to disconnect the client:
+
+```ts
+connection.disconnect();
+```
+
+Typically, you will use the SDK with types generated from a backend DB service. For example, given a table named `Player` you can subscribe to player updates like this:
+
+```ts
+connection.db.player.onInsert((ctx, player) => {
+ console.log(player);
+});
+```
+
+Given a reducer called `CreatePlayer` you can call it using a call method:
+
+```ts
+connection.reducers.createPlayer();
+```
+
+### Developer notes
+
+To run the tests, do:
+
+```sh
+pnpm compile && pnpm test
+```
diff --git a/sdks/typescript/packages/sdk/package.json b/sdks/typescript/packages/sdk/package.json
new file mode 100644
index 00000000000..6b675b00371
--- /dev/null
+++ b/sdks/typescript/packages/sdk/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "@clockworklabs/spacetimedb-sdk",
+ "version": "1.2.2",
+ "description": "SDK for SpacetimeDB",
+ "author": {
+ "name": "Clockwork Labs",
+ "email": "no-reply@clockworklabs.io"
+ },
+ "types": "./dist/index.d.ts",
+ "main": "./dist/index.js",
+ "module": "./dist/index.js",
+ "browser": "./dist/browser/index.js",
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "browser": "./dist/browser/index.js",
+ "import": "./dist/index.js",
+ "default": "./dist/index.js"
+ },
+ "./package.json": "./package.json"
+ },
+ "scripts": {
+ "compile": "tsup",
+ "test": "vitest run",
+ "size": "brotli-size dist/min/index.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/clockworklabs/spacetimedb-typescript-sdk"
+ },
+ "publishConfig": {
+ "provenance": true
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "undici": "^6.19.2"
+ },
+ "devDependencies": {
+ "@clockworklabs/test-app": "file:../test-app",
+ "tsup": "^8.1.0",
+ "undici": "^6.19.2"
+ },
+ "dependencies": {
+ "base64-js": "^1.5.1"
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/algebraic_type.ts b/sdks/typescript/packages/sdk/src/algebraic_type.ts
new file mode 100644
index 00000000000..0c9846fedeb
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/algebraic_type.ts
@@ -0,0 +1,672 @@
+import { TimeDuration } from './time_duration';
+import { Timestamp } from './timestamp';
+import { ConnectionId } from './connection_id';
+import type BinaryReader from './binary_reader';
+import BinaryWriter from './binary_writer';
+import { Identity } from './identity';
+import ScheduleAt from './schedule_at';
+
+/**
+ * A variant of a sum type.
+ *
+ * NOTE: Each element has an implicit element tag based on its order.
+ * Uniquely identifies an element similarly to protobuf tags.
+ */
+export class SumTypeVariant {
+ name: string;
+ algebraicType: AlgebraicType;
+
+ constructor(name: string, algebraicType: AlgebraicType) {
+ this.name = name;
+ this.algebraicType = algebraicType;
+ }
+}
+
+/**
+ * Unlike most languages, sums in SATS are *[structural]* and not nominal.
+ * When checking whether two nominal types are the same,
+ * their names and/or declaration sites (e.g., module / namespace) are considered.
+ * Meanwhile, a structural type system would only check the structure of the type itself,
+ * e.g., the names of its variants and their inner data types in the case of a sum.
+ *
+ * This is also known as a discriminated union (implementation) or disjoint union.
+ * Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).
+ *
+ * These structures are known as sum types because the number of possible values a sum
+ * ```ignore
+ * { N_0(T_0), N_1(T_1), ..., N_n(T_n) }
+ * ```
+ * is:
+ * ```ignore
+ * Σ (i ∈ 0..n). values(T_i)
+ * ```
+ * so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.
+ *
+ * See also: https://ncatlab.org/nlab/show/sum+type.
+ *
+ * [structural]: https://en.wikipedia.org/wiki/Structural_type_system
+ */
+export class SumType {
+ variants: SumTypeVariant[];
+
+ constructor(variants: SumTypeVariant[]) {
+ this.variants = variants;
+ }
+
+ serialize = (writer: BinaryWriter, value: any): void => {
+ // In TypeScript we handle Option values as a special case
+ // we don't represent the some and none variants, but instead
+ // we represent the value directly.
+ if (
+ this.variants.length == 2 &&
+ this.variants[0].name === 'some' &&
+ this.variants[1].name === 'none'
+ ) {
+ if (value !== null && value !== undefined) {
+ writer.writeByte(0);
+ this.variants[0].algebraicType.serialize(writer, value);
+ } else {
+ writer.writeByte(1);
+ }
+ } else {
+ let variant = value['tag'];
+ const index = this.variants.findIndex(v => v.name === variant);
+ if (index < 0) {
+ throw `Can't serialize a sum type, couldn't find ${value.tag} tag`;
+ }
+ writer.writeU8(index);
+ this.variants[index].algebraicType.serialize(writer, value['value']);
+ }
+ };
+
+ deserialize = (reader: BinaryReader): any => {
+ let tag = reader.readU8();
+ // In TypeScript we handle Option values as a special case
+ // we don't represent the some and none variants, but instead
+ // we represent the value directly.
+ if (
+ this.variants.length == 2 &&
+ this.variants[0].name === 'some' &&
+ this.variants[1].name === 'none'
+ ) {
+ if (tag === 0) {
+ return this.variants[0].algebraicType.deserialize(reader);
+ } else if (tag === 1) {
+ return undefined;
+ } else {
+ throw `Can't deserialize an option type, couldn't find ${tag} tag`;
+ }
+ } else {
+ let variant = this.variants[tag];
+ let value = variant.algebraicType.deserialize(reader);
+ return { tag: variant.name, value };
+ }
+ };
+}
+
+/**
+ * A factor / element of a product type.
+ *
+ * An element consist of an optional name and a type.
+ *
+ * NOTE: Each element has an implicit element tag based on its order.
+ * Uniquely identifies an element similarly to protobuf tags.
+ */
+export class ProductTypeElement {
+ name: string;
+ algebraicType: AlgebraicType;
+
+ constructor(name: string, algebraicType: AlgebraicType) {
+ this.name = name;
+ this.algebraicType = algebraicType;
+ }
+}
+
+/**
+ * A structural product type of the factors given by `elements`.
+ *
+ * This is also known as `struct` and `tuple` in many languages,
+ * but note that unlike most languages, products in SATs are *[structural]* and not nominal.
+ * When checking whether two nominal types are the same,
+ * their names and/or declaration sites (e.g., module / namespace) are considered.
+ * Meanwhile, a structural type system would only check the structure of the type itself,
+ * e.g., the names of its fields and their types in the case of a record.
+ * The name "product" comes from category theory.
+ *
+ * See also: https://ncatlab.org/nlab/show/product+type.
+ *
+ * These structures are known as product types because the number of possible values in product
+ * ```ignore
+ * { N_0: T_0, N_1: T_1, ..., N_n: T_n }
+ * ```
+ * is:
+ * ```ignore
+ * Π (i ∈ 0..n). values(T_i)
+ * ```
+ * so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.
+ *
+ * [structural]: https://en.wikipedia.org/wiki/Structural_type_system
+ */
+export class ProductType {
+ elements: ProductTypeElement[];
+
+ constructor(elements: ProductTypeElement[]) {
+ this.elements = elements;
+ }
+
+ isEmpty(): boolean {
+ return this.elements.length === 0;
+ }
+
+ serialize = (writer: BinaryWriter, value: object): void => {
+ for (let element of this.elements) {
+ element.algebraicType.serialize(writer, value[element.name]);
+ }
+ };
+
+ intoMapKey(value: any): ComparablePrimitive {
+ if (this.elements.length === 1) {
+ if (this.elements[0].name === '__time_duration_micros__') {
+ return (value as TimeDuration).__time_duration_micros__;
+ }
+
+ if (this.elements[0].name === '__timestamp_micros_since_unix_epoch__') {
+ return (value as Timestamp).__timestamp_micros_since_unix_epoch__;
+ }
+
+ if (this.elements[0].name === '__identity__') {
+ return (value as Identity).__identity__;
+ }
+
+ if (this.elements[0].name === '__connection_id__') {
+ return (value as ConnectionId).__connection_id__;
+ }
+ }
+ // The fallback is to serialize and base64 encode the bytes.
+ const writer = new BinaryWriter(10);
+ this.serialize(writer, value);
+ return writer.toBase64();
+ }
+
+ deserialize = (reader: BinaryReader): { [key: string]: any } => {
+ let result: { [key: string]: any } = {};
+ if (this.elements.length === 1) {
+ if (this.elements[0].name === '__time_duration_micros__') {
+ return new TimeDuration(reader.readI64());
+ }
+
+ if (this.elements[0].name === '__timestamp_micros_since_unix_epoch__') {
+ return new Timestamp(reader.readI64());
+ }
+
+ if (this.elements[0].name === '__identity__') {
+ return new Identity(reader.readU256());
+ }
+
+ if (this.elements[0].name === '__connection_id__') {
+ return new ConnectionId(reader.readU128());
+ }
+ }
+
+ for (let element of this.elements) {
+ result[element.name] = element.algebraicType.deserialize(reader);
+ }
+ return result;
+ };
+}
+
+/* A map type from keys of type `keyType` to values of type `valueType`. */
+export class MapType {
+ keyType: AlgebraicType;
+ valueType: AlgebraicType;
+
+ constructor(keyType: AlgebraicType, valueType: AlgebraicType) {
+ this.keyType = keyType;
+ this.valueType = valueType;
+ }
+}
+
+type ArrayBaseType = AlgebraicType;
+type TypeRef = null;
+type None = null;
+export type EnumLabel = { label: string };
+
+type AnyType =
+ | ProductType
+ | SumType
+ | ArrayBaseType
+ | MapType
+ | EnumLabel
+ | TypeRef
+ | None;
+
+export type ComparablePrimitive = number | string | String | boolean | bigint;
+
+/**
+ * The SpacetimeDB Algebraic Type System (SATS) is a structural type system in
+ * which a nominal type system can be constructed.
+ *
+ * The type system unifies the concepts sum types, product types, and built-in
+ * primitive types into a single type system.
+ */
+export class AlgebraicType {
+ type!: Type;
+ type_?: AnyType;
+
+ #setter(type: Type, payload: AnyType | undefined) {
+ this.type_ = payload;
+ this.type = payload === undefined ? Type.None : type;
+ }
+
+ get product(): ProductType {
+ if (this.type !== Type.ProductType) {
+ throw 'product type was requested, but the type is not ProductType';
+ }
+ return this.type_ as ProductType;
+ }
+
+ set product(value: ProductType | undefined) {
+ this.#setter(Type.ProductType, value);
+ }
+
+ get sum(): SumType {
+ if (this.type !== Type.SumType) {
+ throw 'sum type was requested, but the type is not SumType';
+ }
+ return this.type_ as SumType;
+ }
+ set sum(value: SumType | undefined) {
+ this.#setter(Type.SumType, value);
+ }
+
+ get array(): ArrayBaseType {
+ if (this.type !== Type.ArrayType) {
+ throw 'array type was requested, but the type is not ArrayType';
+ }
+ return this.type_ as ArrayBaseType;
+ }
+ set array(value: ArrayBaseType | undefined) {
+ this.#setter(Type.ArrayType, value);
+ }
+
+ get map(): MapType {
+ if (this.type !== Type.MapType) {
+ throw 'map type was requested, but the type is not MapType';
+ }
+ return this.type_ as MapType;
+ }
+ set map(value: MapType | undefined) {
+ this.#setter(Type.MapType, value);
+ }
+
+ static #createType(type: Type, payload: AnyType | undefined): AlgebraicType {
+ let at = new AlgebraicType();
+ at.#setter(type, payload);
+ return at;
+ }
+
+ static createProductType(elements: ProductTypeElement[]): AlgebraicType {
+ return this.#createType(Type.ProductType, new ProductType(elements));
+ }
+
+ static createSumType(variants: SumTypeVariant[]): AlgebraicType {
+ return this.#createType(Type.SumType, new SumType(variants));
+ }
+
+ static createArrayType(elementType: AlgebraicType): AlgebraicType {
+ return this.#createType(Type.ArrayType, elementType);
+ }
+
+ static createMapType(key: AlgebraicType, val: AlgebraicType): AlgebraicType {
+ return this.#createType(Type.MapType, new MapType(key, val));
+ }
+
+ static createBoolType(): AlgebraicType {
+ return this.#createType(Type.Bool, null);
+ }
+ static createI8Type(): AlgebraicType {
+ return this.#createType(Type.I8, null);
+ }
+ static createU8Type(): AlgebraicType {
+ return this.#createType(Type.U8, null);
+ }
+ static createI16Type(): AlgebraicType {
+ return this.#createType(Type.I16, null);
+ }
+ static createU16Type(): AlgebraicType {
+ return this.#createType(Type.U16, null);
+ }
+ static createI32Type(): AlgebraicType {
+ return this.#createType(Type.I32, null);
+ }
+ static createU32Type(): AlgebraicType {
+ return this.#createType(Type.U32, null);
+ }
+ static createI64Type(): AlgebraicType {
+ return this.#createType(Type.I64, null);
+ }
+ static createU64Type(): AlgebraicType {
+ return this.#createType(Type.U64, null);
+ }
+ static createI128Type(): AlgebraicType {
+ return this.#createType(Type.I128, null);
+ }
+ static createU128Type(): AlgebraicType {
+ return this.#createType(Type.U128, null);
+ }
+ static createI256Type(): AlgebraicType {
+ return this.#createType(Type.I256, null);
+ }
+ static createU256Type(): AlgebraicType {
+ return this.#createType(Type.U256, null);
+ }
+ static createF32Type(): AlgebraicType {
+ return this.#createType(Type.F32, null);
+ }
+ static createF64Type(): AlgebraicType {
+ return this.#createType(Type.F64, null);
+ }
+ static createStringType(): AlgebraicType {
+ return this.#createType(Type.String, null);
+ }
+ static createBytesType(): AlgebraicType {
+ return this.createArrayType(this.createU8Type());
+ }
+ static createOptionType(innerType: AlgebraicType): AlgebraicType {
+ return this.createSumType([
+ new SumTypeVariant('some', innerType),
+ new SumTypeVariant('none', this.createProductType([])),
+ ]);
+ }
+ static createIdentityType(): AlgebraicType {
+ return this.createProductType([
+ new ProductTypeElement('__identity__', this.createU256Type()),
+ ]);
+ }
+
+ static createConnectionIdType(): AlgebraicType {
+ return this.createProductType([
+ new ProductTypeElement('__connection_id__', this.createU128Type()),
+ ]);
+ }
+
+ static createScheduleAtType(): AlgebraicType {
+ return ScheduleAt.getAlgebraicType();
+ }
+
+ static createTimestampType(): AlgebraicType {
+ return this.createProductType([
+ new ProductTypeElement(
+ '__timestamp_micros_since_unix_epoch__',
+ this.createI64Type()
+ ),
+ ]);
+ }
+
+ static createTimeDurationType(): AlgebraicType {
+ return this.createProductType([
+ new ProductTypeElement('__time_duration_micros__', this.createI64Type()),
+ ]);
+ }
+
+ isProductType(): boolean {
+ return this.type === Type.ProductType;
+ }
+
+ isSumType(): boolean {
+ return this.type === Type.SumType;
+ }
+
+ isArrayType(): boolean {
+ return this.type === Type.ArrayType;
+ }
+
+ isMapType(): boolean {
+ return this.type === Type.MapType;
+ }
+
+ #isBytes(): boolean {
+ return this.isArrayType() && this.array.type == Type.U8;
+ }
+
+ #isBytesNewtype(tag: string): boolean {
+ return (
+ this.isProductType() &&
+ this.product.elements.length === 1 &&
+ (this.product.elements[0].algebraicType.type == Type.U128 ||
+ this.product.elements[0].algebraicType.type == Type.U256) &&
+ this.product.elements[0].name === tag
+ );
+ }
+
+ #isI64Newtype(tag: string): boolean {
+ return (
+ this.isProductType() &&
+ this.product.elements.length === 1 &&
+ this.product.elements[0].algebraicType.type === Type.I64 &&
+ this.product.elements[0].name === tag
+ );
+ }
+
+ isIdentity(): boolean {
+ return this.#isBytesNewtype('__identity__');
+ }
+
+ isConnectionId(): boolean {
+ return this.#isBytesNewtype('__connection_id__');
+ }
+
+ isScheduleAt(): boolean {
+ return (
+ this.isSumType() &&
+ this.sum.variants.length === 2 &&
+ this.sum.variants[0].name === 'Interval' &&
+ this.sum.variants[0].algebraicType.type === Type.U64 &&
+ this.sum.variants[1].name === 'Time' &&
+ this.sum.variants[1].algebraicType.type === Type.U64
+ );
+ }
+
+ isTimestamp(): boolean {
+ return this.#isI64Newtype('__timestamp_micros_since_unix_epoch__');
+ }
+
+ isTimeDuration(): boolean {
+ return this.#isI64Newtype('__time_duration_micros__');
+ }
+
+ /**
+ * Convert a value of the algebraic type into something that can be used as a key in a map.
+ * There are no guarantees about being able to order it.
+ * This is only guaranteed to be comparable to other values of the same type.
+ * @param value A value of the algebraic type
+ * @returns Something that can be used as a key in a map.
+ */
+ intoMapKey(value: any): ComparablePrimitive {
+ switch (this.type) {
+ case Type.U8:
+ case Type.U16:
+ case Type.U32:
+ case Type.U64:
+ case Type.U128:
+ case Type.U256:
+ case Type.I8:
+ case Type.I16:
+ case Type.I64:
+ case Type.I128:
+ case Type.F32:
+ case Type.F64:
+ case Type.String:
+ case Type.Bool:
+ return value;
+ case Type.ProductType:
+ return this.product.intoMapKey(value);
+ default:
+ const writer = new BinaryWriter(10);
+ this.serialize(writer, value);
+ return writer.toBase64();
+ }
+ }
+
+ serialize(writer: BinaryWriter, value: any): void {
+ switch (this.type) {
+ case Type.ProductType:
+ this.product.serialize(writer, value);
+ break;
+ case Type.SumType:
+ this.sum.serialize(writer, value);
+ break;
+ case Type.ArrayType:
+ if (this.#isBytes()) {
+ writer.writeUInt8Array(value);
+ } else {
+ const elemType = this.array;
+ writer.writeU32(value.length);
+ for (let elem of value) {
+ elemType.serialize(writer, elem);
+ }
+ }
+ break;
+ case Type.MapType:
+ throw new Error('not implemented');
+ case Type.Bool:
+ writer.writeBool(value);
+ break;
+ case Type.I8:
+ writer.writeI8(value);
+ break;
+ case Type.U8:
+ writer.writeU8(value);
+ break;
+ case Type.I16:
+ writer.writeI16(value);
+ break;
+ case Type.U16:
+ writer.writeU16(value);
+ break;
+ case Type.I32:
+ writer.writeI32(value);
+ break;
+ case Type.U32:
+ writer.writeU32(value);
+ break;
+ case Type.I64:
+ writer.writeI64(value);
+ break;
+ case Type.U64:
+ writer.writeU64(value);
+ break;
+ case Type.I128:
+ writer.writeI128(value);
+ break;
+ case Type.U128:
+ writer.writeU128(value);
+ break;
+ case Type.I256:
+ writer.writeI256(value);
+ break;
+ case Type.U256:
+ writer.writeU256(value);
+ break;
+ case Type.F32:
+ writer.writeF32(value);
+ break;
+ case Type.F64:
+ writer.writeF64(value);
+ break;
+ case Type.String:
+ writer.writeString(value);
+ break;
+ default:
+ throw new Error(`not implemented, ${this.type}`);
+ }
+ }
+
+ deserialize(reader: BinaryReader): any {
+ switch (this.type) {
+ case Type.ProductType:
+ return this.product.deserialize(reader);
+ case Type.SumType:
+ return this.sum.deserialize(reader);
+ case Type.ArrayType:
+ if (this.#isBytes()) {
+ return reader.readUInt8Array();
+ } else {
+ const elemType = this.array;
+ const length = reader.readU32();
+ let result: any[] = [];
+ for (let i = 0; i < length; i++) {
+ result.push(elemType.deserialize(reader));
+ }
+ return result;
+ }
+ case Type.MapType:
+ // TODO: MapType is being removed
+ throw new Error('not implemented');
+ case Type.Bool:
+ return reader.readBool();
+ case Type.I8:
+ return reader.readI8();
+ case Type.U8:
+ return reader.readU8();
+ case Type.I16:
+ return reader.readI16();
+ case Type.U16:
+ return reader.readU16();
+ case Type.I32:
+ return reader.readI32();
+ case Type.U32:
+ return reader.readU32();
+ case Type.I64:
+ return reader.readI64();
+ case Type.U64:
+ return reader.readU64();
+ case Type.I128:
+ return reader.readI128();
+ case Type.U128:
+ return reader.readU128();
+ case Type.U256:
+ return reader.readU256();
+ case Type.F32:
+ return reader.readF32();
+ case Type.F64:
+ return reader.readF64();
+ case Type.String:
+ return reader.readString();
+ default:
+ throw new Error(`not implemented, ${this.type}`);
+ }
+ }
+}
+
+export namespace AlgebraicType {
+ export enum Type {
+ SumType = 'SumType',
+ ProductType = 'ProductType',
+ ArrayType = 'ArrayType',
+ MapType = 'MapType',
+ Bool = 'Bool',
+ I8 = 'I8',
+ U8 = 'U8',
+ I16 = 'I16',
+ U16 = 'U16',
+ I32 = 'I32',
+ U32 = 'U32',
+ I64 = 'I64',
+ U64 = 'U64',
+ I128 = 'I128',
+ U128 = 'U128',
+ I256 = 'I256',
+ U256 = 'U256',
+ F32 = 'F32',
+ F64 = 'F64',
+ /** UTF-8 encoded */
+ String = 'String',
+ None = 'None',
+ }
+}
+
+// No idea why but in order to have a local alias for both of these
+// need to be present
+type Type = AlgebraicType.Type;
+let Type: typeof AlgebraicType.Type = AlgebraicType.Type;
diff --git a/sdks/typescript/packages/sdk/src/algebraic_value.ts b/sdks/typescript/packages/sdk/src/algebraic_value.ts
new file mode 100644
index 00000000000..b91a1de5a51
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/algebraic_value.ts
@@ -0,0 +1,331 @@
+import { ConnectionId } from './connection_id';
+import { AlgebraicType, ProductType, SumType } from './algebraic_type';
+import BinaryReader from './binary_reader';
+import { Identity } from './identity';
+import { ScheduleAt } from './schedule_at';
+
+export interface ReducerArgsAdapter {
+ next: () => ValueAdapter;
+}
+
+export class BinaryReducerArgsAdapter {
+ adapter: BinaryAdapter;
+
+ constructor(adapter: BinaryAdapter) {
+ this.adapter = adapter;
+ }
+
+ next(): ValueAdapter {
+ return this.adapter;
+ }
+}
+
+/** Defines the interface for deserialize `AlgebraicValue`s*/
+export interface ValueAdapter {
+ readUInt8Array: () => Uint8Array;
+ readArray: (type: AlgebraicType) => AlgebraicValue[];
+ readMap: (keyType: AlgebraicType, valueType: AlgebraicType) => MapValue;
+ readString: () => string;
+ readSum: (type: SumType) => SumValue;
+ readProduct: (type: ProductType) => ProductValue;
+
+ readBool: () => boolean;
+ readByte: () => number;
+ readI8: () => number;
+ readU8: () => number;
+ readI16: () => number;
+ readU16: () => number;
+ readI32: () => number;
+ readU32: () => number;
+ readI64: () => bigint;
+ readU64: () => bigint;
+ readU128: () => bigint;
+ readI128: () => bigint;
+ readF32: () => number;
+ readF64: () => number;
+
+ callMethod(methodName: K): any;
+}
+
+export class BinaryAdapter implements ValueAdapter {
+ #reader: BinaryReader;
+
+ constructor(reader: BinaryReader) {
+ this.#reader = reader;
+ }
+
+ callMethod(methodName: K): any {
+ return (this[methodName] as Function)();
+ }
+
+ readUInt8Array(): Uint8Array {
+ return this.#reader.readUInt8Array();
+ }
+
+ readArray(type: AlgebraicType): AlgebraicValue[] {
+ const length = this.#reader.readU32();
+ let result: AlgebraicValue[] = [];
+ for (let i = 0; i < length; i++) {
+ result.push(AlgebraicValue.deserialize(type, this));
+ }
+
+ return result;
+ }
+
+ readMap(keyType: AlgebraicType, valueType: AlgebraicType): MapValue {
+ const mapLength = this.#reader.readU32();
+ let result: MapValue = new Map();
+ for (let i = 0; i < mapLength; i++) {
+ const key = AlgebraicValue.deserialize(keyType, this);
+ const value = AlgebraicValue.deserialize(valueType, this);
+ result.set(key, value);
+ }
+
+ return result;
+ }
+
+ readString(): string {
+ return this.#reader.readString();
+ }
+
+ readSum(type: SumType): SumValue {
+ let tag = this.#reader.readByte();
+ let sumValue = AlgebraicValue.deserialize(
+ type.variants[tag].algebraicType,
+ this
+ );
+ return new SumValue(tag, sumValue);
+ }
+
+ readProduct(type: ProductType): ProductValue {
+ let elements: AlgebraicValue[] = [];
+
+ for (let element of type.elements) {
+ elements.push(AlgebraicValue.deserialize(element.algebraicType, this));
+ }
+ return new ProductValue(elements);
+ }
+
+ readBool(): boolean {
+ return this.#reader.readBool();
+ }
+ readByte(): number {
+ return this.#reader.readByte();
+ }
+ readI8(): number {
+ return this.#reader.readI8();
+ }
+ readU8(): number {
+ return this.#reader.readU8();
+ }
+ readI16(): number {
+ return this.#reader.readI16();
+ }
+ readU16(): number {
+ return this.#reader.readU16();
+ }
+ readI32(): number {
+ return this.#reader.readI32();
+ }
+ readU32(): number {
+ return this.#reader.readU32();
+ }
+ readI64(): bigint {
+ return this.#reader.readI64();
+ }
+ readU64(): bigint {
+ return this.#reader.readU64();
+ }
+ readU128(): bigint {
+ return this.#reader.readU128();
+ }
+ readI128(): bigint {
+ return this.#reader.readI128();
+ }
+ readF32(): number {
+ return this.#reader.readF32();
+ }
+ readF64(): number {
+ return this.#reader.readF64();
+ }
+}
+
+/** A value of a sum type choosing a specific variant of the type. */
+export class SumValue {
+ /** A tag representing the choice of one variant of the sum type's variants. */
+ tag: number;
+ /**
+ * Given a variant `Var(Ty)` in a sum type `{ Var(Ty), ... }`,
+ * this provides the `value` for `Ty`.
+ */
+ value: AlgebraicValue;
+
+ constructor(tag: number, value: AlgebraicValue) {
+ this.tag = tag;
+ this.value = value;
+ }
+
+ static deserialize(type: SumType, adapter: ValueAdapter): SumValue {
+ return adapter.readSum(type);
+ }
+}
+
+/**
+ * A product value is made of a list of
+ * "elements" / "fields" / "factors" of other `AlgebraicValue`s.
+ *
+ * The type of product value is a [product type](`ProductType`).
+ */
+export class ProductValue {
+ elements: AlgebraicValue[];
+
+ constructor(elements: AlgebraicValue[]) {
+ this.elements = elements;
+ }
+
+ static deserialize(type: ProductType, adapter: ValueAdapter): ProductValue {
+ return adapter.readProduct(type);
+ }
+}
+
+export type MapValue = Map;
+
+type AnyValue =
+ | SumValue
+ | ProductValue
+ | AlgebraicValue[]
+ | Uint8Array
+ | MapValue
+ | string
+ | boolean
+ | number
+ | bigint;
+
+/** A value in SATS. */
+export class AlgebraicValue {
+ value: AnyValue;
+
+ constructor(value: AnyValue | undefined) {
+ if (value === undefined) {
+ // TODO: possibly get rid of it
+ throw 'value is undefined';
+ }
+ this.value = value;
+ }
+
+ callMethod(methodName: K): any {
+ return (this[methodName] as Function)();
+ }
+
+ static deserialize(
+ type: AlgebraicType,
+ adapter: ValueAdapter
+ ): AlgebraicValue {
+ switch (type.type) {
+ case AlgebraicType.Type.ProductType:
+ return new this(ProductValue.deserialize(type.product, adapter));
+ case AlgebraicType.Type.SumType:
+ return new this(SumValue.deserialize(type.sum, adapter));
+ case AlgebraicType.Type.ArrayType:
+ let elemType = type.array;
+ if (elemType.type === AlgebraicType.Type.U8) {
+ return new this(adapter.readUInt8Array());
+ } else {
+ return new this(adapter.readArray(elemType));
+ }
+ case AlgebraicType.Type.MapType:
+ let mapType = type.map;
+ return new this(adapter.readMap(mapType.keyType, mapType.valueType));
+ case AlgebraicType.Type.Bool:
+ return new this(adapter.readBool());
+ case AlgebraicType.Type.I8:
+ return new this(adapter.readI8());
+ case AlgebraicType.Type.U8:
+ return new this(adapter.readU8());
+ case AlgebraicType.Type.I16:
+ return new this(adapter.readI16());
+ case AlgebraicType.Type.U16:
+ return new this(adapter.readU16());
+ case AlgebraicType.Type.I32:
+ return new this(adapter.readI32());
+ case AlgebraicType.Type.U32:
+ return new this(adapter.readU32());
+ case AlgebraicType.Type.I64:
+ return new this(adapter.readI64());
+ case AlgebraicType.Type.U64:
+ return new this(adapter.readU64());
+ case AlgebraicType.Type.I128:
+ return new this(adapter.readI128());
+ case AlgebraicType.Type.U128:
+ return new this(adapter.readU128());
+ case AlgebraicType.Type.String:
+ return new this(adapter.readString());
+ default:
+ throw new Error(`not implemented, ${type.type}`);
+ }
+ }
+
+ // TODO: all of the following methods should actually check the type of `self.value`
+ // and throw if it does not match.
+
+ asProductValue(): ProductValue {
+ return this.value as ProductValue;
+ }
+
+ asField(index: number): AlgebraicValue {
+ return this.asProductValue().elements[index];
+ }
+
+ asSumValue(): SumValue {
+ return this.value as SumValue;
+ }
+
+ asArray(): AlgebraicValue[] {
+ return this.value as AlgebraicValue[];
+ }
+
+ asMap(): MapValue {
+ return this.value as MapValue;
+ }
+
+ asString(): string {
+ return this.value as string;
+ }
+
+ asBoolean(): boolean {
+ return this.value as boolean;
+ }
+
+ asNumber(): number {
+ return this.value as number;
+ }
+
+ asBytes(): Uint8Array {
+ return this.value as Uint8Array;
+ }
+
+ asBigInt(): bigint {
+ return this.value as bigint;
+ }
+
+ asIdentity(): Identity {
+ return new Identity(this.asField(0).asBigInt());
+ }
+
+ asConnectionId(): ConnectionId {
+ return new ConnectionId(this.asField(0).asBigInt());
+ }
+
+ asScheduleAt(): ScheduleAt {
+ return ScheduleAt.fromValue(this);
+ }
+}
+
+export interface ParseableType {
+ deserialize: (reader: BinaryReader) => T;
+}
+
+export function parseValue(ty: ParseableType, src: Uint8Array): T {
+ const reader = new BinaryReader(src);
+ return ty.deserialize(reader);
+}
diff --git a/sdks/typescript/packages/sdk/src/binary_reader.ts b/sdks/typescript/packages/sdk/src/binary_reader.ts
new file mode 100644
index 00000000000..f40f4101bca
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/binary_reader.ts
@@ -0,0 +1,165 @@
+export default class BinaryReader {
+ #buffer: DataView;
+ #offset: number = 0;
+
+ constructor(input: Uint8Array) {
+ this.#buffer = new DataView(input.buffer);
+ this.#offset = input.byteOffset;
+ }
+
+ get offset(): number {
+ return this.#offset;
+ }
+
+ readUInt8Array(): Uint8Array {
+ const length = this.readU32();
+ const value: Uint8Array = new Uint8Array(
+ this.#buffer.buffer,
+ this.#offset,
+ length
+ );
+ this.#offset += length;
+ return value;
+ }
+
+ readBool(): boolean {
+ const value = this.#buffer.getUint8(this.#offset);
+ this.#offset += 1;
+ return value !== 0;
+ }
+
+ readByte(): number {
+ const value = this.#buffer.getUint8(this.#offset);
+ this.#offset += 1;
+ return value;
+ }
+
+ readBytes(length: number): Uint8Array {
+ const value: DataView = new DataView(
+ this.#buffer.buffer,
+ this.#offset,
+ length
+ );
+ this.#offset += length;
+ return new Uint8Array(value.buffer);
+ }
+
+ readI8(): number {
+ const value = this.#buffer.getInt8(this.#offset);
+ this.#offset += 1;
+ return value;
+ }
+
+ readU8(): number {
+ const value = this.#buffer.getUint8(this.#offset);
+ this.#offset += 1;
+ return value;
+ }
+
+ readI16(): number {
+ const value = this.#buffer.getInt16(this.#offset, true);
+ this.#offset += 2;
+ return value;
+ }
+
+ readU16(): number {
+ const value = this.#buffer.getUint16(this.#offset, true);
+ this.#offset += 2;
+ return value;
+ }
+
+ readI32(): number {
+ const value = this.#buffer.getInt32(this.#offset, true);
+ this.#offset += 4;
+ return value;
+ }
+
+ readU32(): number {
+ const value = this.#buffer.getUint32(this.#offset, true);
+ this.#offset += 4;
+ return value;
+ }
+
+ readI64(): bigint {
+ const value = this.#buffer.getBigInt64(this.#offset, true);
+ this.#offset += 8;
+ return value;
+ }
+
+ readU64(): bigint {
+ const value = this.#buffer.getBigUint64(this.#offset, true);
+ this.#offset += 8;
+ return value;
+ }
+
+ readU128(): bigint {
+ const lowerPart = this.#buffer.getBigUint64(this.#offset, true);
+ const upperPart = this.#buffer.getBigUint64(this.#offset + 8, true);
+ this.#offset += 16;
+
+ return (upperPart << BigInt(64)) + lowerPart;
+ }
+
+ readI128(): bigint {
+ const lowerPart = this.#buffer.getBigUint64(this.#offset, true);
+ const upperPart = this.#buffer.getBigInt64(this.#offset + 8, true);
+ this.#offset += 16;
+
+ return (upperPart << BigInt(64)) + lowerPart;
+ }
+
+ readU256(): bigint {
+ const p0 = this.#buffer.getBigUint64(this.#offset, true);
+ const p1 = this.#buffer.getBigUint64(this.#offset + 8, true);
+ const p2 = this.#buffer.getBigUint64(this.#offset + 16, true);
+ const p3 = this.#buffer.getBigUint64(this.#offset + 24, true);
+ this.#offset += 32;
+
+ return (
+ (p3 << BigInt(3 * 64)) +
+ (p2 << BigInt(2 * 64)) +
+ (p1 << BigInt(1 * 64)) +
+ p0
+ );
+ }
+
+ readI256(): bigint {
+ const p0 = this.#buffer.getBigUint64(this.#offset, true);
+ const p1 = this.#buffer.getBigUint64(this.#offset + 8, true);
+ const p2 = this.#buffer.getBigUint64(this.#offset + 16, true);
+ const p3 = this.#buffer.getBigInt64(this.#offset + 24, true);
+ this.#offset += 32;
+
+ return (
+ (p3 << BigInt(3 * 64)) +
+ (p2 << BigInt(2 * 64)) +
+ (p1 << BigInt(1 * 64)) +
+ p0
+ );
+ }
+
+ readF32(): number {
+ const value = this.#buffer.getFloat32(this.#offset, true);
+ this.#offset += 4;
+ return value;
+ }
+
+ readF64(): number {
+ const value = this.#buffer.getFloat64(this.#offset, true);
+ this.#offset += 8;
+ return value;
+ }
+
+ readString(): string {
+ const length = this.readU32();
+ const uint8Array = new Uint8Array(
+ this.#buffer.buffer,
+ this.#offset,
+ length
+ );
+ const decoder = new TextDecoder('utf-8');
+ const value = decoder.decode(uint8Array);
+ this.#offset += length;
+ return value;
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/binary_writer.ts b/sdks/typescript/packages/sdk/src/binary_writer.ts
new file mode 100644
index 00000000000..8a55a3ffd44
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/binary_writer.ts
@@ -0,0 +1,168 @@
+import { fromByteArray } from 'base64-js';
+
+export default class BinaryWriter {
+ #buffer: Uint8Array;
+ #view: DataView;
+ #offset: number = 0;
+
+ constructor(size: number) {
+ this.#buffer = new Uint8Array(size);
+ this.#view = new DataView(this.#buffer.buffer);
+ }
+
+ #expandBuffer(additionalCapacity: number): void {
+ const minCapacity = this.#offset + additionalCapacity + 1;
+ if (minCapacity <= this.#buffer.length) return;
+ let newCapacity = this.#buffer.length * 2;
+ if (newCapacity < minCapacity) newCapacity = minCapacity;
+ const newBuffer = new Uint8Array(newCapacity);
+ newBuffer.set(this.#buffer);
+ this.#buffer = newBuffer;
+ this.#view = new DataView(this.#buffer.buffer);
+ }
+
+ toBase64(): string {
+ return fromByteArray(this.#buffer.subarray(0, this.#offset));
+ }
+
+ getBuffer(): Uint8Array {
+ return this.#buffer.slice(0, this.#offset);
+ }
+
+ writeUInt8Array(value: Uint8Array): void {
+ const length = value.length;
+
+ this.#expandBuffer(4 + length);
+
+ this.writeU32(length);
+ this.#buffer.set(value, this.#offset);
+ this.#offset += value.length;
+ }
+
+ writeBool(value: boolean): void {
+ this.#expandBuffer(1);
+ this.#view.setUint8(this.#offset, value ? 1 : 0);
+ this.#offset += 1;
+ }
+
+ writeByte(value: number): void {
+ this.#expandBuffer(1);
+ this.#view.setUint8(this.#offset, value);
+ this.#offset += 1;
+ }
+
+ writeI8(value: number): void {
+ this.#expandBuffer(1);
+ this.#view.setInt8(this.#offset, value);
+ this.#offset += 1;
+ }
+
+ writeU8(value: number): void {
+ this.#expandBuffer(1);
+ this.#view.setUint8(this.#offset, value);
+ this.#offset += 1;
+ }
+
+ writeI16(value: number): void {
+ this.#expandBuffer(2);
+ this.#view.setInt16(this.#offset, value, true);
+ this.#offset += 2;
+ }
+
+ writeU16(value: number): void {
+ this.#expandBuffer(2);
+ this.#view.setUint16(this.#offset, value, true);
+ this.#offset += 2;
+ }
+
+ writeI32(value: number): void {
+ this.#expandBuffer(4);
+ this.#view.setInt32(this.#offset, value, true);
+ this.#offset += 4;
+ }
+
+ writeU32(value: number): void {
+ this.#expandBuffer(4);
+ this.#view.setUint32(this.#offset, value, true);
+ this.#offset += 4;
+ }
+
+ writeI64(value: bigint): void {
+ this.#expandBuffer(8);
+ this.#view.setBigInt64(this.#offset, value, true);
+ this.#offset += 8;
+ }
+
+ writeU64(value: bigint): void {
+ this.#expandBuffer(8);
+ this.#view.setBigUint64(this.#offset, value, true);
+ this.#offset += 8;
+ }
+
+ writeU128(value: bigint): void {
+ this.#expandBuffer(16);
+ const lowerPart = value & BigInt('0xFFFFFFFFFFFFFFFF');
+ const upperPart = value >> BigInt(64);
+ this.#view.setBigUint64(this.#offset, lowerPart, true);
+ this.#view.setBigUint64(this.#offset + 8, upperPart, true);
+ this.#offset += 16;
+ }
+
+ writeI128(value: bigint): void {
+ this.#expandBuffer(16);
+ const lowerPart = value & BigInt('0xFFFFFFFFFFFFFFFF');
+ const upperPart = value >> BigInt(64);
+ this.#view.setBigInt64(this.#offset, lowerPart, true);
+ this.#view.setBigInt64(this.#offset + 8, upperPart, true);
+ this.#offset += 16;
+ }
+
+ writeU256(value: bigint): void {
+ this.#expandBuffer(32);
+ const low_64_mask = BigInt('0xFFFFFFFFFFFFFFFF');
+ const p0 = value & low_64_mask;
+ const p1 = (value >> BigInt(64 * 1)) & low_64_mask;
+ const p2 = (value >> BigInt(64 * 2)) & low_64_mask;
+ const p3 = value >> BigInt(64 * 3);
+ this.#view.setBigUint64(this.#offset + 8 * 0, p0, true);
+ this.#view.setBigUint64(this.#offset + 8 * 1, p1, true);
+ this.#view.setBigUint64(this.#offset + 8 * 2, p2, true);
+ this.#view.setBigUint64(this.#offset + 8 * 3, p3, true);
+ this.#offset += 32;
+ }
+
+ writeI256(value: bigint): void {
+ this.#expandBuffer(32);
+ const low_64_mask = BigInt('0xFFFFFFFFFFFFFFFF');
+ const p0 = value & low_64_mask;
+ const p1 = (value >> BigInt(64 * 1)) & low_64_mask;
+ const p2 = (value >> BigInt(64 * 2)) & low_64_mask;
+ const p3 = value >> BigInt(64 * 3);
+ this.#view.setBigUint64(this.#offset + 8 * 0, p0, true);
+ this.#view.setBigUint64(this.#offset + 8 * 1, p1, true);
+ this.#view.setBigUint64(this.#offset + 8 * 2, p2, true);
+ this.#view.setBigInt64(this.#offset + 8 * 3, p3, true);
+ this.#offset += 32;
+ }
+
+ writeF32(value: number): void {
+ this.#expandBuffer(4);
+ this.#view.setFloat32(this.#offset, value, true);
+ this.#offset += 4;
+ }
+
+ writeF64(value: number): void {
+ this.#expandBuffer(8);
+ this.#view.setFloat64(this.#offset, value, true);
+ this.#offset += 8;
+ }
+
+ writeString(value: string): void {
+ const encoder = new TextEncoder();
+ const encodedString = encoder.encode(value);
+ this.writeU32(encodedString.length);
+ this.#expandBuffer(encodedString.length);
+ this.#buffer.set(encodedString, this.#offset);
+ this.#offset += encodedString.length;
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/bsatn_row_list_type.ts b/sdks/typescript/packages/sdk/src/client_api/bsatn_row_list_type.ts
new file mode 100644
index 00000000000..800837cc879
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/bsatn_row_list_type.ts
@@ -0,0 +1,68 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { RowSizeHint as __RowSizeHint } from './row_size_hint_type';
+
+export type BsatnRowList = {
+ sizeHint: __RowSizeHint;
+ rowsData: Uint8Array;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace BsatnRowList {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'sizeHint',
+ __RowSizeHint.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement(
+ 'rowsData',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: BsatnRowList): void {
+ BsatnRowList.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): BsatnRowList {
+ return BsatnRowList.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/call_reducer_type.ts b/sdks/typescript/packages/sdk/src/client_api/call_reducer_type.ts
new file mode 100644
index 00000000000..d3bc69d59e3
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/call_reducer_type.ts
@@ -0,0 +1,67 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type CallReducer = {
+ reducer: string;
+ args: Uint8Array;
+ requestId: number;
+ flags: number;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace CallReducer {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('reducer', AlgebraicType.createStringType()),
+ new ProductTypeElement(
+ 'args',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('flags', AlgebraicType.createU8Type()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: CallReducer): void {
+ CallReducer.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): CallReducer {
+ return CallReducer.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/client_message_type.ts b/sdks/typescript/packages/sdk/src/client_api/client_message_type.ts
new file mode 100644
index 00000000000..024fb466d5a
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/client_message_type.ts
@@ -0,0 +1,146 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { CallReducer as __CallReducer } from './call_reducer_type';
+import { Subscribe as __Subscribe } from './subscribe_type';
+import { OneOffQuery as __OneOffQuery } from './one_off_query_type';
+import { SubscribeSingle as __SubscribeSingle } from './subscribe_single_type';
+import { SubscribeMulti as __SubscribeMulti } from './subscribe_multi_type';
+import { Unsubscribe as __Unsubscribe } from './unsubscribe_type';
+import { UnsubscribeMulti as __UnsubscribeMulti } from './unsubscribe_multi_type';
+
+// A namespace for generated variants and helper functions.
+export namespace ClientMessage {
+ // These are the generated variant types for each variant of the tagged union.
+ // One type is generated per variant and will be used in the `value` field of
+ // the tagged union.
+ export type CallReducer = { tag: 'CallReducer'; value: __CallReducer };
+ export type Subscribe = { tag: 'Subscribe'; value: __Subscribe };
+ export type OneOffQuery = { tag: 'OneOffQuery'; value: __OneOffQuery };
+ export type SubscribeSingle = {
+ tag: 'SubscribeSingle';
+ value: __SubscribeSingle;
+ };
+ export type SubscribeMulti = {
+ tag: 'SubscribeMulti';
+ value: __SubscribeMulti;
+ };
+ export type Unsubscribe = { tag: 'Unsubscribe'; value: __Unsubscribe };
+ export type UnsubscribeMulti = {
+ tag: 'UnsubscribeMulti';
+ value: __UnsubscribeMulti;
+ };
+
+ // Helper functions for constructing each variant of the tagged union.
+ // ```
+ // const foo = Foo.A(42);
+ // assert!(foo.tag === "A");
+ // assert!(foo.value === 42);
+ // ```
+ export const CallReducer = (value: __CallReducer): ClientMessage => ({
+ tag: 'CallReducer',
+ value,
+ });
+ export const Subscribe = (value: __Subscribe): ClientMessage => ({
+ tag: 'Subscribe',
+ value,
+ });
+ export const OneOffQuery = (value: __OneOffQuery): ClientMessage => ({
+ tag: 'OneOffQuery',
+ value,
+ });
+ export const SubscribeSingle = (value: __SubscribeSingle): ClientMessage => ({
+ tag: 'SubscribeSingle',
+ value,
+ });
+ export const SubscribeMulti = (value: __SubscribeMulti): ClientMessage => ({
+ tag: 'SubscribeMulti',
+ value,
+ });
+ export const Unsubscribe = (value: __Unsubscribe): ClientMessage => ({
+ tag: 'Unsubscribe',
+ value,
+ });
+ export const UnsubscribeMulti = (
+ value: __UnsubscribeMulti
+ ): ClientMessage => ({ tag: 'UnsubscribeMulti', value });
+
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant(
+ 'CallReducer',
+ __CallReducer.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant('Subscribe', __Subscribe.getTypeScriptAlgebraicType()),
+ new SumTypeVariant(
+ 'OneOffQuery',
+ __OneOffQuery.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'SubscribeSingle',
+ __SubscribeSingle.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'SubscribeMulti',
+ __SubscribeMulti.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'Unsubscribe',
+ __Unsubscribe.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'UnsubscribeMulti',
+ __UnsubscribeMulti.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: ClientMessage): void {
+ ClientMessage.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): ClientMessage {
+ return ClientMessage.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
+
+// The tagged union or sum type for the algebraic type `ClientMessage`.
+export type ClientMessage =
+ | ClientMessage.CallReducer
+ | ClientMessage.Subscribe
+ | ClientMessage.OneOffQuery
+ | ClientMessage.SubscribeSingle
+ | ClientMessage.SubscribeMulti
+ | ClientMessage.Unsubscribe
+ | ClientMessage.UnsubscribeMulti;
+
+export default ClientMessage;
diff --git a/sdks/typescript/packages/sdk/src/client_api/compressable_query_update_type.ts b/sdks/typescript/packages/sdk/src/client_api/compressable_query_update_type.ts
new file mode 100644
index 00000000000..a1727419171
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/compressable_query_update_type.ts
@@ -0,0 +1,102 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryUpdate as __QueryUpdate } from './query_update_type';
+
+// A namespace for generated variants and helper functions.
+export namespace CompressableQueryUpdate {
+ // These are the generated variant types for each variant of the tagged union.
+ // One type is generated per variant and will be used in the `value` field of
+ // the tagged union.
+ export type Uncompressed = { tag: 'Uncompressed'; value: __QueryUpdate };
+ export type Brotli = { tag: 'Brotli'; value: Uint8Array };
+ export type Gzip = { tag: 'Gzip'; value: Uint8Array };
+
+ // Helper functions for constructing each variant of the tagged union.
+ // ```
+ // const foo = Foo.A(42);
+ // assert!(foo.tag === "A");
+ // assert!(foo.value === 42);
+ // ```
+ export const Uncompressed = (
+ value: __QueryUpdate
+ ): CompressableQueryUpdate => ({ tag: 'Uncompressed', value });
+ export const Brotli = (value: Uint8Array): CompressableQueryUpdate => ({
+ tag: 'Brotli',
+ value,
+ });
+ export const Gzip = (value: Uint8Array): CompressableQueryUpdate => ({
+ tag: 'Gzip',
+ value,
+ });
+
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant(
+ 'Uncompressed',
+ __QueryUpdate.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'Brotli',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ new SumTypeVariant(
+ 'Gzip',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: CompressableQueryUpdate
+ ): void {
+ CompressableQueryUpdate.getTypeScriptAlgebraicType().serialize(
+ writer,
+ value
+ );
+ }
+
+ export function deserialize(reader: BinaryReader): CompressableQueryUpdate {
+ return CompressableQueryUpdate.getTypeScriptAlgebraicType().deserialize(
+ reader
+ );
+ }
+}
+
+// The tagged union or sum type for the algebraic type `CompressableQueryUpdate`.
+export type CompressableQueryUpdate =
+ | CompressableQueryUpdate.Uncompressed
+ | CompressableQueryUpdate.Brotli
+ | CompressableQueryUpdate.Gzip;
+
+export default CompressableQueryUpdate;
diff --git a/sdks/typescript/packages/sdk/src/client_api/database_update_type.ts b/sdks/typescript/packages/sdk/src/client_api/database_update_type.ts
new file mode 100644
index 00000000000..3a133f50b69
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/database_update_type.ts
@@ -0,0 +1,65 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { TableUpdate as __TableUpdate } from './table_update_type';
+
+export type DatabaseUpdate = {
+ tables: __TableUpdate[];
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace DatabaseUpdate {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'tables',
+ AlgebraicType.createArrayType(
+ __TableUpdate.getTypeScriptAlgebraicType()
+ )
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: DatabaseUpdate): void {
+ DatabaseUpdate.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): DatabaseUpdate {
+ return DatabaseUpdate.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/energy_quanta_type.ts b/sdks/typescript/packages/sdk/src/client_api/energy_quanta_type.ts
new file mode 100644
index 00000000000..686ca504d84
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/energy_quanta_type.ts
@@ -0,0 +1,58 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type EnergyQuanta = {
+ quanta: bigint;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace EnergyQuanta {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('quanta', AlgebraicType.createU128Type()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: EnergyQuanta): void {
+ EnergyQuanta.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): EnergyQuanta {
+ return EnergyQuanta.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/identity_token_type.ts b/sdks/typescript/packages/sdk/src/client_api/identity_token_type.ts
new file mode 100644
index 00000000000..11dd455da03
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/identity_token_type.ts
@@ -0,0 +1,65 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type IdentityToken = {
+ identity: Identity;
+ token: string;
+ connectionId: ConnectionId;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace IdentityToken {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('identity', AlgebraicType.createIdentityType()),
+ new ProductTypeElement('token', AlgebraicType.createStringType()),
+ new ProductTypeElement(
+ 'connectionId',
+ AlgebraicType.createConnectionIdType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: IdentityToken): void {
+ IdentityToken.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): IdentityToken {
+ return IdentityToken.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/index.ts b/sdks/typescript/packages/sdk/src/client_api/index.ts
new file mode 100644
index 00000000000..4274ff8c8e1
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/index.ts
@@ -0,0 +1,196 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+
+// Import and reexport all reducer arg types
+
+// Import and reexport all table handle types
+
+// Import and reexport all types
+import { BsatnRowList } from './bsatn_row_list_type.ts';
+export { BsatnRowList };
+import { CallReducer } from './call_reducer_type.ts';
+export { CallReducer };
+import { ClientMessage } from './client_message_type.ts';
+export { ClientMessage };
+import { CompressableQueryUpdate } from './compressable_query_update_type.ts';
+export { CompressableQueryUpdate };
+import { DatabaseUpdate } from './database_update_type.ts';
+export { DatabaseUpdate };
+import { EnergyQuanta } from './energy_quanta_type.ts';
+export { EnergyQuanta };
+import { IdentityToken } from './identity_token_type.ts';
+export { IdentityToken };
+import { InitialSubscription } from './initial_subscription_type.ts';
+export { InitialSubscription };
+import { OneOffQuery } from './one_off_query_type.ts';
+export { OneOffQuery };
+import { OneOffQueryResponse } from './one_off_query_response_type.ts';
+export { OneOffQueryResponse };
+import { OneOffTable } from './one_off_table_type.ts';
+export { OneOffTable };
+import { QueryId } from './query_id_type.ts';
+export { QueryId };
+import { QueryUpdate } from './query_update_type.ts';
+export { QueryUpdate };
+import { ReducerCallInfo } from './reducer_call_info_type.ts';
+export { ReducerCallInfo };
+import { RowSizeHint } from './row_size_hint_type.ts';
+export { RowSizeHint };
+import { ServerMessage } from './server_message_type.ts';
+export { ServerMessage };
+import { Subscribe } from './subscribe_type.ts';
+export { Subscribe };
+import { SubscribeApplied } from './subscribe_applied_type.ts';
+export { SubscribeApplied };
+import { SubscribeMulti } from './subscribe_multi_type.ts';
+export { SubscribeMulti };
+import { SubscribeMultiApplied } from './subscribe_multi_applied_type.ts';
+export { SubscribeMultiApplied };
+import { SubscribeRows } from './subscribe_rows_type.ts';
+export { SubscribeRows };
+import { SubscribeSingle } from './subscribe_single_type.ts';
+export { SubscribeSingle };
+import { SubscriptionError } from './subscription_error_type.ts';
+export { SubscriptionError };
+import { TableUpdate } from './table_update_type.ts';
+export { TableUpdate };
+import { TransactionUpdate } from './transaction_update_type.ts';
+export { TransactionUpdate };
+import { TransactionUpdateLight } from './transaction_update_light_type.ts';
+export { TransactionUpdateLight };
+import { Unsubscribe } from './unsubscribe_type.ts';
+export { Unsubscribe };
+import { UnsubscribeApplied } from './unsubscribe_applied_type.ts';
+export { UnsubscribeApplied };
+import { UnsubscribeMulti } from './unsubscribe_multi_type.ts';
+export { UnsubscribeMulti };
+import { UnsubscribeMultiApplied } from './unsubscribe_multi_applied_type.ts';
+export { UnsubscribeMultiApplied };
+import { UpdateStatus } from './update_status_type.ts';
+export { UpdateStatus };
+
+const REMOTE_MODULE = {
+ tables: {},
+ reducers: {},
+ // Constructors which are used by the DbConnectionImpl to
+ // extract type information from the generated RemoteModule.
+ //
+ // NOTE: This is not strictly necessary for `eventContextConstructor` because
+ // all we do is build a TypeScript object which we could have done inside the
+ // SDK, but if in the future we wanted to create a class this would be
+ // necessary because classes have methods, so we'll keep it.
+ eventContextConstructor: (imp: DbConnectionImpl, event: Event) => {
+ return {
+ ...(imp as DbConnection),
+ event,
+ };
+ },
+ dbViewConstructor: (imp: DbConnectionImpl) => {
+ return new RemoteTables(imp);
+ },
+ reducersConstructor: (
+ imp: DbConnectionImpl,
+ setReducerFlags: SetReducerFlags
+ ) => {
+ return new RemoteReducers(imp, setReducerFlags);
+ },
+ setReducerFlagsConstructor: () => {
+ return new SetReducerFlags();
+ },
+};
+
+// A type representing all the possible variants of a reducer.
+export type Reducer = never;
+
+export class RemoteReducers {
+ constructor(
+ private connection: DbConnectionImpl,
+ private setCallReducerFlags: SetReducerFlags
+ ) {}
+}
+
+export class SetReducerFlags {}
+
+export class RemoteTables {
+ constructor(private connection: DbConnectionImpl) {}
+}
+
+export class SubscriptionBuilder extends SubscriptionBuilderImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {}
+
+export class DbConnection extends DbConnectionImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {
+ static builder = (): DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ > => {
+ return new DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ >(REMOTE_MODULE, (imp: DbConnectionImpl) => imp as DbConnection);
+ };
+ subscriptionBuilder = (): SubscriptionBuilder => {
+ return new SubscriptionBuilder(this);
+ };
+}
+
+export type EventContext = EventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type ReducerEventContext = ReducerEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type SubscriptionEventContext = SubscriptionEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
+export type ErrorContext = ErrorContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
diff --git a/sdks/typescript/packages/sdk/src/client_api/initial_subscription_type.ts b/sdks/typescript/packages/sdk/src/client_api/initial_subscription_type.ts
new file mode 100644
index 00000000000..266ac6833e0
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/initial_subscription_type.ts
@@ -0,0 +1,73 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { DatabaseUpdate as __DatabaseUpdate } from './database_update_type';
+
+export type InitialSubscription = {
+ databaseUpdate: __DatabaseUpdate;
+ requestId: number;
+ totalHostExecutionDuration: TimeDuration;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace InitialSubscription {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'databaseUpdate',
+ __DatabaseUpdate.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'totalHostExecutionDuration',
+ AlgebraicType.createTimeDurationType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: InitialSubscription
+ ): void {
+ InitialSubscription.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): InitialSubscription {
+ return InitialSubscription.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/one_off_query_response_type.ts b/sdks/typescript/packages/sdk/src/client_api/one_off_query_response_type.ts
new file mode 100644
index 00000000000..885f9809361
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/one_off_query_response_type.ts
@@ -0,0 +1,83 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { OneOffTable as __OneOffTable } from './one_off_table_type';
+
+export type OneOffQueryResponse = {
+ messageId: Uint8Array;
+ error: string | undefined;
+ tables: __OneOffTable[];
+ totalHostExecutionDuration: TimeDuration;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace OneOffQueryResponse {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'messageId',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ new ProductTypeElement(
+ 'error',
+ AlgebraicType.createOptionType(AlgebraicType.createStringType())
+ ),
+ new ProductTypeElement(
+ 'tables',
+ AlgebraicType.createArrayType(
+ __OneOffTable.getTypeScriptAlgebraicType()
+ )
+ ),
+ new ProductTypeElement(
+ 'totalHostExecutionDuration',
+ AlgebraicType.createTimeDurationType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: OneOffQueryResponse
+ ): void {
+ OneOffQueryResponse.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): OneOffQueryResponse {
+ return OneOffQueryResponse.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/one_off_query_type.ts b/sdks/typescript/packages/sdk/src/client_api/one_off_query_type.ts
new file mode 100644
index 00000000000..8fb2fdc881a
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/one_off_query_type.ts
@@ -0,0 +1,63 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type OneOffQuery = {
+ messageId: Uint8Array;
+ queryString: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace OneOffQuery {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'messageId',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ new ProductTypeElement('queryString', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: OneOffQuery): void {
+ OneOffQuery.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): OneOffQuery {
+ return OneOffQuery.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/one_off_table_type.ts b/sdks/typescript/packages/sdk/src/client_api/one_off_table_type.ts
new file mode 100644
index 00000000000..32c8abb64ba
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/one_off_table_type.ts
@@ -0,0 +1,65 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { BsatnRowList as __BsatnRowList } from './bsatn_row_list_type';
+
+export type OneOffTable = {
+ tableName: string;
+ rows: __BsatnRowList;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace OneOffTable {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('tableName', AlgebraicType.createStringType()),
+ new ProductTypeElement(
+ 'rows',
+ __BsatnRowList.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: OneOffTable): void {
+ OneOffTable.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): OneOffTable {
+ return OneOffTable.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/query_id_type.ts b/sdks/typescript/packages/sdk/src/client_api/query_id_type.ts
new file mode 100644
index 00000000000..1f41bf5e3ad
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/query_id_type.ts
@@ -0,0 +1,58 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type QueryId = {
+ id: number;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace QueryId {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('id', AlgebraicType.createU32Type()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: QueryId): void {
+ QueryId.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): QueryId {
+ return QueryId.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/query_update_type.ts b/sdks/typescript/packages/sdk/src/client_api/query_update_type.ts
new file mode 100644
index 00000000000..10e0147f047
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/query_update_type.ts
@@ -0,0 +1,68 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { BsatnRowList as __BsatnRowList } from './bsatn_row_list_type';
+
+export type QueryUpdate = {
+ deletes: __BsatnRowList;
+ inserts: __BsatnRowList;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace QueryUpdate {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'deletes',
+ __BsatnRowList.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement(
+ 'inserts',
+ __BsatnRowList.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: QueryUpdate): void {
+ QueryUpdate.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): QueryUpdate {
+ return QueryUpdate.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/reducer_call_info_type.ts b/sdks/typescript/packages/sdk/src/client_api/reducer_call_info_type.ts
new file mode 100644
index 00000000000..337fb073e23
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/reducer_call_info_type.ts
@@ -0,0 +1,70 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type ReducerCallInfo = {
+ reducerName: string;
+ reducerId: number;
+ args: Uint8Array;
+ requestId: number;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace ReducerCallInfo {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('reducerName', AlgebraicType.createStringType()),
+ new ProductTypeElement('reducerId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'args',
+ AlgebraicType.createArrayType(AlgebraicType.createU8Type())
+ ),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: ReducerCallInfo
+ ): void {
+ ReducerCallInfo.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): ReducerCallInfo {
+ return ReducerCallInfo.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/row_size_hint_type.ts b/sdks/typescript/packages/sdk/src/client_api/row_size_hint_type.ts
new file mode 100644
index 00000000000..7f6830bcc51
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/row_size_hint_type.ts
@@ -0,0 +1,78 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+// A namespace for generated variants and helper functions.
+export namespace RowSizeHint {
+ // These are the generated variant types for each variant of the tagged union.
+ // One type is generated per variant and will be used in the `value` field of
+ // the tagged union.
+ export type FixedSize = { tag: 'FixedSize'; value: number };
+ export type RowOffsets = { tag: 'RowOffsets'; value: bigint[] };
+
+ // Helper functions for constructing each variant of the tagged union.
+ // ```
+ // const foo = Foo.A(42);
+ // assert!(foo.tag === "A");
+ // assert!(foo.value === 42);
+ // ```
+ export const FixedSize = (value: number): RowSizeHint => ({
+ tag: 'FixedSize',
+ value,
+ });
+ export const RowOffsets = (value: bigint[]): RowSizeHint => ({
+ tag: 'RowOffsets',
+ value,
+ });
+
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant('FixedSize', AlgebraicType.createU16Type()),
+ new SumTypeVariant(
+ 'RowOffsets',
+ AlgebraicType.createArrayType(AlgebraicType.createU64Type())
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: RowSizeHint): void {
+ RowSizeHint.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): RowSizeHint {
+ return RowSizeHint.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
+
+// The tagged union or sum type for the algebraic type `RowSizeHint`.
+export type RowSizeHint = RowSizeHint.FixedSize | RowSizeHint.RowOffsets;
+
+export default RowSizeHint;
diff --git a/sdks/typescript/packages/sdk/src/client_api/server_message_type.ts b/sdks/typescript/packages/sdk/src/client_api/server_message_type.ts
new file mode 100644
index 00000000000..c88b76f43f1
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/server_message_type.ts
@@ -0,0 +1,192 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { InitialSubscription as __InitialSubscription } from './initial_subscription_type';
+import { TransactionUpdate as __TransactionUpdate } from './transaction_update_type';
+import { TransactionUpdateLight as __TransactionUpdateLight } from './transaction_update_light_type';
+import { IdentityToken as __IdentityToken } from './identity_token_type';
+import { OneOffQueryResponse as __OneOffQueryResponse } from './one_off_query_response_type';
+import { SubscribeApplied as __SubscribeApplied } from './subscribe_applied_type';
+import { UnsubscribeApplied as __UnsubscribeApplied } from './unsubscribe_applied_type';
+import { SubscriptionError as __SubscriptionError } from './subscription_error_type';
+import { SubscribeMultiApplied as __SubscribeMultiApplied } from './subscribe_multi_applied_type';
+import { UnsubscribeMultiApplied as __UnsubscribeMultiApplied } from './unsubscribe_multi_applied_type';
+
+// A namespace for generated variants and helper functions.
+export namespace ServerMessage {
+ // These are the generated variant types for each variant of the tagged union.
+ // One type is generated per variant and will be used in the `value` field of
+ // the tagged union.
+ export type InitialSubscription = {
+ tag: 'InitialSubscription';
+ value: __InitialSubscription;
+ };
+ export type TransactionUpdate = {
+ tag: 'TransactionUpdate';
+ value: __TransactionUpdate;
+ };
+ export type TransactionUpdateLight = {
+ tag: 'TransactionUpdateLight';
+ value: __TransactionUpdateLight;
+ };
+ export type IdentityToken = { tag: 'IdentityToken'; value: __IdentityToken };
+ export type OneOffQueryResponse = {
+ tag: 'OneOffQueryResponse';
+ value: __OneOffQueryResponse;
+ };
+ export type SubscribeApplied = {
+ tag: 'SubscribeApplied';
+ value: __SubscribeApplied;
+ };
+ export type UnsubscribeApplied = {
+ tag: 'UnsubscribeApplied';
+ value: __UnsubscribeApplied;
+ };
+ export type SubscriptionError = {
+ tag: 'SubscriptionError';
+ value: __SubscriptionError;
+ };
+ export type SubscribeMultiApplied = {
+ tag: 'SubscribeMultiApplied';
+ value: __SubscribeMultiApplied;
+ };
+ export type UnsubscribeMultiApplied = {
+ tag: 'UnsubscribeMultiApplied';
+ value: __UnsubscribeMultiApplied;
+ };
+
+ // Helper functions for constructing each variant of the tagged union.
+ // ```
+ // const foo = Foo.A(42);
+ // assert!(foo.tag === "A");
+ // assert!(foo.value === 42);
+ // ```
+ export const InitialSubscription = (
+ value: __InitialSubscription
+ ): ServerMessage => ({ tag: 'InitialSubscription', value });
+ export const TransactionUpdate = (
+ value: __TransactionUpdate
+ ): ServerMessage => ({ tag: 'TransactionUpdate', value });
+ export const TransactionUpdateLight = (
+ value: __TransactionUpdateLight
+ ): ServerMessage => ({ tag: 'TransactionUpdateLight', value });
+ export const IdentityToken = (value: __IdentityToken): ServerMessage => ({
+ tag: 'IdentityToken',
+ value,
+ });
+ export const OneOffQueryResponse = (
+ value: __OneOffQueryResponse
+ ): ServerMessage => ({ tag: 'OneOffQueryResponse', value });
+ export const SubscribeApplied = (
+ value: __SubscribeApplied
+ ): ServerMessage => ({ tag: 'SubscribeApplied', value });
+ export const UnsubscribeApplied = (
+ value: __UnsubscribeApplied
+ ): ServerMessage => ({ tag: 'UnsubscribeApplied', value });
+ export const SubscriptionError = (
+ value: __SubscriptionError
+ ): ServerMessage => ({ tag: 'SubscriptionError', value });
+ export const SubscribeMultiApplied = (
+ value: __SubscribeMultiApplied
+ ): ServerMessage => ({ tag: 'SubscribeMultiApplied', value });
+ export const UnsubscribeMultiApplied = (
+ value: __UnsubscribeMultiApplied
+ ): ServerMessage => ({ tag: 'UnsubscribeMultiApplied', value });
+
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant(
+ 'InitialSubscription',
+ __InitialSubscription.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'TransactionUpdate',
+ __TransactionUpdate.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'TransactionUpdateLight',
+ __TransactionUpdateLight.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'IdentityToken',
+ __IdentityToken.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'OneOffQueryResponse',
+ __OneOffQueryResponse.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'SubscribeApplied',
+ __SubscribeApplied.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'UnsubscribeApplied',
+ __UnsubscribeApplied.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'SubscriptionError',
+ __SubscriptionError.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'SubscribeMultiApplied',
+ __SubscribeMultiApplied.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant(
+ 'UnsubscribeMultiApplied',
+ __UnsubscribeMultiApplied.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: ServerMessage): void {
+ ServerMessage.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): ServerMessage {
+ return ServerMessage.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
+
+// The tagged union or sum type for the algebraic type `ServerMessage`.
+export type ServerMessage =
+ | ServerMessage.InitialSubscription
+ | ServerMessage.TransactionUpdate
+ | ServerMessage.TransactionUpdateLight
+ | ServerMessage.IdentityToken
+ | ServerMessage.OneOffQueryResponse
+ | ServerMessage.SubscribeApplied
+ | ServerMessage.UnsubscribeApplied
+ | ServerMessage.SubscriptionError
+ | ServerMessage.SubscribeMultiApplied
+ | ServerMessage.UnsubscribeMultiApplied;
+
+export default ServerMessage;
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_applied_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_applied_type.ts
new file mode 100644
index 00000000000..fbd3dfb9c55
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_applied_type.ts
@@ -0,0 +1,76 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+import { SubscribeRows as __SubscribeRows } from './subscribe_rows_type';
+
+export type SubscribeApplied = {
+ requestId: number;
+ totalHostExecutionDurationMicros: bigint;
+ queryId: __QueryId;
+ rows: __SubscribeRows;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscribeApplied {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'totalHostExecutionDurationMicros',
+ AlgebraicType.createU64Type()
+ ),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ new ProductTypeElement(
+ 'rows',
+ __SubscribeRows.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: SubscribeApplied
+ ): void {
+ SubscribeApplied.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscribeApplied {
+ return SubscribeApplied.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_applied_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_applied_type.ts
new file mode 100644
index 00000000000..54b78817959
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_applied_type.ts
@@ -0,0 +1,78 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+import { DatabaseUpdate as __DatabaseUpdate } from './database_update_type';
+
+export type SubscribeMultiApplied = {
+ requestId: number;
+ totalHostExecutionDurationMicros: bigint;
+ queryId: __QueryId;
+ update: __DatabaseUpdate;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscribeMultiApplied {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'totalHostExecutionDurationMicros',
+ AlgebraicType.createU64Type()
+ ),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ new ProductTypeElement(
+ 'update',
+ __DatabaseUpdate.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: SubscribeMultiApplied
+ ): void {
+ SubscribeMultiApplied.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscribeMultiApplied {
+ return SubscribeMultiApplied.getTypeScriptAlgebraicType().deserialize(
+ reader
+ );
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_type.ts
new file mode 100644
index 00000000000..349f9f1a059
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_multi_type.ts
@@ -0,0 +1,67 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+
+export type SubscribeMulti = {
+ queryStrings: string[];
+ requestId: number;
+ queryId: __QueryId;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscribeMulti {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'queryStrings',
+ AlgebraicType.createArrayType(AlgebraicType.createStringType())
+ ),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: SubscribeMulti): void {
+ SubscribeMulti.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscribeMulti {
+ return SubscribeMulti.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_rows_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_rows_type.ts
new file mode 100644
index 00000000000..b1c6d6295ea
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_rows_type.ts
@@ -0,0 +1,67 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { TableUpdate as __TableUpdate } from './table_update_type';
+
+export type SubscribeRows = {
+ tableId: number;
+ tableName: string;
+ tableRows: __TableUpdate;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscribeRows {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('tableId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('tableName', AlgebraicType.createStringType()),
+ new ProductTypeElement(
+ 'tableRows',
+ __TableUpdate.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: SubscribeRows): void {
+ SubscribeRows.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscribeRows {
+ return SubscribeRows.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_single_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_single_type.ts
new file mode 100644
index 00000000000..2f564678c7e
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_single_type.ts
@@ -0,0 +1,67 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+
+export type SubscribeSingle = {
+ query: string;
+ requestId: number;
+ queryId: __QueryId;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscribeSingle {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('query', AlgebraicType.createStringType()),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: SubscribeSingle
+ ): void {
+ SubscribeSingle.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscribeSingle {
+ return SubscribeSingle.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscribe_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscribe_type.ts
new file mode 100644
index 00000000000..cb6e4edb6ad
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscribe_type.ts
@@ -0,0 +1,63 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type Subscribe = {
+ queryStrings: string[];
+ requestId: number;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace Subscribe {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'queryStrings',
+ AlgebraicType.createArrayType(AlgebraicType.createStringType())
+ ),
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: Subscribe): void {
+ Subscribe.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): Subscribe {
+ return Subscribe.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/subscription_error_type.ts b/sdks/typescript/packages/sdk/src/client_api/subscription_error_type.ts
new file mode 100644
index 00000000000..d8e0a792ee2
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/subscription_error_type.ts
@@ -0,0 +1,81 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+export type SubscriptionError = {
+ totalHostExecutionDurationMicros: bigint;
+ requestId: number | undefined;
+ queryId: number | undefined;
+ tableId: number | undefined;
+ error: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace SubscriptionError {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'totalHostExecutionDurationMicros',
+ AlgebraicType.createU64Type()
+ ),
+ new ProductTypeElement(
+ 'requestId',
+ AlgebraicType.createOptionType(AlgebraicType.createU32Type())
+ ),
+ new ProductTypeElement(
+ 'queryId',
+ AlgebraicType.createOptionType(AlgebraicType.createU32Type())
+ ),
+ new ProductTypeElement(
+ 'tableId',
+ AlgebraicType.createOptionType(AlgebraicType.createU32Type())
+ ),
+ new ProductTypeElement('error', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: SubscriptionError
+ ): void {
+ SubscriptionError.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): SubscriptionError {
+ return SubscriptionError.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/table_update_type.ts b/sdks/typescript/packages/sdk/src/client_api/table_update_type.ts
new file mode 100644
index 00000000000..cc4999cb9fc
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/table_update_type.ts
@@ -0,0 +1,71 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { CompressableQueryUpdate as __CompressableQueryUpdate } from './compressable_query_update_type';
+
+export type TableUpdate = {
+ tableId: number;
+ tableName: string;
+ numRows: bigint;
+ updates: __CompressableQueryUpdate[];
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace TableUpdate {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('tableId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('tableName', AlgebraicType.createStringType()),
+ new ProductTypeElement('numRows', AlgebraicType.createU64Type()),
+ new ProductTypeElement(
+ 'updates',
+ AlgebraicType.createArrayType(
+ __CompressableQueryUpdate.getTypeScriptAlgebraicType()
+ )
+ ),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: TableUpdate): void {
+ TableUpdate.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): TableUpdate {
+ return TableUpdate.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/transaction_update_light_type.ts b/sdks/typescript/packages/sdk/src/client_api/transaction_update_light_type.ts
new file mode 100644
index 00000000000..567fe4b332a
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/transaction_update_light_type.ts
@@ -0,0 +1,73 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { DatabaseUpdate as __DatabaseUpdate } from './database_update_type';
+
+export type TransactionUpdateLight = {
+ requestId: number;
+ update: __DatabaseUpdate;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace TransactionUpdateLight {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'update',
+ __DatabaseUpdate.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: TransactionUpdateLight
+ ): void {
+ TransactionUpdateLight.getTypeScriptAlgebraicType().serialize(
+ writer,
+ value
+ );
+ }
+
+ export function deserialize(reader: BinaryReader): TransactionUpdateLight {
+ return TransactionUpdateLight.getTypeScriptAlgebraicType().deserialize(
+ reader
+ );
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/transaction_update_type.ts b/sdks/typescript/packages/sdk/src/client_api/transaction_update_type.ts
new file mode 100644
index 00000000000..6391ba98d6d
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/transaction_update_type.ts
@@ -0,0 +1,95 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { UpdateStatus as __UpdateStatus } from './update_status_type';
+import { ReducerCallInfo as __ReducerCallInfo } from './reducer_call_info_type';
+import { EnergyQuanta as __EnergyQuanta } from './energy_quanta_type';
+
+export type TransactionUpdate = {
+ status: __UpdateStatus;
+ timestamp: Timestamp;
+ callerIdentity: Identity;
+ callerConnectionId: ConnectionId;
+ reducerCall: __ReducerCallInfo;
+ energyQuantaUsed: __EnergyQuanta;
+ totalHostExecutionDuration: TimeDuration;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace TransactionUpdate {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement(
+ 'status',
+ __UpdateStatus.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement('timestamp', AlgebraicType.createTimestampType()),
+ new ProductTypeElement(
+ 'callerIdentity',
+ AlgebraicType.createIdentityType()
+ ),
+ new ProductTypeElement(
+ 'callerConnectionId',
+ AlgebraicType.createConnectionIdType()
+ ),
+ new ProductTypeElement(
+ 'reducerCall',
+ __ReducerCallInfo.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement(
+ 'energyQuantaUsed',
+ __EnergyQuanta.getTypeScriptAlgebraicType()
+ ),
+ new ProductTypeElement(
+ 'totalHostExecutionDuration',
+ AlgebraicType.createTimeDurationType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: TransactionUpdate
+ ): void {
+ TransactionUpdate.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): TransactionUpdate {
+ return TransactionUpdate.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/unsubscribe_applied_type.ts b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_applied_type.ts
new file mode 100644
index 00000000000..ab63a797d5a
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_applied_type.ts
@@ -0,0 +1,76 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+import { SubscribeRows as __SubscribeRows } from './subscribe_rows_type';
+
+export type UnsubscribeApplied = {
+ requestId: number;
+ totalHostExecutionDurationMicros: bigint;
+ queryId: __QueryId;
+ rows: __SubscribeRows;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace UnsubscribeApplied {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'totalHostExecutionDurationMicros',
+ AlgebraicType.createU64Type()
+ ),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ new ProductTypeElement(
+ 'rows',
+ __SubscribeRows.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: UnsubscribeApplied
+ ): void {
+ UnsubscribeApplied.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): UnsubscribeApplied {
+ return UnsubscribeApplied.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_applied_type.ts b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_applied_type.ts
new file mode 100644
index 00000000000..92126057962
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_applied_type.ts
@@ -0,0 +1,81 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+import { DatabaseUpdate as __DatabaseUpdate } from './database_update_type';
+
+export type UnsubscribeMultiApplied = {
+ requestId: number;
+ totalHostExecutionDurationMicros: bigint;
+ queryId: __QueryId;
+ update: __DatabaseUpdate;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace UnsubscribeMultiApplied {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement(
+ 'totalHostExecutionDurationMicros',
+ AlgebraicType.createU64Type()
+ ),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ new ProductTypeElement(
+ 'update',
+ __DatabaseUpdate.getTypeScriptAlgebraicType()
+ ),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: UnsubscribeMultiApplied
+ ): void {
+ UnsubscribeMultiApplied.getTypeScriptAlgebraicType().serialize(
+ writer,
+ value
+ );
+ }
+
+ export function deserialize(reader: BinaryReader): UnsubscribeMultiApplied {
+ return UnsubscribeMultiApplied.getTypeScriptAlgebraicType().deserialize(
+ reader
+ );
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_type.ts b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_type.ts
new file mode 100644
index 00000000000..32d02ba14c0
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_multi_type.ts
@@ -0,0 +1,65 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+
+export type UnsubscribeMulti = {
+ requestId: number;
+ queryId: __QueryId;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace UnsubscribeMulti {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: UnsubscribeMulti
+ ): void {
+ UnsubscribeMulti.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): UnsubscribeMulti {
+ return UnsubscribeMulti.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/unsubscribe_type.ts b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_type.ts
new file mode 100644
index 00000000000..368f68c4d87
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/unsubscribe_type.ts
@@ -0,0 +1,62 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { QueryId as __QueryId } from './query_id_type';
+
+export type Unsubscribe = {
+ requestId: number;
+ queryId: __QueryId;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace Unsubscribe {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('requestId', AlgebraicType.createU32Type()),
+ new ProductTypeElement('queryId', __QueryId.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: Unsubscribe): void {
+ Unsubscribe.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): Unsubscribe {
+ return Unsubscribe.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/client_api/update_status_type.ts b/sdks/typescript/packages/sdk/src/client_api/update_status_type.ts
new file mode 100644
index 00000000000..68aa8865f54
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_api/update_status_type.ts
@@ -0,0 +1,86 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ CallReducerFlags,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ DbContext,
+ ErrorContextInterface,
+ Event,
+ EventContextInterface,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ ReducerEventContextInterface,
+ SubscriptionBuilderImpl,
+ SubscriptionEventContextInterface,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+} from '../index';
+import { DatabaseUpdate as __DatabaseUpdate } from './database_update_type';
+
+// A namespace for generated variants and helper functions.
+export namespace UpdateStatus {
+ // These are the generated variant types for each variant of the tagged union.
+ // One type is generated per variant and will be used in the `value` field of
+ // the tagged union.
+ export type Committed = { tag: 'Committed'; value: __DatabaseUpdate };
+ export type Failed = { tag: 'Failed'; value: string };
+ export type OutOfEnergy = { tag: 'OutOfEnergy' };
+
+ // Helper functions for constructing each variant of the tagged union.
+ // ```
+ // const foo = Foo.A(42);
+ // assert!(foo.tag === "A");
+ // assert!(foo.value === 42);
+ // ```
+ export const Committed = (value: __DatabaseUpdate): UpdateStatus => ({
+ tag: 'Committed',
+ value,
+ });
+ export const Failed = (value: string): UpdateStatus => ({
+ tag: 'Failed',
+ value,
+ });
+ export const OutOfEnergy = { tag: 'OutOfEnergy' };
+
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant(
+ 'Committed',
+ __DatabaseUpdate.getTypeScriptAlgebraicType()
+ ),
+ new SumTypeVariant('Failed', AlgebraicType.createStringType()),
+ new SumTypeVariant('OutOfEnergy', AlgebraicType.createProductType([])),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: UpdateStatus): void {
+ UpdateStatus.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): UpdateStatus {
+ return UpdateStatus.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
+
+// The tagged union or sum type for the algebraic type `UpdateStatus`.
+export type UpdateStatus =
+ | UpdateStatus.Committed
+ | UpdateStatus.Failed
+ | UpdateStatus.OutOfEnergy;
+
+export default UpdateStatus;
diff --git a/sdks/typescript/packages/sdk/src/client_cache.ts b/sdks/typescript/packages/sdk/src/client_cache.ts
new file mode 100644
index 00000000000..94ac8749422
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/client_cache.ts
@@ -0,0 +1,45 @@
+import type { TableRuntimeTypeInfo } from './spacetime_module.ts';
+import { TableCache } from './table_cache.ts';
+
+export class ClientCache {
+ /**
+ * The tables in the database.
+ */
+ tables: Map;
+
+ constructor() {
+ this.tables = new Map();
+ }
+
+ /**
+ * Returns the table with the given name.
+ * @param name The name of the table.
+ * @returns The table
+ */
+ getTable(name: string): TableCache {
+ const table = this.tables.get(name);
+
+ // ! This should not happen as the table should be available but an exception is thrown just in case.
+ if (!table) {
+ console.error(
+ 'The table has not been registered for this client. Please register the table before using it. If you have registered global tables using the SpacetimeDBClient.registerTables() or `registerTable()` method, please make sure that is executed first!'
+ );
+ throw new Error(`Table ${name} does not exist`);
+ }
+
+ return table;
+ }
+
+ getOrCreateTable(
+ tableTypeInfo: TableRuntimeTypeInfo
+ ): TableCache {
+ let table: TableCache;
+ if (!this.tables.has(tableTypeInfo.tableName)) {
+ table = new TableCache(tableTypeInfo);
+ this.tables.set(tableTypeInfo.tableName, table);
+ } else {
+ table = this.tables.get(tableTypeInfo.tableName)!;
+ }
+ return table;
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/connection_id.ts b/sdks/typescript/packages/sdk/src/connection_id.ts
new file mode 100644
index 00000000000..c8d95016c18
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/connection_id.ts
@@ -0,0 +1,79 @@
+import { hexStringToU128, u128ToHexString, u128ToUint8Array } from './utils';
+
+/**
+ * A unique identifier for a client connected to a database.
+ */
+export class ConnectionId {
+ data: bigint;
+
+ get __connection_id__(): bigint {
+ return this.data;
+ }
+
+ /**
+ * Creates a new `ConnectionId`.
+ */
+ constructor(data: bigint) {
+ this.data = data;
+ }
+
+ isZero(): boolean {
+ return this.data === BigInt(0);
+ }
+
+ static nullIfZero(addr: ConnectionId): ConnectionId | null {
+ if (addr.isZero()) {
+ return null;
+ } else {
+ return addr;
+ }
+ }
+
+ static random(): ConnectionId {
+ function randomU8(): number {
+ return Math.floor(Math.random() * 0xff);
+ }
+ let result = BigInt(0);
+ for (let i = 0; i < 16; i++) {
+ result = (result << BigInt(8)) | BigInt(randomU8());
+ }
+ return new ConnectionId(result);
+ }
+
+ /**
+ * Compare two connection IDs for equality.
+ */
+ isEqual(other: ConnectionId): boolean {
+ return this.data == other.data;
+ }
+
+ /**
+ * Print the connection ID as a hexadecimal string.
+ */
+ toHexString(): string {
+ return u128ToHexString(this.data);
+ }
+
+ /**
+ * Convert the connection ID to a Uint8Array.
+ */
+ toUint8Array(): Uint8Array {
+ return u128ToUint8Array(this.data);
+ }
+
+ /**
+ * Parse a connection ID from a hexadecimal string.
+ */
+ static fromString(str: string): ConnectionId {
+ return new ConnectionId(hexStringToU128(str));
+ }
+
+ static fromStringOrNull(str: string): ConnectionId | null {
+ let addr = ConnectionId.fromString(str);
+ if (addr.isZero()) {
+ return null;
+ } else {
+ return addr;
+ }
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/db_connection_builder.ts b/sdks/typescript/packages/sdk/src/db_connection_builder.ts
new file mode 100644
index 00000000000..ed62b93eff0
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/db_connection_builder.ts
@@ -0,0 +1,236 @@
+import { DbConnectionImpl, type ConnectionEvent } from './db_connection_impl';
+import { EventEmitter } from './event_emitter';
+import type { Identity } from './identity';
+import type RemoteModule from './spacetime_module';
+import { ensureMinimumVersionOrThrow } from './version';
+import { WebsocketDecompressAdapter } from './websocket_decompress_adapter';
+
+/**
+ * The database client connection to a SpacetimeDB server.
+ */
+export class DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext,
+> {
+ #uri?: URL;
+ #nameOrAddress?: string;
+ #identity?: Identity;
+ #token?: string;
+ #emitter: EventEmitter = new EventEmitter();
+ #compression: 'gzip' | 'none' = 'gzip';
+ #lightMode: boolean = false;
+ #createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn;
+
+ /**
+ * Creates a new `DbConnectionBuilder` database client and set the initial parameters.
+ *
+ * Users are not expected to call this constructor directly. Instead, use the static method `DbConnection.builder()`.
+ *
+ * @param remoteModule The remote module to use to connect to the SpacetimeDB server.
+ * @param dbConnectionConstructor The constructor to use to create a new `DbConnection`.
+ */
+ constructor(
+ private remoteModule: RemoteModule,
+ private dbConnectionConstructor: (imp: DbConnectionImpl) => DbConnection
+ ) {
+ this.#createWSFn = WebsocketDecompressAdapter.createWebSocketFn;
+ }
+
+ /**
+ * Set the URI of the SpacetimeDB server to connect to.
+ *
+ * @param uri The URI of the SpacetimeDB server to connect to.
+ *
+ **/
+ withUri(uri: string | URL): this {
+ this.#uri = new URL(uri);
+ return this;
+ }
+
+ /**
+ * Set the name or Identity of the database module to connect to.
+ *
+ * @param nameOrAddress
+ *
+ * @returns The `DbConnectionBuilder` instance.
+ */
+ withModuleName(nameOrAddress: string): this {
+ this.#nameOrAddress = nameOrAddress;
+ return this;
+ }
+
+ /**
+ * Set the identity of the client to connect to the database.
+ *
+ * @param token The credentials to use to authenticate with SpacetimeDB. This
+ * is optional. You can store the token returned by the `onConnect` callback
+ * to use in future connections.
+ *
+ * @returns The `DbConnectionBuilder` instance.
+ */
+ withToken(token?: string): this {
+ this.#token = token;
+ return this;
+ }
+
+ withWSFn(
+ createWSFn: (args: {
+ url: URL;
+ wsProtocol: string;
+ authToken?: string;
+ }) => Promise
+ ): this {
+ this.#createWSFn = createWSFn;
+ return this;
+ }
+
+ /**
+ * Set the compression algorithm to use for the connection.
+ *
+ * @param compression The compression algorithm to use for the connection.
+ */
+ withCompression(compression: 'gzip' | 'none'): this {
+ this.#compression = compression;
+ return this;
+ }
+
+ /**
+ * Sets the connection to operate in light mode.
+ *
+ * Light mode is a mode that reduces the amount of data sent over the network.
+ *
+ * @param lightMode The light mode for the connection.
+ */
+ withLightMode(lightMode: boolean): this {
+ this.#lightMode = lightMode;
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked upon authentication with the database.
+ *
+ * @param identity A unique identifier for a client connected to a database.
+ * @param token The credentials to use to authenticate with SpacetimeDB.
+ *
+ * @returns The `DbConnectionBuilder` instance.
+ *
+ * The callback will be invoked with the `Identity` and private authentication `token` provided by the database to identify this connection.
+ *
+ * If credentials were supplied to connect, those passed to the callback will be equivalent to the ones used to connect.
+ *
+ * If the initial connection was anonymous, a new set of credentials will be generated by the database to identify this user.
+ *
+ * The credentials passed to the callback can be saved and used to authenticate the same user in future connections.
+ *
+ * @example
+ *
+ * ```ts
+ * DbConnection.builder().onConnect((ctx, identity, token) => {
+ * console.log("Connected to SpacetimeDB with identity:", identity.toHexString());
+ * });
+ * ```
+ */
+ onConnect(
+ callback: (
+ connection: DbConnection,
+ identity: Identity,
+ token: string
+ ) => void
+ ): this {
+ this.#emitter.on('connect', callback);
+ return this;
+ }
+
+ /**
+ * Register a callback to be invoked upon an error.
+ *
+ * @example
+ *
+ * ```ts
+ * DbConnection.builder().onConnectError((ctx, error) => {
+ * console.log("Error connecting to SpacetimeDB:", error);
+ * });
+ * ```
+ */
+ onConnectError(callback: (ctx: ErrorContext, error: Error) => void): this {
+ this.#emitter.on('connectError', callback);
+ return this;
+ }
+
+ /**
+ * Registers a callback to run when a {@link DbConnection} whose connection initially succeeded
+ * is disconnected, either after a {@link DbConnection.disconnect} call or due to an error.
+ *
+ * If the connection ended because of an error, the error is passed to the callback.
+ *
+ * The `callback` will be installed on the `DbConnection` created by `build`
+ * before initiating the connection, ensuring there's no opportunity for the disconnect to happen
+ * before the callback is installed.
+ *
+ * Note that this does not trigger if `build` fails
+ * or in cases where {@link DbConnectionBuilder.onConnectError} would trigger.
+ * This callback only triggers if the connection closes after `build` returns successfully
+ * and {@link DbConnectionBuilder.onConnect} is invoked, i.e., after the `IdentityToken` is received.
+ *
+ * To simplify SDK implementation, at most one such callback can be registered.
+ * Calling `onDisconnect` on the same `DbConnectionBuilder` multiple times throws an error.
+ *
+ * Unlike callbacks registered via {@link DbConnection},
+ * no mechanism is provided to unregister the provided callback.
+ * This is a concession to ergonomics; there's no clean place to return a `CallbackId` from this method
+ * or from `build`.
+ *
+ * @param {function(error?: Error): void} callback - The callback to invoke upon disconnection.
+ * @throws {Error} Throws an error if called multiple times on the same `DbConnectionBuilder`.
+ */
+ onDisconnect(
+ callback: (ctx: ErrorContext, error?: Error | undefined) => void
+ ): this {
+ this.#emitter.on('disconnect', callback);
+ return this;
+ }
+
+ /**
+ * Builds a new `DbConnection` with the parameters set on this `DbConnectionBuilder` and attempts to connect to the SpacetimeDB server.
+ *
+ * @returns A new `DbConnection` with the parameters set on this `DbConnectionBuilder`.
+ *
+ * @example
+ *
+ * ```ts
+ * const host = "http://localhost:3000";
+ * const name_or_address = "database_name"
+ * const auth_token = undefined;
+ * DbConnection.builder().withUri(host).withModuleName(name_or_address).withToken(auth_token).build();
+ * ```
+ */
+ build(): DbConnection {
+ if (!this.#uri) {
+ throw new Error('URI is required to connect to SpacetimeDB');
+ }
+
+ if (!this.#nameOrAddress) {
+ throw new Error(
+ 'Database name or address is required to connect to SpacetimeDB'
+ );
+ }
+ // We could consider making this an `onConnectError` instead of throwing here.
+ // Ideally, it would be a compile time error, but I'm not sure how to accomplish that.
+ ensureMinimumVersionOrThrow(this.remoteModule.versionInfo?.cliVersion);
+
+ return this.dbConnectionConstructor(
+ new DbConnectionImpl({
+ uri: this.#uri,
+ nameOrAddress: this.#nameOrAddress,
+ identity: this.#identity,
+ token: this.#token,
+ emitter: this.#emitter,
+ compression: this.#compression,
+ lightMode: this.#lightMode,
+ createWSFn: this.#createWSFn,
+ remoteModule: this.remoteModule,
+ })
+ );
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/db_connection_impl.ts b/sdks/typescript/packages/sdk/src/db_connection_impl.ts
new file mode 100644
index 00000000000..d4ef11f66c0
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/db_connection_impl.ts
@@ -0,0 +1,904 @@
+import { ConnectionId } from './connection_id';
+import {
+ AlgebraicType,
+ ProductType,
+ ProductTypeElement,
+ SumType,
+ SumTypeVariant,
+ type ComparablePrimitive,
+} from './algebraic_type.ts';
+import {
+ AlgebraicValue,
+ parseValue,
+ ProductValue,
+ type ReducerArgsAdapter,
+ type ValueAdapter,
+} from './algebraic_value.ts';
+import BinaryReader from './binary_reader.ts';
+import BinaryWriter from './binary_writer.ts';
+import { BsatnRowList } from './client_api/bsatn_row_list_type.ts';
+import { ClientMessage } from './client_api/client_message_type.ts';
+import { DatabaseUpdate } from './client_api/database_update_type.ts';
+import { QueryUpdate } from './client_api/query_update_type.ts';
+import { ServerMessage } from './client_api/server_message_type.ts';
+import { TableUpdate as RawTableUpdate } from './client_api/table_update_type.ts';
+import type * as clientApi from './client_api/index.ts';
+import { ClientCache } from './client_cache.ts';
+import { DbConnectionBuilder } from './db_connection_builder.ts';
+import { type DbContext } from './db_context.ts';
+import type { Event } from './event.ts';
+import {
+ type ErrorContextInterface,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from './event_context.ts';
+import { EventEmitter } from './event_emitter.ts';
+import { decompress } from './decompress.ts';
+import type { Identity } from './identity.ts';
+import type {
+ IdentityTokenMessage,
+ Message,
+ SubscribeAppliedMessage,
+ UnsubscribeAppliedMessage,
+} from './message_types.ts';
+import type { ReducerEvent } from './reducer_event.ts';
+import type RemoteModule from './spacetime_module.ts';
+import {
+ TableCache,
+ type Operation,
+ type PendingCallback,
+ type TableUpdate as CacheTableUpdate,
+} from './table_cache.ts';
+import { deepEqual, toPascalCase } from './utils.ts';
+import { WebsocketDecompressAdapter } from './websocket_decompress_adapter.ts';
+import type { WebsocketTestAdapter } from './websocket_test_adapter.ts';
+import {
+ SubscriptionBuilderImpl,
+ SubscriptionHandleImpl,
+ SubscriptionManager,
+ type SubscribeEvent,
+} from './subscription_builder_impl.ts';
+import { stdbLogger } from './logger.ts';
+import { type ReducerRuntimeTypeInfo } from './spacetime_module.ts';
+import { fromByteArray } from 'base64-js';
+
+export {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ DbConnectionBuilder,
+ deepEqual,
+ ProductType,
+ ProductTypeElement,
+ ProductValue,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ type Event,
+ type ReducerArgsAdapter,
+ type ValueAdapter,
+};
+
+export type {
+ DbContext,
+ EventContextInterface,
+ ReducerEventContextInterface,
+ SubscriptionEventContextInterface,
+ ErrorContextInterface,
+ ReducerEvent,
+};
+
+export type ConnectionEvent = 'connect' | 'disconnect' | 'connectError';
+export type CallReducerFlags = 'FullUpdate' | 'NoSuccessNotify';
+
+type ReducerEventCallback = (
+ ctx: ReducerEventContextInterface,
+ ...args: ReducerArgs
+) => void;
+type SubscriptionEventCallback = (
+ ctx: SubscriptionEventContextInterface
+) => void;
+type ErrorCallback = (ctx: ErrorContextInterface) => void;
+
+function callReducerFlagsToNumber(flags: CallReducerFlags): number {
+ switch (flags) {
+ case 'FullUpdate':
+ return 0;
+ case 'NoSuccessNotify':
+ return 1;
+ }
+}
+
+type DbConnectionConfig = {
+ uri: URL;
+ nameOrAddress: string;
+ identity?: Identity;
+ token?: string;
+ emitter: EventEmitter;
+ remoteModule: RemoteModule;
+ createWSFn: typeof WebsocketDecompressAdapter.createWebSocketFn;
+ compression: 'gzip' | 'none';
+ lightMode: boolean;
+};
+
+export class DbConnectionImpl<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> implements DbContext
+{
+ /**
+ * Whether or not the connection is active.
+ */
+ isActive = false;
+
+ /**
+ * This connection's public identity.
+ */
+ identity?: Identity = undefined;
+
+ /**
+ * This connection's private authentication token.
+ */
+ token?: string = undefined;
+
+ /**
+ * The accessor field to access the tables in the database and associated
+ * callback functions.
+ */
+ db: DBView;
+
+ /**
+ * The accessor field to access the reducers in the database and associated
+ * callback functions.
+ */
+ reducers: Reducers;
+
+ /**
+ * The accessor field to access functions related to setting flags on
+ * reducers regarding how the server should handle the reducer call and
+ * the events that it sends back to the client.
+ */
+ setReducerFlags: SetReducerFlags;
+
+ /**
+ * The `ConnectionId` of the connection to to the database.
+ */
+ connectionId: ConnectionId = ConnectionId.random();
+
+ // These fields are meant to be strictly private.
+ #queryId = 0;
+ #emitter: EventEmitter;
+ #reducerEmitter: EventEmitter =
+ new EventEmitter();
+ #onApplied?: SubscriptionEventCallback;
+ #remoteModule: RemoteModule;
+ #messageQueue = Promise.resolve();
+ #subscriptionManager = new SubscriptionManager();
+
+ // These fields are not part of the public API, but in a pinch you
+ // could use JavaScript to access them by bypassing TypeScript's
+ // private fields.
+ // We use them in testing.
+ private clientCache: ClientCache;
+ private ws?: WebsocketDecompressAdapter | WebsocketTestAdapter;
+ private wsPromise: Promise<
+ WebsocketDecompressAdapter | WebsocketTestAdapter | undefined
+ >;
+
+ constructor({
+ uri,
+ nameOrAddress,
+ identity,
+ token,
+ emitter,
+ remoteModule,
+ createWSFn,
+ compression,
+ lightMode,
+ }: DbConnectionConfig) {
+ stdbLogger('info', 'Connecting to SpacetimeDB WS...');
+
+ let url = new URL(uri);
+ if (!/^wss?:/.test(uri.protocol)) {
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
+ }
+
+ this.identity = identity;
+ this.token = token;
+
+ this.#remoteModule = remoteModule;
+ this.#emitter = emitter;
+
+ let connectionId = this.connectionId.toHexString();
+ url.searchParams.set('connection_id', connectionId);
+
+ this.clientCache = new ClientCache();
+ this.db = this.#remoteModule.dbViewConstructor(this);
+ this.setReducerFlags = this.#remoteModule.setReducerFlagsConstructor();
+ this.reducers = this.#remoteModule.reducersConstructor(
+ this,
+ this.setReducerFlags
+ );
+
+ this.wsPromise = createWSFn({
+ url,
+ nameOrAddress,
+ wsProtocol: 'v1.bsatn.spacetimedb',
+ authToken: token,
+ compression: compression,
+ lightMode: lightMode,
+ })
+ .then(v => {
+ this.ws = v;
+
+ this.ws.onclose = () => {
+ this.#emitter.emit('disconnect', this);
+ };
+ this.ws.onerror = (e: ErrorEvent) => {
+ this.#emitter.emit('connectError', this, e);
+ };
+ this.ws.onopen = this.#handleOnOpen.bind(this);
+ this.ws.onmessage = this.#handleOnMessage.bind(this);
+ return v;
+ })
+ .catch(e => {
+ stdbLogger('error', 'Error connecting to SpacetimeDB WS');
+ this.#emitter.emit('connectError', this, e);
+
+ return undefined;
+ });
+ }
+
+ #getNextQueryId = () => {
+ const queryId = this.#queryId;
+ this.#queryId += 1;
+ return queryId;
+ };
+
+ // NOTE: This is very important!!! This is the actual function that
+ // gets called when you call `connection.subscriptionBuilder()`.
+ // The `subscriptionBuilder` function which is generated, just shadows
+ // this function in the type system, but not the actual implementation!
+ // Do not remove this function, or shoot yourself in the foot please.
+ // It's not clear what would be a better way to do this at this exact
+ // moment.
+ subscriptionBuilder = (): SubscriptionBuilderImpl => {
+ return new SubscriptionBuilderImpl(this);
+ };
+
+ registerSubscription(
+ handle: SubscriptionHandleImpl,
+ handleEmitter: EventEmitter,
+ querySql: string[]
+ ): number {
+ const queryId = this.#getNextQueryId();
+ this.#subscriptionManager.subscriptions.set(queryId, {
+ handle,
+ emitter: handleEmitter,
+ });
+ this.#sendMessage(
+ ClientMessage.SubscribeMulti({
+ queryStrings: querySql,
+ queryId: { id: queryId },
+ // The TypeScript SDK doesn't currently track `request_id`s,
+ // so always use 0.
+ requestId: 0,
+ })
+ );
+ return queryId;
+ }
+
+ unregisterSubscription(queryId: number): void {
+ this.#sendMessage(
+ ClientMessage.UnsubscribeMulti({
+ queryId: { id: queryId },
+ // The TypeScript SDK doesn't currently track `request_id`s,
+ // so always use 0.
+ requestId: 0,
+ })
+ );
+ }
+
+ // This function is async because we decompress the message async
+ async #processParsedMessage(
+ message: ServerMessage
+ ): Promise {
+ const parseRowList = (
+ type: 'insert' | 'delete',
+ tableName: string,
+ rowList: BsatnRowList
+ ): Operation[] => {
+ const buffer = rowList.rowsData;
+ const reader = new BinaryReader(buffer);
+ const rows: Operation[] = [];
+ const rowType = this.#remoteModule.tables[tableName]!.rowType;
+ const primaryKeyInfo =
+ this.#remoteModule.tables[tableName]!.primaryKeyInfo;
+ while (reader.offset < buffer.length + buffer.byteOffset) {
+ const initialOffset = reader.offset;
+ const row = rowType.deserialize(reader);
+ let rowId: ComparablePrimitive | undefined = undefined;
+ if (primaryKeyInfo !== undefined) {
+ rowId = primaryKeyInfo.colType.intoMapKey(
+ row[primaryKeyInfo.colName]
+ );
+ } else {
+ // Get a view of the bytes for this row.
+ const rowBytes = buffer.subarray(
+ initialOffset - buffer.byteOffset,
+ reader.offset - buffer.byteOffset
+ );
+ // Convert it to a base64 string, so we can use it as a map key.
+ const asBase64 = fromByteArray(rowBytes);
+ rowId = asBase64;
+ }
+
+ rows.push({
+ type,
+ rowId,
+ row,
+ });
+ }
+ return rows;
+ };
+
+ const parseTableUpdate = async (
+ rawTableUpdate: RawTableUpdate
+ ): Promise => {
+ const tableName = rawTableUpdate.tableName;
+ let operations: Operation[] = [];
+ for (const update of rawTableUpdate.updates) {
+ let decompressed: QueryUpdate;
+ if (update.tag === 'Gzip') {
+ const decompressedBuffer = await decompress(update.value, 'gzip');
+ decompressed = QueryUpdate.deserialize(
+ new BinaryReader(decompressedBuffer)
+ );
+ } else if (update.tag === 'Brotli') {
+ throw new Error(
+ 'Brotli compression not supported. Please use gzip or none compression in withCompression method on DbConnection.'
+ );
+ } else {
+ decompressed = update.value;
+ }
+ operations = operations.concat(
+ parseRowList('insert', tableName, decompressed.inserts)
+ );
+ operations = operations.concat(
+ parseRowList('delete', tableName, decompressed.deletes)
+ );
+ }
+ return {
+ tableName,
+ operations,
+ };
+ };
+
+ const parseDatabaseUpdate = async (
+ dbUpdate: DatabaseUpdate
+ ): Promise => {
+ const tableUpdates: CacheTableUpdate[] = [];
+ for (const rawTableUpdate of dbUpdate.tables) {
+ tableUpdates.push(await parseTableUpdate(rawTableUpdate));
+ }
+ return tableUpdates;
+ };
+
+ switch (message.tag) {
+ case 'InitialSubscription': {
+ const dbUpdate = message.value.databaseUpdate;
+ const tableUpdates = await parseDatabaseUpdate(dbUpdate);
+ const subscriptionUpdate: Message = {
+ tag: 'InitialSubscription',
+ tableUpdates,
+ };
+ return subscriptionUpdate;
+ }
+
+ case 'TransactionUpdateLight': {
+ const dbUpdate = message.value.update;
+ const tableUpdates = await parseDatabaseUpdate(dbUpdate);
+ const subscriptionUpdate: Message = {
+ tag: 'TransactionUpdateLight',
+ tableUpdates,
+ };
+ return subscriptionUpdate;
+ }
+
+ case 'TransactionUpdate': {
+ const txUpdate = message.value;
+ const identity = txUpdate.callerIdentity;
+ const connectionId = ConnectionId.nullIfZero(
+ txUpdate.callerConnectionId
+ );
+ const reducerName: string = txUpdate.reducerCall.reducerName;
+ const args = txUpdate.reducerCall.args;
+ const energyQuantaUsed = txUpdate.energyQuantaUsed;
+
+ let tableUpdates: CacheTableUpdate[];
+ let errMessage = '';
+ switch (txUpdate.status.tag) {
+ case 'Committed':
+ tableUpdates = await parseDatabaseUpdate(txUpdate.status.value);
+ break;
+ case 'Failed':
+ tableUpdates = [];
+ errMessage = txUpdate.status.value;
+ break;
+ case 'OutOfEnergy':
+ tableUpdates = [];
+ break;
+ }
+
+ // TODO: Can `reducerName` be ''?
+ // See: https://github.com/clockworklabs/SpacetimeDB/blob/a2a1b5d9b2e0ebaaf753d074db056d319952d442/crates/core/src/client/message_handlers.rs#L155
+ if (reducerName === '') {
+ let errorMessage = errMessage;
+ console.error(`Received an error from the database: ${errorMessage}`);
+ return;
+ }
+
+ let reducerInfo:
+ | {
+ reducerName: string;
+ args: Uint8Array;
+ }
+ | undefined;
+ if (reducerName !== '') {
+ reducerInfo = {
+ reducerName,
+ args,
+ };
+ }
+
+ const transactionUpdate: Message = {
+ tag: 'TransactionUpdate',
+ tableUpdates,
+ identity,
+ connectionId,
+ reducerInfo,
+ status: txUpdate.status,
+ energyConsumed: energyQuantaUsed.quanta,
+ message: errMessage,
+ timestamp: txUpdate.timestamp,
+ };
+ return transactionUpdate;
+ }
+
+ case 'IdentityToken': {
+ const identityTokenMessage: IdentityTokenMessage = {
+ tag: 'IdentityToken',
+ identity: message.value.identity,
+ token: message.value.token,
+ connectionId: message.value.connectionId,
+ };
+ return identityTokenMessage;
+ }
+
+ case 'OneOffQueryResponse': {
+ throw new Error(
+ `TypeScript SDK never sends one-off queries, but got OneOffQueryResponse ${message}`
+ );
+ }
+
+ case 'SubscribeMultiApplied': {
+ const parsedTableUpdates = await parseDatabaseUpdate(
+ message.value.update
+ );
+ const subscribeAppliedMessage: SubscribeAppliedMessage = {
+ tag: 'SubscribeApplied',
+ queryId: message.value.queryId.id,
+ tableUpdates: parsedTableUpdates,
+ };
+ return subscribeAppliedMessage;
+ }
+
+ case 'UnsubscribeMultiApplied': {
+ const parsedTableUpdates = await parseDatabaseUpdate(
+ message.value.update
+ );
+ const unsubscribeAppliedMessage: UnsubscribeAppliedMessage = {
+ tag: 'UnsubscribeApplied',
+ queryId: message.value.queryId.id,
+ tableUpdates: parsedTableUpdates,
+ };
+ return unsubscribeAppliedMessage;
+ }
+
+ case 'SubscriptionError': {
+ return {
+ tag: 'SubscriptionError',
+ queryId: message.value.queryId,
+ error: message.value.error,
+ };
+ }
+ }
+ }
+
+ #sendMessage(message: ClientMessage): void {
+ this.wsPromise.then(wsResolved => {
+ if (wsResolved) {
+ const writer = new BinaryWriter(1024);
+ ClientMessage.serialize(writer, message);
+ const encoded = writer.getBuffer();
+ wsResolved.send(encoded);
+ }
+ });
+ }
+
+ /**
+ * Handles WebSocket onOpen event.
+ */
+ #handleOnOpen(): void {
+ this.isActive = true;
+ }
+
+ #applyTableUpdates(
+ tableUpdates: CacheTableUpdate[],
+ eventContext: EventContextInterface
+ ): PendingCallback[] {
+ let pendingCallbacks: PendingCallback[] = [];
+ for (let tableUpdate of tableUpdates) {
+ // Get table information for the table being updated
+ const tableName = tableUpdate.tableName;
+ const tableTypeInfo = this.#remoteModule.tables[tableName]!;
+ const table = this.clientCache.getOrCreateTable(tableTypeInfo);
+ const newCallbacks = table.applyOperations(
+ tableUpdate.operations,
+ eventContext
+ );
+ for (const callback of newCallbacks) {
+ pendingCallbacks.push(callback);
+ }
+ }
+ return pendingCallbacks;
+ }
+
+ async #processMessage(data: Uint8Array): Promise {
+ const serverMessage = parseValue(ServerMessage, data);
+ const message = await this.#processParsedMessage(serverMessage);
+ if (!message) {
+ return;
+ }
+ switch (message.tag) {
+ case 'InitialSubscription': {
+ let event: Event = { tag: 'SubscribeApplied' };
+
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ // Remove the event from the subscription event context
+ // It is not a field in the type narrowed SubscriptionEventContext
+ const { event: _, ...subscriptionEventContext } = eventContext;
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+
+ if (this.#emitter) {
+ this.#onApplied?.(subscriptionEventContext);
+ }
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ break;
+ }
+ case 'TransactionUpdateLight': {
+ let event: Event = { tag: 'UnknownTransaction' };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ break;
+ }
+ case 'TransactionUpdate': {
+ let reducerInfo = message.reducerInfo;
+ let unknownTransaction = false;
+ let reducerArgs: any | undefined;
+ let reducerTypeInfo: ReducerRuntimeTypeInfo | undefined;
+ if (!reducerInfo) {
+ unknownTransaction = true;
+ } else {
+ reducerTypeInfo =
+ this.#remoteModule.reducers[reducerInfo.reducerName];
+ try {
+ const reader = new BinaryReader(reducerInfo.args as Uint8Array);
+ reducerArgs = reducerTypeInfo.argsType.deserialize(reader);
+ } catch {
+ // This should only be printed in development, since it's
+ // possible for clients to receive new reducers that they don't
+ // know about.
+ console.debug('Failed to deserialize reducer arguments');
+ unknownTransaction = true;
+ }
+ }
+
+ if (unknownTransaction) {
+ const event: Event = { tag: 'UnknownTransaction' };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ return;
+ }
+
+ // At this point, we know that `reducerInfo` is not null because
+ // we return if `unknownTransaction` is true.
+ reducerInfo = reducerInfo!;
+ reducerTypeInfo = reducerTypeInfo!;
+
+ // Thus this must be a reducer event create it and emit it.
+ const reducerEvent = {
+ callerIdentity: message.identity,
+ status: message.status,
+ callerConnectionId: message.connectionId as ConnectionId,
+ timestamp: message.timestamp,
+ energyConsumed: message.energyConsumed,
+ reducer: {
+ name: reducerInfo.reducerName,
+ args: reducerArgs,
+ },
+ };
+ const event: Event = {
+ tag: 'Reducer',
+ value: reducerEvent,
+ };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const reducerEventContext = {
+ ...eventContext,
+ event: reducerEvent,
+ };
+
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+
+ const argsArray: any[] = [];
+ reducerTypeInfo.argsType.product.elements.forEach((element, index) => {
+ argsArray.push(reducerArgs[element.name]);
+ });
+ this.#reducerEmitter.emit(
+ reducerInfo.reducerName,
+ reducerEventContext,
+ ...argsArray
+ );
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ break;
+ }
+ case 'IdentityToken': {
+ this.identity = message.identity;
+ if (!this.token && message.token) {
+ this.token = message.token;
+ }
+ this.connectionId = message.connectionId;
+ this.#emitter.emit('connect', this, this.identity, this.token);
+ break;
+ }
+ case 'SubscribeApplied': {
+ const subscription = this.#subscriptionManager.subscriptions.get(
+ message.queryId
+ );
+ if (subscription === undefined) {
+ stdbLogger(
+ 'error',
+ `Received SubscribeApplied for unknown queryId ${message.queryId}.`
+ );
+ // If we don't know about the subscription, we won't apply the table updates.
+ break;
+ }
+ const event: Event = { tag: 'SubscribeApplied' };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const { event: _, ...subscriptionEventContext } = eventContext;
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+ subscription?.emitter.emit('applied', subscriptionEventContext);
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ break;
+ }
+ case 'UnsubscribeApplied': {
+ const subscription = this.#subscriptionManager.subscriptions.get(
+ message.queryId
+ );
+ if (subscription === undefined) {
+ stdbLogger(
+ 'error',
+ `Received UnsubscribeApplied for unknown queryId ${message.queryId}.`
+ );
+ // If we don't know about the subscription, we won't apply the table updates.
+ break;
+ }
+ const event: Event = { tag: 'UnsubscribeApplied' };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const { event: _, ...subscriptionEventContext } = eventContext;
+ const callbacks = this.#applyTableUpdates(
+ message.tableUpdates,
+ eventContext
+ );
+ subscription?.emitter.emit('end', subscriptionEventContext);
+ this.#subscriptionManager.subscriptions.delete(message.queryId);
+ for (const callback of callbacks) {
+ callback.cb();
+ }
+ break;
+ }
+ case 'SubscriptionError': {
+ const error = Error(message.error);
+ const event: Event = { tag: 'Error', value: error };
+ const eventContext = this.#remoteModule.eventContextConstructor(
+ this,
+ event
+ );
+ const errorContext = {
+ ...eventContext,
+ event: error,
+ };
+ if (message.queryId !== undefined) {
+ this.#subscriptionManager.subscriptions
+ .get(message.queryId)
+ ?.emitter.emit('error', errorContext, error);
+ this.#subscriptionManager.subscriptions.delete(message.queryId);
+ } else {
+ console.error('Received an error message without a queryId: ', error);
+ // TODO: This should actually kill the connection.
+ // A subscription error without a specific subscription means we aren't receiving
+ // updates for all of our subscriptions, so our cache is out of sync.
+
+ // Send it to all of them:
+ this.#subscriptionManager.subscriptions.forEach(({ emitter }) => {
+ emitter.emit('error', errorContext, error);
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles WebSocket onMessage event.
+ * @param wsMessage MessageEvent object.
+ */
+ #handleOnMessage(wsMessage: { data: Uint8Array }): void {
+ // Utilize promise chaining to ensure that we process messages in order
+ // even though we are processing them asyncronously. This will not begin
+ // processing the next message until we await the processing of the
+ // current message.
+ this.#messageQueue = this.#messageQueue.then(() => {
+ return this.#processMessage(wsMessage.data);
+ });
+ }
+
+ /**
+ * Call a reducer on your SpacetimeDB module.
+ *
+ * @param reducerName The name of the reducer to call
+ * @param argsSerializer The arguments to pass to the reducer
+ */
+ callReducer(
+ reducerName: string,
+ argsBuffer: Uint8Array,
+ flags: CallReducerFlags
+ ): void {
+ const message = ClientMessage.CallReducer({
+ reducer: reducerName,
+ args: argsBuffer,
+ // The TypeScript SDK doesn't currently track `request_id`s,
+ // so always use 0.
+ requestId: 0,
+ flags: callReducerFlagsToNumber(flags),
+ });
+ this.#sendMessage(message);
+ }
+
+ /**
+ * Close the current connection.
+ *
+ * @example
+ *
+ * ```ts
+ * const connection = DbConnection.builder().build();
+ * connection.disconnect()
+ * ```
+ */
+ disconnect(): void {
+ this.wsPromise.then(wsResolved => {
+ if (wsResolved) {
+ wsResolved.close();
+ }
+ });
+ }
+
+ #on(
+ eventName: ConnectionEvent,
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.on(eventName, callback);
+ }
+
+ #off(
+ eventName: ConnectionEvent,
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.off(eventName, callback);
+ }
+
+ #onConnect(callback: (ctx: DbConnectionImpl, ...args: any[]) => void): void {
+ this.#emitter.on('connect', callback);
+ }
+
+ #onDisconnect(
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.on('disconnect', callback);
+ }
+
+ #onConnectError(
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.on('connectError', callback);
+ }
+
+ #removeOnConnect(
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.off('connect', callback);
+ }
+
+ #removeOnDisconnect(
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.off('disconnect', callback);
+ }
+
+ #removeOnConnectError(
+ callback: (ctx: DbConnectionImpl, ...args: any[]) => void
+ ): void {
+ this.#emitter.off('connectError', callback);
+ }
+
+ // Note: This is required to be public because it needs to be
+ // called from the `RemoteReducers` class.
+ onReducer(reducerName: string, callback: ReducerEventCallback): void {
+ this.#reducerEmitter.on(reducerName, callback);
+ }
+
+ // Note: This is required to be public because it needs to be
+ // called from the `RemoteReducers` class.
+ offReducer(reducerName: string, callback: ReducerEventCallback): void {
+ this.#reducerEmitter.off(reducerName, callback);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/db_context.ts b/sdks/typescript/packages/sdk/src/db_context.ts
new file mode 100644
index 00000000000..533ce32e923
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/db_context.ts
@@ -0,0 +1,35 @@
+import type { SubscriptionBuilderImpl } from './subscription_builder_impl';
+
+/**
+ * Interface representing a database context.
+ *
+ * @template DBView - Type representing the database view.
+ * @template Reducers - Type representing the reducers.
+ * @template SetReducerFlags - Type representing the reducer flags collection.
+ */
+export interface DbContext<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> {
+ db: DBView;
+ reducers: Reducers;
+ setReducerFlags: SetReducerFlags;
+ isActive: boolean;
+
+ /**
+ * Creates a new subscription builder.
+ *
+ * @returns The subscription builder.
+ */
+ subscriptionBuilder(): SubscriptionBuilderImpl<
+ DBView,
+ Reducers,
+ SetReducerFlags
+ >;
+
+ /**
+ * Disconnects from the database.
+ */
+ disconnect(): void;
+}
diff --git a/sdks/typescript/packages/sdk/src/decompress.ts b/sdks/typescript/packages/sdk/src/decompress.ts
new file mode 100644
index 00000000000..cdc49a2241a
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/decompress.ts
@@ -0,0 +1,52 @@
+export async function decompress(
+ buffer: Uint8Array,
+ // Leaving it here to expand to brotli when it lands in the browsers and NodeJS
+ type: 'gzip',
+ chunkSize: number = 128 * 1024 // 128KB
+): Promise {
+ // Create a single ReadableStream to handle chunks
+ let offset = 0;
+ const readableStream = new ReadableStream({
+ pull(controller) {
+ if (offset < buffer.length) {
+ // Slice a chunk of the buffer and enqueue it
+ const chunk = buffer.subarray(
+ offset,
+ Math.min(offset + chunkSize, buffer.length)
+ );
+ controller.enqueue(chunk);
+ offset += chunkSize;
+ } else {
+ controller.close();
+ }
+ },
+ });
+
+ // Create a single DecompressionStream
+ const decompressionStream = new DecompressionStream(type);
+
+ // Pipe the ReadableStream through the DecompressionStream
+ const decompressedStream = readableStream.pipeThrough(decompressionStream);
+
+ // Collect the decompressed chunks efficiently
+ const reader = decompressedStream.getReader();
+ const chunks: Uint8Array[] = [];
+ let totalLength = 0;
+ let result: any;
+
+ while (!(result = await reader.read()).done) {
+ chunks.push(result.value);
+ totalLength += result.value.length;
+ }
+
+ // Allocate a single Uint8Array for the decompressed data
+ const decompressedArray = new Uint8Array(totalLength);
+ let chunkOffset = 0;
+
+ for (const chunk of chunks) {
+ decompressedArray.set(chunk, chunkOffset);
+ chunkOffset += chunk.length;
+ }
+
+ return decompressedArray;
+}
diff --git a/sdks/typescript/packages/sdk/src/event.ts b/sdks/typescript/packages/sdk/src/event.ts
new file mode 100644
index 00000000000..8be677e47ab
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/event.ts
@@ -0,0 +1,8 @@
+import type { ReducerEvent, ReducerInfoType } from './reducer_event';
+
+export type Event =
+ | { tag: 'Reducer'; value: ReducerEvent }
+ | { tag: 'SubscribeApplied' }
+ | { tag: 'UnsubscribeApplied' }
+ | { tag: 'Error'; value: Error }
+ | { tag: 'UnknownTransaction' };
diff --git a/sdks/typescript/packages/sdk/src/event_context.ts b/sdks/typescript/packages/sdk/src/event_context.ts
new file mode 100644
index 00000000000..f4c67bc5685
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/event_context.ts
@@ -0,0 +1,40 @@
+import type { DbContext } from './db_context';
+import type { Event } from './event.ts';
+import type { ReducerEvent, ReducerInfoType } from './reducer_event.ts';
+
+export interface EventContextInterface<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+ Reducer extends ReducerInfoType = never,
+> extends DbContext {
+ /** Enum with variants for all possible events. */
+ event: Event;
+}
+
+export interface ReducerEventContextInterface<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+ Reducer extends ReducerInfoType = never,
+> extends DbContext {
+ /** Enum with variants for all possible events. */
+ event: ReducerEvent;
+}
+
+export interface SubscriptionEventContextInterface<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> extends DbContext {
+ /** No event is provided **/
+}
+
+export interface ErrorContextInterface<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> extends DbContext {
+ /** Enum with variants for all possible events. */
+ event?: Error;
+}
diff --git a/sdks/typescript/packages/sdk/src/event_emitter.ts b/sdks/typescript/packages/sdk/src/event_emitter.ts
new file mode 100644
index 00000000000..26c702a5e67
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/event_emitter.ts
@@ -0,0 +1,31 @@
+export class EventEmitter {
+ #events: Map> = new Map();
+
+ on(event: Key, callback: Callback): void {
+ let callbacks = this.#events.get(event);
+ if (!callbacks) {
+ callbacks = new Set();
+ this.#events.set(event, callbacks);
+ }
+ callbacks.add(callback);
+ }
+
+ off(event: Key, callback: Callback): void {
+ let callbacks = this.#events.get(event);
+ if (!callbacks) {
+ return;
+ }
+ callbacks.delete(callback);
+ }
+
+ emit(event: Key, ...args: any[]): void {
+ let callbacks = this.#events.get(event);
+ if (!callbacks) {
+ return;
+ }
+
+ for (let callback of callbacks) {
+ callback(...args);
+ }
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/identity.ts b/sdks/typescript/packages/sdk/src/identity.ts
new file mode 100644
index 00000000000..9ce25dccbdc
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/identity.ts
@@ -0,0 +1,53 @@
+import BinaryReader from './binary_reader';
+import BinaryWriter from './binary_writer';
+import { hexStringToU256, u256ToHexString, u256ToUint8Array } from './utils';
+
+/**
+ * A unique identifier for a user connected to a database.
+ */
+export class Identity {
+ data: bigint;
+
+ get __identity__(): bigint {
+ return this.data;
+ }
+
+ /**
+ * Creates a new `Identity`.
+ *
+ * `data` can be a hexadecimal string or a `bigint`.
+ */
+ constructor(data: string | bigint) {
+ // we get a JSON with __identity__ when getting a token with a JSON API
+ // and an bigint when using BSATN
+ this.data = typeof data === 'string' ? hexStringToU256(data) : data;
+ }
+
+ /**
+ * Compare two identities for equality.
+ */
+ isEqual(other: Identity): boolean {
+ return this.toHexString() === other.toHexString();
+ }
+
+ /**
+ * Print the identity as a hexadecimal string.
+ */
+ toHexString(): string {
+ return u256ToHexString(this.data);
+ }
+
+ /**
+ * Convert the address to a Uint8Array.
+ */
+ toUint8Array(): Uint8Array {
+ return u256ToUint8Array(this.data);
+ }
+
+ /**
+ * Parse an Identity from a hexadecimal string.
+ */
+ static fromString(str: string): Identity {
+ return new Identity(str);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/index.ts b/sdks/typescript/packages/sdk/src/index.ts
new file mode 100644
index 00000000000..7aa615aaebe
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/index.ts
@@ -0,0 +1,10 @@
+// Should be at the top as other modules depend on it
+export * from './db_connection_impl.ts';
+
+export * from './connection_id.ts';
+export * from './schedule_at';
+export * from './client_cache.ts';
+export * from './identity.ts';
+export * from './message_types.ts';
+export * from './timestamp.ts';
+export * from './time_duration.ts';
diff --git a/sdks/typescript/packages/sdk/src/json_api.ts b/sdks/typescript/packages/sdk/src/json_api.ts
new file mode 100644
index 00000000000..e48cff24f1d
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/json_api.ts
@@ -0,0 +1,46 @@
+export interface Message {
+ IdentityToken?: IdentityToken | undefined;
+ SubscriptionUpdate?: SubscriptionUpdate | undefined;
+ TransactionUpdate?: TransactionUpdate | undefined;
+}
+
+export interface IdentityToken {
+ identity: string;
+ token: string;
+ address: string;
+}
+
+export interface SubscriptionUpdate {
+ table_updates: TableUpdate[];
+}
+
+export interface TableUpdate {
+ table_id: number;
+ table_name: string;
+ table_row_operations: TableRowOperation[];
+}
+
+export interface TableRowOperation {
+ op: 'insert' | 'delete';
+ row: any[];
+}
+
+export interface TransactionUpdate {
+ event: Event;
+ subscription_update: SubscriptionUpdate;
+}
+
+export interface Event {
+ timestamp: number;
+ status: 'committed' | 'failed' | 'out_of_energy';
+ caller_identity: string;
+ caller_address: string;
+ function_call: FunctionCall;
+ energy_quanta_used: number;
+ message: string;
+}
+
+export interface FunctionCall {
+ reducer: string;
+ args: string;
+}
diff --git a/sdks/typescript/packages/sdk/src/logger.ts b/sdks/typescript/packages/sdk/src/logger.ts
new file mode 100644
index 00000000000..2b986862e90
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/logger.ts
@@ -0,0 +1,36 @@
+type LogLevel = 'info' | 'warn' | 'error' | 'debug';
+
+const LogLevelIdentifierIcon = {
+ component: '📦',
+ info: 'ℹ️',
+ warn: '⚠️',
+ error: '❌',
+ debug: '🐛',
+};
+
+const LogStyle = {
+ component:
+ 'color: #fff; background-color: #8D6FDD; padding: 2px 5px; border-radius: 3px;',
+ info: 'color: #fff; background-color: #007bff; padding: 2px 5px; border-radius: 3px;',
+ warn: 'color: #fff; background-color: #ffc107; padding: 2px 5px; border-radius: 3px;',
+ error:
+ 'color: #fff; background-color: #dc3545; padding: 2px 5px; border-radius: 3px;',
+ debug:
+ 'color: #fff; background-color: #28a745; padding: 2px 5px; border-radius: 3px;',
+};
+
+const LogTextStyle = {
+ component: 'color: #8D6FDD;',
+ info: 'color: #007bff;',
+ warn: 'color: #ffc107;',
+ error: 'color: #dc3545;',
+ debug: 'color: #28a745;',
+};
+
+export const stdbLogger = (level: LogLevel, message: any): void => {
+ console.log(
+ `%c${LogLevelIdentifierIcon[level]} ${level.toUpperCase()}%c ${message}`,
+ LogStyle[level],
+ LogTextStyle[level]
+ );
+};
diff --git a/sdks/typescript/packages/sdk/src/message_types.ts b/sdks/typescript/packages/sdk/src/message_types.ts
new file mode 100644
index 00000000000..3ce1f9ca982
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/message_types.ts
@@ -0,0 +1,64 @@
+import { ConnectionId } from './connection_id';
+import type { UpdateStatus } from './client_api/index.ts';
+import { Identity } from './identity.ts';
+import type { TableUpdate } from './table_cache.ts';
+import { Timestamp } from './timestamp.ts';
+
+export type InitialSubscriptionMessage = {
+ tag: 'InitialSubscription';
+ tableUpdates: TableUpdate[];
+};
+
+export type TransactionUpdateMessage = {
+ tag: 'TransactionUpdate';
+ tableUpdates: TableUpdate[];
+ identity: Identity;
+ connectionId: ConnectionId | null;
+ reducerInfo?: {
+ reducerName: string;
+ args: Uint8Array;
+ };
+ status: UpdateStatus;
+ message: string;
+ timestamp: Timestamp;
+ energyConsumed: bigint;
+};
+
+export type TransactionUpdateLightMessage = {
+ tag: 'TransactionUpdateLight';
+ tableUpdates: TableUpdate[];
+};
+
+export type IdentityTokenMessage = {
+ tag: 'IdentityToken';
+ identity: Identity;
+ token: string;
+ connectionId: ConnectionId;
+};
+
+export type SubscribeAppliedMessage = {
+ tag: 'SubscribeApplied';
+ queryId: number;
+ tableUpdates: TableUpdate[];
+};
+
+export type UnsubscribeAppliedMessage = {
+ tag: 'UnsubscribeApplied';
+ queryId: number;
+ tableUpdates: TableUpdate[];
+};
+
+export type SubscriptionError = {
+ tag: 'SubscriptionError';
+ queryId?: number;
+ error: string;
+};
+
+export type Message =
+ | InitialSubscriptionMessage
+ | TransactionUpdateMessage
+ | TransactionUpdateLightMessage
+ | IdentityTokenMessage
+ | SubscribeAppliedMessage
+ | UnsubscribeAppliedMessage
+ | SubscriptionError;
diff --git a/sdks/typescript/packages/sdk/src/reducer_event.ts b/sdks/typescript/packages/sdk/src/reducer_event.ts
new file mode 100644
index 00000000000..8711dbfb139
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/reducer_event.ts
@@ -0,0 +1,45 @@
+import { ConnectionId } from './connection_id';
+import { Timestamp } from './timestamp.ts';
+import type { UpdateStatus } from './client_api/index.ts';
+import { Identity } from './identity.ts';
+
+export type ReducerInfoType = { name: string; args?: any } | never;
+
+export type ReducerEvent = {
+ /**
+ * The time when the reducer started running.
+ *
+ * @internal This is a number and not Date, as JSON.stringify with date in it gives number, but JSON.parse of the same string does not give date. TO avoid
+ * confusion in typing we'll keep it a number
+ */
+ timestamp: Timestamp;
+
+ /**
+ * Whether the reducer committed, was aborted due to insufficient energy, or failed with an error message.
+ */
+ status: UpdateStatus;
+
+ /**
+ * The identity of the caller.
+ * TODO: Revise these to reflect the forthcoming Identity proposal.
+ */
+ callerIdentity: Identity;
+
+ /**
+ * The connection ID of the caller.
+ *
+ * May be `null`, e.g. for scheduled reducers.
+ */
+ callerConnectionId?: ConnectionId;
+
+ /**
+ * The amount of energy consumed by the reducer run, in eV.
+ * (Not literal eV, but our SpacetimeDB energy unit eV.)
+ * May be present or undefined at the implementor's discretion;
+ * future work may determine an interface for module developers
+ * to request this value be published or hidden.
+ */
+ energyConsumed?: bigint;
+
+ reducer: Reducer;
+};
diff --git a/sdks/typescript/packages/sdk/src/schedule_at.ts b/sdks/typescript/packages/sdk/src/schedule_at.ts
new file mode 100644
index 00000000000..e31dd692f5e
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/schedule_at.ts
@@ -0,0 +1,45 @@
+import { AlgebraicType, SumTypeVariant } from './algebraic_type';
+import type { AlgebraicValue } from './algebraic_value';
+
+export namespace ScheduleAt {
+ export function getAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createSumType([
+ new SumTypeVariant('Interval', AlgebraicType.createU64Type()),
+ new SumTypeVariant('Time', AlgebraicType.createU64Type()),
+ ]);
+ }
+
+ export function serialize(value: ScheduleAt): object {
+ switch (value.tag) {
+ case 'Interval':
+ return { Interval: value.value };
+ case 'Time':
+ return { Time: value.value };
+ default:
+ throw 'unreachable';
+ }
+ }
+
+ export type Interval = { tag: 'Interval'; value: BigInt };
+ export const Interval = (value: BigInt): Interval => ({
+ tag: 'Interval',
+ value,
+ });
+ export type Time = { tag: 'Time'; value: BigInt };
+ export const Time = (value: BigInt): Time => ({ tag: 'Time', value });
+
+ export function fromValue(value: AlgebraicValue): ScheduleAt {
+ let sumValue = value.asSumValue();
+ switch (sumValue.tag) {
+ case 0:
+ return { tag: 'Interval', value: sumValue.value.asBigInt() };
+ case 1:
+ return { tag: 'Time', value: sumValue.value.asBigInt() };
+ default:
+ throw 'unreachable';
+ }
+ }
+}
+
+export type ScheduleAt = ScheduleAt.Interval | ScheduleAt.Time;
+export default ScheduleAt;
diff --git a/sdks/typescript/packages/sdk/src/serializer.ts b/sdks/typescript/packages/sdk/src/serializer.ts
new file mode 100644
index 00000000000..ca1130adbd7
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/serializer.ts
@@ -0,0 +1,123 @@
+import { AlgebraicType } from './algebraic_type';
+import type { MapValue } from './algebraic_value';
+import BinaryWriter from './binary_writer';
+
+export interface Serializer {
+ write(type: AlgebraicType, value: any): any;
+ args(): any;
+}
+
+export class BinarySerializer {
+ #writer: BinaryWriter;
+
+ constructor() {
+ this.#writer = new BinaryWriter(1024);
+ }
+
+ args(): any {
+ return this.getBuffer();
+ }
+
+ getBuffer(): Uint8Array {
+ return this.#writer.getBuffer();
+ }
+
+ write(type: AlgebraicType, value: any): void {
+ switch (type.type) {
+ case AlgebraicType.Type.ProductType: {
+ for (const element of type.product.elements) {
+ this.write(element.algebraicType, value[element.name]);
+ }
+ break;
+ }
+ case AlgebraicType.Type.SumType:
+ const sum = type.sum;
+ if (
+ sum.variants.length == 2 &&
+ sum.variants[0].name === 'some' &&
+ sum.variants[1].name === 'none'
+ ) {
+ if (value) {
+ this.writeByte(0);
+ this.write(sum.variants[0].algebraicType, value);
+ } else {
+ this.writeByte(1);
+ }
+ } else {
+ const index = type.sum.variants.findIndex(v => v.name === value.tag);
+ if (index < 0) {
+ throw `Can't serialize a sum type, couldn't find ${value.tag} tag`;
+ }
+
+ this.writeByte(index);
+ this.write(sum.variants[index].algebraicType, value.value);
+ }
+ break;
+ case AlgebraicType.Type.ArrayType:
+ const elemTy = type.array;
+ const array = value as any[];
+ this.#writer.writeU32(array.length);
+ for (const element of array) {
+ this.write(elemTy, element);
+ }
+ break;
+ case AlgebraicType.Type.MapType:
+ const mapTy = type.map;
+ const map = value as MapValue;
+ this.#writer.writeU32(map.size);
+ for (const entry of map) {
+ this.write(mapTy.keyType, entry[0]);
+ this.write(mapTy.valueType, entry[1]);
+ }
+ break;
+ case AlgebraicType.Type.String:
+ this.#writer.writeString(value);
+ break;
+ case AlgebraicType.Type.Bool:
+ this.#writer.writeBool(value);
+ break;
+ case AlgebraicType.Type.I8:
+ this.#writer.writeI8(value);
+ break;
+ case AlgebraicType.Type.U8:
+ this.#writer.writeU8(value);
+ break;
+ case AlgebraicType.Type.I16:
+ this.#writer.writeI16(value);
+ break;
+ case AlgebraicType.Type.U16:
+ this.#writer.writeU16(value);
+ break;
+ case AlgebraicType.Type.I32:
+ this.#writer.writeI32(value);
+ break;
+ case AlgebraicType.Type.U32:
+ this.#writer.writeU32(value);
+ break;
+ case AlgebraicType.Type.I64:
+ this.#writer.writeI64(value);
+ break;
+ case AlgebraicType.Type.U64:
+ this.#writer.writeU64(value);
+ break;
+ case AlgebraicType.Type.I128:
+ this.#writer.writeI128(value);
+ break;
+ case AlgebraicType.Type.U128:
+ this.#writer.writeU128(value);
+ break;
+ case AlgebraicType.Type.F32:
+ this.#writer.writeF32(value);
+ break;
+ case AlgebraicType.Type.F64:
+ this.#writer.writeF64(value);
+ break;
+ default:
+ break;
+ }
+ }
+
+ writeByte(byte: number): void {
+ this.#writer.writeU8(byte);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/spacetime_module.ts b/sdks/typescript/packages/sdk/src/spacetime_module.ts
new file mode 100644
index 00000000000..1ccefe2deb5
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/spacetime_module.ts
@@ -0,0 +1,33 @@
+import type { AlgebraicType } from './algebraic_type';
+import type { DbConnectionImpl } from './db_connection_impl';
+
+export interface TableRuntimeTypeInfo {
+ tableName: string;
+ rowType: AlgebraicType;
+ primaryKeyInfo?: PrimaryKeyInfo;
+}
+
+export interface PrimaryKeyInfo {
+ colName: string;
+ colType: AlgebraicType;
+}
+
+export interface ReducerRuntimeTypeInfo {
+ reducerName: string;
+ argsType: AlgebraicType;
+}
+
+export default interface RemoteModule {
+ tables: { [name: string]: TableRuntimeTypeInfo };
+ reducers: { [name: string]: ReducerRuntimeTypeInfo };
+ eventContextConstructor: (imp: DbConnectionImpl, event: any) => any;
+ dbViewConstructor: (connection: DbConnectionImpl) => any;
+ reducersConstructor: (
+ connection: DbConnectionImpl,
+ setReducerFlags: any
+ ) => any;
+ setReducerFlagsConstructor: () => any;
+ versionInfo?: {
+ cliVersion: string;
+ };
+}
diff --git a/sdks/typescript/packages/sdk/src/subscription_builder_impl.ts b/sdks/typescript/packages/sdk/src/subscription_builder_impl.ts
new file mode 100644
index 00000000000..5fffd581237
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/subscription_builder_impl.ts
@@ -0,0 +1,276 @@
+import type { DbConnectionImpl } from './db_connection_impl';
+import type {
+ ErrorContextInterface,
+ SubscriptionEventContextInterface,
+} from './event_context';
+import { EventEmitter } from './event_emitter';
+
+export class SubscriptionBuilderImpl<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> {
+ #onApplied?: (
+ ctx: SubscriptionEventContextInterface
+ ) => void = undefined;
+ #onError?: (
+ ctx: ErrorContextInterface
+ ) => void = undefined;
+ constructor(
+ private db: DbConnectionImpl
+ ) {}
+
+ /**
+ * Registers `callback` to run when this query is successfully added to our subscribed set,
+ * I.e. when its `SubscriptionApplied` message is received.
+ *
+ * The database state exposed via the `&EventContext` argument
+ * includes all the rows added to the client cache as a result of the new subscription.
+ *
+ * The event in the `&EventContext` argument is `Event::SubscribeApplied`.
+ *
+ * Multiple `on_applied` callbacks for the same query may coexist.
+ * No mechanism for un-registering `on_applied` callbacks is exposed.
+ *
+ * @param cb - Callback to run when the subscription is applied.
+ * @returns The current `SubscriptionBuilder` instance.
+ */
+ onApplied(
+ cb: (
+ ctx: SubscriptionEventContextInterface
+ ) => void
+ ): SubscriptionBuilderImpl {
+ this.#onApplied = cb;
+ return this;
+ }
+
+ /**
+ * Registers `callback` to run when this query either:
+ * - Fails to be added to our subscribed set.
+ * - Is unexpectedly removed from our subscribed set.
+ *
+ * If the subscription had previously started and has been unexpectedly removed,
+ * the database state exposed via the `&EventContext` argument contains no rows
+ * from any subscriptions removed within the same error event.
+ * As proposed, it must therefore contain no rows.
+ *
+ * The event in the `&EventContext` argument is `Event::SubscribeError`,
+ * containing a dynamic error object with a human-readable description of the error
+ * for diagnostic purposes.
+ *
+ * Multiple `on_error` callbacks for the same query may coexist.
+ * No mechanism for un-registering `on_error` callbacks is exposed.
+ *
+ * @param cb - Callback to run when there is an error in subscription.
+ * @returns The current `SubscriptionBuilder` instance.
+ */
+ onError(
+ cb: (ctx: ErrorContextInterface) => void
+ ): SubscriptionBuilderImpl {
+ this.#onError = cb;
+ return this;
+ }
+
+ /**
+ * Subscribe to a single query. The results of the query will be merged into the client
+ * cache and deduplicated on the client.
+ *
+ * @param query_sql A `SQL` query to subscribe to.
+ *
+ * @example
+ *
+ * ```ts
+ * const subscription = connection.subscriptionBuilder().onApplied(() => {
+ * console.log("SDK client cache initialized.");
+ * }).subscribe("SELECT * FROM User");
+ *
+ * subscription.unsubscribe();
+ * ```
+ */
+ subscribe(
+ query_sql: string | string[]
+ ): SubscriptionHandleImpl {
+ const queries = Array.isArray(query_sql) ? query_sql : [query_sql];
+ if (queries.length === 0) {
+ throw new Error('Subscriptions must have at least one query');
+ }
+ return new SubscriptionHandleImpl(
+ this.db,
+ queries,
+ this.#onApplied,
+ this.#onError
+ );
+ }
+
+ /**
+ * Subscribes to all rows from all tables.
+ *
+ * This method is intended as a convenience
+ * for applications where client-side memory use and network bandwidth are not concerns.
+ * Applications where these resources are a constraint
+ * should register more precise queries via `subscribe`
+ * in order to replicate only the subset of data which the client needs to function.
+ *
+ * This method should not be combined with `subscribe` on the same `DbConnection`.
+ * A connection may either `subscribe` to particular queries,
+ * or `subscribeToAllTables`, but not both.
+ * Attempting to call `subscribe`
+ * on a `DbConnection` that has previously used `subscribeToAllTables`,
+ * or vice versa, may misbehave in any number of ways,
+ * including dropping subscriptions, corrupting the client cache, or throwing errors.
+ */
+ subscribeToAllTables(): void {
+ this.subscribe('SELECT * FROM *');
+ }
+}
+
+export type SubscribeEvent = 'applied' | 'error' | 'end';
+
+export class SubscriptionManager {
+ subscriptions: Map<
+ number,
+ { handle: SubscriptionHandleImpl; emitter: EventEmitter }
+ > = new Map();
+}
+
+export class SubscriptionHandleImpl<
+ DBView = any,
+ Reducers = any,
+ SetReducerFlags = any,
+> {
+ #queryId: number;
+ #unsubscribeCalled: boolean = false;
+ #endedState: boolean = false;
+ #activeState: boolean = false;
+ #emitter: EventEmitter void> =
+ new EventEmitter();
+
+ constructor(
+ private db: DbConnectionImpl,
+ querySql: string[],
+ onApplied?: (
+ ctx: SubscriptionEventContextInterface
+ ) => void,
+ onError?: (
+ ctx: ErrorContextInterface,
+ error: Error
+ ) => void
+ ) {
+ this.#emitter.on(
+ 'applied',
+ (
+ ctx: SubscriptionEventContextInterface<
+ DBView,
+ Reducers,
+ SetReducerFlags
+ >
+ ) => {
+ this.#activeState = true;
+ if (onApplied) {
+ onApplied(ctx);
+ }
+ }
+ );
+ this.#emitter.on(
+ 'error',
+ (
+ ctx: ErrorContextInterface,
+ error: Error
+ ) => {
+ this.#activeState = false;
+ this.#endedState = true;
+ if (onError) {
+ onError(ctx, error);
+ }
+ }
+ );
+ this.#queryId = this.db.registerSubscription(this, this.#emitter, querySql);
+ }
+
+ /**
+ * Consumes self and issues an `Unsubscribe` message,
+ * removing this query from the client's set of subscribed queries.
+ * It is only valid to call this method if `is_active()` is `true`.
+ */
+ unsubscribe(): void {
+ if (this.#unsubscribeCalled) {
+ throw new Error('Unsubscribe has already been called');
+ }
+ this.#unsubscribeCalled = true;
+ this.db.unregisterSubscription(this.#queryId);
+ this.#emitter.on(
+ 'end',
+ (
+ _ctx: SubscriptionEventContextInterface<
+ DBView,
+ Reducers,
+ SetReducerFlags
+ >
+ ) => {
+ this.#endedState = true;
+ this.#activeState = false;
+ }
+ );
+ }
+
+ /**
+ * Unsubscribes and also registers a callback to run upon success.
+ * I.e. when an `UnsubscribeApplied` message is received.
+ *
+ * If `Unsubscribe` returns an error,
+ * or if the `on_error` callback(s) are invoked before this subscription would end normally,
+ * the `on_end` callback is not invoked.
+ *
+ * @param onEnd - Callback to run upon successful unsubscribe.
+ */
+ unsubscribeThen(
+ onEnd: (
+ ctx: SubscriptionEventContextInterface
+ ) => void
+ ): void {
+ if (this.#endedState) {
+ throw new Error('Subscription has already ended');
+ }
+ if (this.#unsubscribeCalled) {
+ throw new Error('Unsubscribe has already been called');
+ }
+ this.#unsubscribeCalled = true;
+ this.db.unregisterSubscription(this.#queryId);
+ this.#emitter.on(
+ 'end',
+ (
+ ctx: SubscriptionEventContextInterface<
+ DBView,
+ Reducers,
+ SetReducerFlags
+ >
+ ) => {
+ this.#endedState = true;
+ this.#activeState = false;
+ onEnd(ctx);
+ }
+ );
+ }
+
+ /**
+ * True if this `SubscriptionHandle` has ended,
+ * either due to an error or a call to `unsubscribe`.
+ *
+ * This is initially false, and becomes true when either the `on_end` or `on_error` callback is invoked.
+ * A subscription which has not yet been applied is not active, but is also not ended.
+ */
+ isEnded(): boolean {
+ return this.#endedState;
+ }
+
+ /**
+ * True if this `SubscriptionHandle` is active, meaning it has been successfully applied
+ * and has not since ended, either due to an error or a complete `unsubscribe` request-response pair.
+ *
+ * This corresponds exactly to the interval bounded at the start by the `on_applied` callback
+ * and at the end by either the `on_end` or `on_error` callback.
+ */
+ isActive(): boolean {
+ return this.#activeState;
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/table_cache.ts b/sdks/typescript/packages/sdk/src/table_cache.ts
new file mode 100644
index 00000000000..8763c2189f6
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/table_cache.ts
@@ -0,0 +1,326 @@
+import { EventEmitter } from './event_emitter.ts';
+import type { TableRuntimeTypeInfo } from './spacetime_module.ts';
+
+import {
+ BinaryWriter,
+ type EventContextInterface,
+} from './db_connection_impl.ts';
+import { stdbLogger } from './logger.ts';
+import type { ComparablePrimitive } from './algebraic_type.ts';
+
+export type Operation = {
+ type: 'insert' | 'delete';
+ // For tables with a primary key, this is the primary key value, as a primitive or string.
+ // Otherwise, it is an encoding of the full row.
+ rowId: ComparablePrimitive;
+ // TODO: Refine this type to at least reflect that it is a product.
+ row: any;
+};
+
+export type TableUpdate = {
+ tableName: string;
+ operations: Operation[];
+};
+
+export type PendingCallback = {
+ type: 'insert' | 'delete' | 'update';
+ table: string;
+ cb: () => void;
+};
+/**
+ * Builder to generate calls to query a `table` in the database
+ */
+export class TableCache {
+ private rows: Map;
+ private tableTypeInfo: TableRuntimeTypeInfo;
+ private emitter: EventEmitter<'insert' | 'delete' | 'update'>;
+
+ /**
+ * @param name the table name
+ * @param primaryKeyCol column index designated as `#[primarykey]`
+ * @param primaryKey column name designated as `#[primarykey]`
+ * @param entityClass the entityClass
+ */
+ constructor(tableTypeInfo: TableRuntimeTypeInfo) {
+ this.tableTypeInfo = tableTypeInfo;
+ this.rows = new Map();
+ this.emitter = new EventEmitter();
+ }
+
+ /**
+ * @returns number of rows in the table
+ */
+ count(): number {
+ return this.rows.size;
+ }
+
+ /**
+ * @returns The values of the rows in the table
+ */
+ iter(): any[] {
+ return Array.from(this.rows.values()).map(([row]) => row);
+ }
+
+ applyOperations = (
+ operations: Operation[],
+ ctx: EventContextInterface
+ ): PendingCallback[] => {
+ const pendingCallbacks: PendingCallback[] = [];
+ if (this.tableTypeInfo.primaryKeyInfo !== undefined) {
+ const insertMap = new Map();
+ const deleteMap = new Map();
+ for (const op of operations) {
+ if (op.type === 'insert') {
+ const [_, prevCount] = insertMap.get(op.rowId) || [op, 0];
+ insertMap.set(op.rowId, [op, prevCount + 1]);
+ } else {
+ const [_, prevCount] = deleteMap.get(op.rowId) || [op, 0];
+ deleteMap.set(op.rowId, [op, prevCount + 1]);
+ }
+ }
+ for (const [primaryKey, [insertOp, refCount]] of insertMap) {
+ const deleteEntry = deleteMap.get(primaryKey);
+ if (deleteEntry) {
+ const [_, deleteCount] = deleteEntry;
+ // In most cases the refCountDelta will be either 0 or refCount, but if
+ // an update moves a row in or out of the result set of different queries, then
+ // other deltas are possible.
+ const refCountDelta = refCount - deleteCount;
+ const maybeCb = this.update(
+ ctx,
+ primaryKey,
+ insertOp.row,
+ refCountDelta
+ );
+ if (maybeCb) {
+ pendingCallbacks.push(maybeCb);
+ }
+ deleteMap.delete(primaryKey);
+ } else {
+ const maybeCb = this.insert(ctx, insertOp, refCount);
+ if (maybeCb) {
+ pendingCallbacks.push(maybeCb);
+ }
+ }
+ }
+ for (const [deleteOp, refCount] of deleteMap.values()) {
+ const maybeCb = this.delete(ctx, deleteOp, refCount);
+ if (maybeCb) {
+ pendingCallbacks.push(maybeCb);
+ }
+ }
+ } else {
+ for (const op of operations) {
+ if (op.type === 'insert') {
+ const maybeCb = this.insert(ctx, op);
+ if (maybeCb) {
+ pendingCallbacks.push(maybeCb);
+ }
+ } else {
+ const maybeCb = this.delete(ctx, op);
+ if (maybeCb) {
+ pendingCallbacks.push(maybeCb);
+ }
+ }
+ }
+ }
+ return pendingCallbacks;
+ };
+
+ update = (
+ ctx: EventContextInterface,
+ rowId: ComparablePrimitive,
+ newRow: RowType,
+ refCountDelta: number = 0
+ ): PendingCallback | undefined => {
+ const existingEntry = this.rows.get(rowId);
+ if (!existingEntry) {
+ // TODO: this should throw an error and kill the connection.
+ stdbLogger(
+ 'error',
+ `Updating a row that was not present in the cache. Table: ${this.tableTypeInfo.tableName}, RowId: ${rowId}`
+ );
+ return undefined;
+ }
+ const [oldRow, previousCount] = existingEntry;
+ const refCount = Math.max(1, previousCount + refCountDelta);
+ if (previousCount + refCountDelta <= 0) {
+ stdbLogger(
+ 'error',
+ `Negative reference count for in table ${this.tableTypeInfo.tableName} row ${rowId} (${previousCount} + ${refCountDelta})`
+ );
+ return undefined;
+ }
+ this.rows.set(rowId, [newRow, refCount]);
+ // This indicates something is wrong, so we could arguably crash here.
+ if (previousCount === 0) {
+ stdbLogger(
+ 'error',
+ `Updating a row id in table ${this.tableTypeInfo.tableName} which was not present in the cache (rowId: ${rowId})`
+ );
+ return {
+ type: 'insert',
+ table: this.tableTypeInfo.tableName,
+ cb: () => {
+ this.emitter.emit('insert', ctx, newRow);
+ },
+ };
+ }
+ return {
+ type: 'update',
+ table: this.tableTypeInfo.tableName,
+ cb: () => {
+ this.emitter.emit('update', ctx, oldRow, newRow);
+ },
+ };
+ };
+
+ insert = (
+ ctx: EventContextInterface,
+ operation: Operation,
+ count: number = 1
+ ): PendingCallback | undefined => {
+ const [_, previousCount] = this.rows.get(operation.rowId) || [
+ operation.row,
+ 0,
+ ];
+ this.rows.set(operation.rowId, [operation.row, previousCount + count]);
+ if (previousCount === 0) {
+ return {
+ type: 'insert',
+ table: this.tableTypeInfo.tableName,
+ cb: () => {
+ this.emitter.emit('insert', ctx, operation.row);
+ },
+ };
+ }
+ // It's possible to get a duplicate insert because rows can be returned from multiple queries.
+ return undefined;
+ };
+
+ delete = (
+ ctx: EventContextInterface,
+ operation: Operation,
+ count: number = 1
+ ): PendingCallback | undefined => {
+ const [_, previousCount] = this.rows.get(operation.rowId) || [
+ operation.row,
+ 0,
+ ];
+ // This should never happen.
+ if (previousCount === 0) {
+ stdbLogger('warn', 'Deleting a row that was not present in the cache');
+ return undefined;
+ }
+ // If this was the last reference, we are actually deleting the row.
+ if (previousCount <= count) {
+ // TODO: Log a warning/error if previousCount is less than count.
+ this.rows.delete(operation.rowId);
+ return {
+ type: 'delete',
+ table: this.tableTypeInfo.tableName,
+ cb: () => {
+ this.emitter.emit('delete', ctx, operation.row);
+ },
+ };
+ }
+ this.rows.set(operation.rowId, [operation.row, previousCount - count]);
+ return undefined;
+ };
+
+ /**
+ * Register a callback for when a row is newly inserted into the database.
+ *
+ * ```ts
+ * User.onInsert((user, reducerEvent) => {
+ * if (reducerEvent) {
+ * console.log("New user on reducer", reducerEvent, user);
+ * } else {
+ * console.log("New user received during subscription update on insert", user);
+ * }
+ * });
+ * ```
+ *
+ * @param cb Callback to be called when a new row is inserted
+ */
+ onInsert = (
+ cb: (ctx: EventContext, row: RowType) => void
+ ): void => {
+ this.emitter.on('insert', cb);
+ };
+
+ /**
+ * Register a callback for when a row is deleted from the database.
+ *
+ * ```ts
+ * User.onDelete((user, reducerEvent) => {
+ * if (reducerEvent) {
+ * console.log("Deleted user on reducer", reducerEvent, user);
+ * } else {
+ * console.log("Deleted user received during subscription update on update", user);
+ * }
+ * });
+ * ```
+ *
+ * @param cb Callback to be called when a new row is inserted
+ */
+ onDelete = (
+ cb: (ctx: EventContext, row: RowType) => void
+ ): void => {
+ this.emitter.on('delete', cb);
+ };
+
+ /**
+ * Register a callback for when a row is updated into the database.
+ *
+ * ```ts
+ * User.onInsert((user, reducerEvent) => {
+ * if (reducerEvent) {
+ * console.log("Updated user on reducer", reducerEvent, user);
+ * } else {
+ * console.log("Updated user received during subscription update on delete", user);
+ * }
+ * });
+ * ```
+ *
+ * @param cb Callback to be called when a new row is inserted
+ */
+ onUpdate = (
+ cb: (ctx: EventContext, oldRow: RowType, row: RowType) => void
+ ): void => {
+ this.emitter.on('update', cb);
+ };
+
+ /**
+ * Remove a callback for when a row is newly inserted into the database.
+ *
+ * @param cb Callback to be removed
+ */
+ removeOnInsert = (
+ cb: (ctx: EventContext, row: RowType) => void
+ ): void => {
+ this.emitter.off('insert', cb);
+ };
+
+ /**
+ * Remove a callback for when a row is deleted from the database.
+ *
+ * @param cb Callback to be removed
+ */
+ removeOnDelete = (
+ cb: (ctx: EventContext, row: RowType) => void
+ ): void => {
+ this.emitter.off('delete', cb);
+ };
+
+ /**
+ * Remove a callback for when a row is updated into the database.
+ *
+ * @param cb Callback to be removed
+ */
+ removeOnUpdate = (
+ cb: (ctx: EventContext, oldRow: RowType, row: RowType) => void
+ ): void => {
+ this.emitter.off('update', cb);
+ };
+}
diff --git a/sdks/typescript/packages/sdk/src/time_duration.ts b/sdks/typescript/packages/sdk/src/time_duration.ts
new file mode 100644
index 00000000000..6179ccd3140
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/time_duration.ts
@@ -0,0 +1,24 @@
+/**
+ * A difference between two points in time, represented as a number of microseconds.
+ */
+export class TimeDuration {
+ __time_duration_micros__: bigint;
+
+ private static MICROS_PER_MILLIS: bigint = 1000n;
+
+ get micros(): bigint {
+ return this.__time_duration_micros__;
+ }
+
+ get millis(): number {
+ return Number(this.micros / TimeDuration.MICROS_PER_MILLIS);
+ }
+
+ constructor(micros: bigint) {
+ this.__time_duration_micros__ = micros;
+ }
+
+ static fromMillis(millis: number): TimeDuration {
+ return new TimeDuration(BigInt(millis) * TimeDuration.MICROS_PER_MILLIS);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/timestamp.ts b/sdks/typescript/packages/sdk/src/timestamp.ts
new file mode 100644
index 00000000000..6a18fd131b7
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/timestamp.ts
@@ -0,0 +1,57 @@
+/**
+ * A point in time, represented as a number of microseconds since the Unix epoch.
+ */
+export class Timestamp {
+ __timestamp_micros_since_unix_epoch__: bigint;
+
+ private static MICROS_PER_MILLIS: bigint = 1000n;
+
+ get microsSinceUnixEpoch(): bigint {
+ return this.__timestamp_micros_since_unix_epoch__;
+ }
+
+ constructor(micros: bigint) {
+ this.__timestamp_micros_since_unix_epoch__ = micros;
+ }
+
+ /**
+ * The Unix epoch, the midnight at the beginning of January 1, 1970, UTC.
+ */
+ static UNIX_EPOCH: Timestamp = new Timestamp(0n);
+
+ /**
+ * Get a `Timestamp` representing the execution environment's belief of the current moment in time.
+ */
+ static now(): Timestamp {
+ return Timestamp.fromDate(new Date());
+ }
+
+ /**
+ * Get a `Timestamp` representing the same point in time as `date`.
+ */
+ static fromDate(date: Date): Timestamp {
+ const millis = date.getTime();
+ const micros = BigInt(millis) * Timestamp.MICROS_PER_MILLIS;
+ return new Timestamp(micros);
+ }
+
+ /**
+ * Get a `Date` representing approximately the same point in time as `this`.
+ *
+ * This method truncates to millisecond precision,
+ * and throws `RangeError` if the `Timestamp` is outside the range representable as a `Date`.
+ */
+ toDate(): Date {
+ const micros = this.__timestamp_micros_since_unix_epoch__;
+ const millis = micros / Timestamp.MICROS_PER_MILLIS;
+ if (
+ millis > BigInt(Number.MAX_SAFE_INTEGER) ||
+ millis < BigInt(Number.MIN_SAFE_INTEGER)
+ ) {
+ throw new RangeError(
+ "Timestamp is outside of the representable range of JS's Date"
+ );
+ }
+ return new Date(Number(millis));
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/utils.ts b/sdks/typescript/packages/sdk/src/utils.ts
new file mode 100644
index 00000000000..4ab3b4609a7
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/utils.ts
@@ -0,0 +1,101 @@
+import BinaryReader from './binary_reader';
+import BinaryWriter from './binary_writer';
+
+export function toPascalCase(s: string): string {
+ const str = s.replace(/([-_][a-z])/gi, $1 => {
+ return $1.toUpperCase().replace('-', '').replace('_', '');
+ });
+
+ return str.charAt(0).toUpperCase() + str.slice(1);
+}
+
+export function deepEqual(obj1: any, obj2: any): boolean {
+ // If both are strictly equal (covers primitives and reference equality), return true
+ if (obj1 === obj2) return true;
+
+ // If either is a primitive type or one is null, return false since we already checked for strict equality
+ if (
+ typeof obj1 !== 'object' ||
+ obj1 === null ||
+ typeof obj2 !== 'object' ||
+ obj2 === null
+ ) {
+ return false;
+ }
+
+ // Get keys of both objects
+ const keys1 = Object.keys(obj1);
+ const keys2 = Object.keys(obj2);
+
+ // If number of keys is different, return false
+ if (keys1.length !== keys2.length) return false;
+
+ // Check all keys and compare values recursively
+ for (let key of keys1) {
+ if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+export function uint8ArrayToHexString(array: Uint8Array): string {
+ return Array.prototype.map
+ .call(array.reverse(), x => ('00' + x.toString(16)).slice(-2))
+ .join('');
+}
+
+export function uint8ArrayToU128(array: Uint8Array): bigint {
+ if (array.length != 16) {
+ throw new Error(`Uint8Array is not 16 bytes long: ${array}`);
+ }
+ return new BinaryReader(array).readU128();
+}
+
+export function uint8ArrayToU256(array: Uint8Array): bigint {
+ if (array.length != 32) {
+ throw new Error(`Uint8Array is not 32 bytes long: [${array}]`);
+ }
+ return new BinaryReader(array).readU256();
+}
+
+export function hexStringToUint8Array(str: string): Uint8Array {
+ if (str.startsWith('0x')) {
+ str = str.slice(2);
+ }
+ let matches = str.match(/.{1,2}/g) || [];
+ let data = Uint8Array.from(matches.map((byte: string) => parseInt(byte, 16)));
+ if (data.length != 32) {
+ return new Uint8Array(0);
+ }
+ return data.reverse();
+}
+
+export function hexStringToU128(str: string): bigint {
+ return uint8ArrayToU128(hexStringToUint8Array(str));
+}
+
+export function hexStringToU256(str: string): bigint {
+ return uint8ArrayToU256(hexStringToUint8Array(str));
+}
+
+export function u128ToUint8Array(data: bigint): Uint8Array {
+ let writer = new BinaryWriter(16);
+ writer.writeU128(data);
+ return writer.getBuffer();
+}
+
+export function u128ToHexString(data: bigint): string {
+ return uint8ArrayToHexString(u128ToUint8Array(data));
+}
+
+export function u256ToUint8Array(data: bigint): Uint8Array {
+ let writer = new BinaryWriter(32);
+ writer.writeU256(data);
+ return writer.getBuffer();
+}
+
+export function u256ToHexString(data: bigint): string {
+ return uint8ArrayToHexString(u256ToUint8Array(data));
+}
diff --git a/sdks/typescript/packages/sdk/src/version.ts b/sdks/typescript/packages/sdk/src/version.ts
new file mode 100644
index 00000000000..9221a121994
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/version.ts
@@ -0,0 +1,135 @@
+export type PrereleaseId = string | number;
+
+export type PreRelease = PrereleaseId[];
+
+// Compare pre-release identifiers according to the semver spec (https://semver.org/#spec-item-11).
+function comparePreReleases(a: PreRelease, b: PreRelease): number {
+ const len = Math.min(a.length, b.length);
+ for (let i = 0; i < len; i++) {
+ const aPart = a[i];
+ const bPart = b[i];
+ if (aPart === bPart) continue;
+ if (typeof aPart === 'number' && typeof bPart === 'number') {
+ return aPart - bPart;
+ }
+ if (typeof aPart === 'string' && typeof bPart === 'string') {
+ return aPart.localeCompare(bPart);
+ }
+ // According to item 11.4.3, numeric identifiers always have lower precedence than non-numeric identifiers.
+ // So if `a` is a string, it has higher precedence than `b`.
+ return typeof aPart === 'string' ? 1 : -1;
+ }
+ // See rule 11.4.4 in the semver spec.
+ return a.length - b.length;
+}
+
+// We don't use these, and they don't matter for version ordering, so I'm not going to parse it to spec.
+export type BuildInfo = string;
+
+// This is exported for tests.
+export class SemanticVersion {
+ major: number;
+ minor: number;
+ patch: number;
+ preRelease: PreRelease | null;
+ buildInfo: BuildInfo | null;
+
+ constructor(
+ major: number,
+ minor: number,
+ patch: number,
+ preRelease: PreRelease | null = null,
+ buildInfo: BuildInfo | null = null
+ ) {
+ this.major = major;
+ this.minor = minor;
+ this.patch = patch;
+ this.preRelease = preRelease;
+ this.buildInfo = buildInfo;
+ }
+
+ toString(): string {
+ let versionString = `${this.major}.${this.minor}.${this.patch}`;
+ if (this.preRelease) {
+ versionString += `-${this.preRelease.join('.')}`;
+ }
+ if (this.buildInfo) {
+ versionString += `+${this.buildInfo}`;
+ }
+ return versionString;
+ }
+
+ compare(other: SemanticVersion): number {
+ if (this.major !== other.major) {
+ return this.major - other.major;
+ }
+ if (this.minor !== other.minor) {
+ return this.minor - other.minor;
+ }
+ if (this.patch !== other.patch) {
+ return this.patch - other.patch;
+ }
+ if (this.preRelease && other.preRelease) {
+ return comparePreReleases(this.preRelease, other.preRelease);
+ }
+ if (this.preRelease) {
+ return -1; // The version without a pre-release is greater.
+ }
+ if (other.preRelease) {
+ return -1; // Since we don't have a pre-release, this version is greater.
+ }
+ return 0; // versions are equal
+ }
+
+ clone(): SemanticVersion {
+ return new SemanticVersion(
+ this.major,
+ this.minor,
+ this.patch,
+ this.preRelease ? [...this.preRelease] : null,
+ this.buildInfo
+ );
+ }
+
+ static parseVersionString(version: string): SemanticVersion {
+ const regex =
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?(?:\+([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?$/;
+ const match = version.match(regex);
+ if (!match) {
+ throw new Error(`Invalid version string: ${version}`);
+ }
+
+ const major = parseInt(match[1], 10);
+ const minor = parseInt(match[2], 10);
+ const patch = parseInt(match[3], 10);
+ const preRelease = match[4]
+ ? match[4].split('.').map(id => (isNaN(Number(id)) ? id : Number(id)))
+ : null;
+ const buildInfo = match[5] || null;
+
+ return new SemanticVersion(major, minor, patch, preRelease, buildInfo);
+ }
+}
+
+// The SDK depends on some module information that was not generated before this version.
+export const _MINIMUM_CLI_VERSION: SemanticVersion = new SemanticVersion(
+ 1,
+ 2,
+ 0
+);
+
+export function ensureMinimumVersionOrThrow(versionString?: string): void {
+ if (versionString === undefined) {
+ throw new Error(versionErrorMessage(versionString));
+ }
+ const version = SemanticVersion.parseVersionString(versionString);
+ if (version.compare(_MINIMUM_CLI_VERSION) < 0) {
+ throw new Error(versionErrorMessage(versionString));
+ }
+}
+
+function versionErrorMessage(incompatibleVersion?: string): string {
+ const badVersion =
+ incompatibleVersion === undefined ? 'unknown' : incompatibleVersion;
+ return `Module code was generated with an incompatible version of the spacetimedb cli (${incompatibleVersion}). Update the cli version to at least ${_MINIMUM_CLI_VERSION.toString()} and regenerate the bindings. You can upgrade to the latest cli version by running: spacetime version upgrade`;
+}
diff --git a/sdks/typescript/packages/sdk/src/websocket_decompress_adapter.ts b/sdks/typescript/packages/sdk/src/websocket_decompress_adapter.ts
new file mode 100644
index 00000000000..fbd1d0faf84
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/websocket_decompress_adapter.ts
@@ -0,0 +1,128 @@
+import { decompress } from './decompress';
+
+export class WebsocketDecompressAdapter {
+ onclose?: (...ev: any[]) => void;
+ onopen?: (...ev: any[]) => void;
+ onmessage?: (msg: { data: Uint8Array }) => void;
+ onerror?: (msg: ErrorEvent) => void;
+
+ #ws: WebSocket;
+
+ async #handleOnMessage(msg: MessageEvent) {
+ const buffer = new Uint8Array(msg.data);
+ let decompressed: Uint8Array;
+
+ if (buffer[0] === 0) {
+ decompressed = buffer.slice(1);
+ } else if (buffer[0] === 1) {
+ throw new Error(
+ 'Brotli Compression not supported. Please use gzip or none compression in withCompression method on DbConnection.'
+ );
+ } else if (buffer[0] === 2) {
+ decompressed = await decompress(buffer.slice(1), 'gzip');
+ } else {
+ throw new Error(
+ 'Unexpected Compression Algorithm. Please use `gzip` or `none`'
+ );
+ }
+
+ this.onmessage?.({ data: decompressed });
+ }
+
+ #handleOnOpen(msg: any) {
+ this.onopen?.(msg);
+ }
+
+ #handleOnError(msg: any) {
+ this.onerror?.(msg);
+ }
+
+ send(msg: any): void {
+ this.#ws.send(msg);
+ }
+
+ close(): void {
+ this.#ws.close();
+ }
+
+ constructor(ws: WebSocket) {
+ this.onmessage = undefined;
+ this.onopen = undefined;
+ this.onmessage = undefined;
+ this.onerror = undefined;
+
+ ws.onmessage = this.#handleOnMessage.bind(this);
+ ws.onerror = this.#handleOnError.bind(this);
+ ws.onclose = this.#handleOnError.bind(this);
+ ws.onopen = this.#handleOnOpen.bind(this);
+
+ ws.binaryType = 'arraybuffer';
+
+ this.#ws = ws;
+ }
+
+ static async createWebSocketFn({
+ url,
+ nameOrAddress,
+ wsProtocol,
+ authToken,
+ compression,
+ lightMode,
+ }: {
+ url: URL;
+ wsProtocol: string;
+ nameOrAddress: string;
+ authToken?: string;
+ compression: 'gzip' | 'none';
+ lightMode: boolean;
+ }): Promise {
+ const headers = new Headers();
+
+ let WS: typeof WebSocket;
+
+ // @ts-ignore
+ if (import.meta.env.BROWSER === 'false') {
+ WS =
+ 'WebSocket' in globalThis
+ ? WebSocket
+ : ((await import('undici')).WebSocket as unknown as typeof WebSocket);
+ } else {
+ WS = WebSocket;
+ }
+
+ // We swap our original token to a shorter-lived token
+ // to avoid sending the original via query params.
+ let temporaryAuthToken: string | undefined = undefined;
+ if (authToken) {
+ headers.set('Authorization', `Bearer ${authToken}`);
+ const tokenUrl = new URL('v1/identity/websocket-token', url);
+ tokenUrl.protocol = url.protocol === 'wss:' ? 'https:' : 'http:';
+
+ const response = await fetch(tokenUrl, { method: 'POST', headers });
+ if (response.ok) {
+ const { token } = await response.json();
+ temporaryAuthToken = token;
+ } else {
+ return Promise.reject(
+ new Error(`Failed to verify token: ${response.statusText}`)
+ );
+ }
+ }
+
+ const databaseUrl = new URL(`v1/database/${nameOrAddress}/subscribe`, url);
+ if (temporaryAuthToken) {
+ databaseUrl.searchParams.set('token', temporaryAuthToken);
+ }
+ databaseUrl.searchParams.set(
+ 'compression',
+ compression === 'gzip' ? 'Gzip' : 'None'
+ );
+ if (lightMode) {
+ databaseUrl.searchParams.set('light', 'true');
+ }
+
+ const ws = new WS(databaseUrl.toString(), wsProtocol);
+
+ return new WebsocketDecompressAdapter(ws);
+ }
+}
diff --git a/sdks/typescript/packages/sdk/src/websocket_test_adapter.ts b/sdks/typescript/packages/sdk/src/websocket_test_adapter.ts
new file mode 100644
index 00000000000..fe151c89d91
--- /dev/null
+++ b/sdks/typescript/packages/sdk/src/websocket_test_adapter.ts
@@ -0,0 +1,52 @@
+import BinaryWriter from './binary_writer.ts';
+import { ServerMessage } from './client_api/index.ts';
+
+class WebsocketTestAdapter {
+ onclose: any;
+ onopen!: Function;
+ onmessage: any;
+ onerror: any;
+
+ messageQueue: any[];
+ closed: boolean;
+
+ constructor() {
+ this.messageQueue = [];
+ this.closed = false;
+ }
+
+ send(message: any): void {
+ this.messageQueue.push(message);
+ }
+
+ close(): void {
+ this.closed = true;
+ }
+
+ acceptConnection(): void {
+ this.onopen();
+ }
+
+ sendToClient(message: ServerMessage): void {
+ const writer = new BinaryWriter(1024);
+ ServerMessage.getTypeScriptAlgebraicType().serialize(writer, message);
+ const rawBytes = writer.getBuffer();
+ // The brotli library's `compress` is somehow broken: it returns `null` for some inputs.
+ // See https://github.com/foliojs/brotli.js/issues/36, which is closed but not actually fixed.
+ // So we send the uncompressed data here, and in `spacetimedb.ts`,
+ // if compression fails, we treat the raw message as having been uncompressed all along.
+ // const data = compress(rawBytes);
+ this.onmessage({ data: rawBytes });
+ }
+
+ async createWebSocketFn(
+ _url: string,
+ _protocol: string,
+ _params: any
+ ): Promise {
+ return this;
+ }
+}
+
+export type { WebsocketTestAdapter };
+export default WebsocketTestAdapter;
diff --git a/sdks/typescript/packages/sdk/tests/algebraic_value.test.ts b/sdks/typescript/packages/sdk/tests/algebraic_value.test.ts
new file mode 100644
index 00000000000..12e778223ed
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tests/algebraic_value.test.ts
@@ -0,0 +1,170 @@
+import { describe, expect, test } from 'vitest';
+import { AlgebraicType } from '../src/algebraic_type';
+import {
+ AlgebraicValue,
+ BinaryAdapter,
+ ProductValue,
+ SumValue,
+} from '../src/algebraic_value';
+import BinaryReader from '../src/binary_reader';
+import BinaryWriter from '../src/binary_writer';
+
+describe('AlgebraicValue', () => {
+ test('when created with a ProductValue it assigns the product property', () => {
+ let value = new ProductValue([]);
+ let av = new AlgebraicValue(value);
+ expect(av.asProductValue()).toBe(value);
+ });
+
+ test('when created with a SumValue it assigns the sum property', () => {
+ let value = new SumValue(1, new AlgebraicValue(1));
+ let av = new AlgebraicValue(value);
+ expect(av.asSumValue()).toBe(value);
+ });
+
+ test('when created with a AlgebraicValue(string) it can be requested as a string', () => {
+ let av = new AlgebraicValue('foo');
+
+ expect(av.asString()).toBe('foo');
+ });
+
+ test('options handle falsy strings', () => {
+ let stringOptionType = AlgebraicType.createOptionType(
+ AlgebraicType.createStringType()
+ );
+ let writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, '');
+ let parsed = stringOptionType.deserialize(
+ new BinaryReader(writer.getBuffer())
+ );
+ // Make sure we don't turn this into undefined.
+ expect(parsed).toEqual('');
+
+ writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, null);
+ parsed = stringOptionType.deserialize(new BinaryReader(writer.getBuffer()));
+ expect(parsed).toEqual(undefined);
+
+ writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, undefined);
+ parsed = stringOptionType.deserialize(new BinaryReader(writer.getBuffer()));
+ expect(parsed).toEqual(undefined);
+ });
+
+ test('options handle falsy numbers', () => {
+ let stringOptionType = AlgebraicType.createOptionType(
+ AlgebraicType.createU32Type()
+ );
+ let writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, 0);
+ let parsed = stringOptionType.deserialize(
+ new BinaryReader(writer.getBuffer())
+ );
+ // Make sure we don't turn this into undefined.
+ expect(parsed).toEqual(0);
+
+ writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, null);
+ parsed = stringOptionType.deserialize(new BinaryReader(writer.getBuffer()));
+ expect(parsed).toEqual(undefined);
+
+ writer = new BinaryWriter(1024);
+ stringOptionType.serialize(writer, undefined);
+ parsed = stringOptionType.deserialize(new BinaryReader(writer.getBuffer()));
+ expect(parsed).toEqual(undefined);
+ });
+
+ test('when created with a AlgebraicValue(AlgebraicValue[]) it can be requested as an array', () => {
+ let array: AlgebraicValue[] = [new AlgebraicValue(1)];
+ let av = new AlgebraicValue(array);
+ expect(av.asArray()).toBe(array);
+ });
+
+ describe('deserialize with a binary adapter', () => {
+ test('should correctly deserialize array with U8 type', () => {
+ const input = new Uint8Array([2, 0, 0, 0, 10, 20]);
+ const reader = new BinaryReader(input);
+ const adapter: BinaryAdapter = new BinaryAdapter(reader);
+ const type = AlgebraicType.createBytesType();
+
+ const result = AlgebraicValue.deserialize(type, adapter);
+
+ expect(result.asBytes()).toEqual(new Uint8Array([10, 20]));
+ });
+
+ test('should correctly deserialize array with U128 type', () => {
+ // byte array of length 0002
+ // prettier-ignore
+ const input = new Uint8Array([
+ 3, 0, 0, 0, // 4 bytes for length
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 bytes for u128
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 16 bytes for max u128
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 bytes for u128
+ ]);
+ const reader = new BinaryReader(input);
+ const adapter: BinaryAdapter = new BinaryAdapter(reader);
+ const type = AlgebraicType.createArrayType(
+ AlgebraicType.createU128Type()
+ );
+
+ const result = AlgebraicValue.deserialize(type, adapter);
+
+ const u128_max = BigInt(2) ** BigInt(128) - BigInt(1);
+ expect(result.asArray().map(e => e.asBigInt())).toEqual([
+ BigInt(1),
+ u128_max,
+ BigInt(10),
+ ]);
+ });
+
+ test('should correctly deserialize an U128 type', () => {
+ // byte array of length 0002
+ // prettier-ignore
+ const input = new Uint8Array([
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 16 bytes for max u128
+ ]);
+ const reader = new BinaryReader(input);
+ const adapter: BinaryAdapter = new BinaryAdapter(reader);
+ const result = AlgebraicValue.deserialize(
+ AlgebraicType.createU128Type(),
+ adapter
+ );
+
+ const u128_max = BigInt(2) ** BigInt(128) - BigInt(1);
+ expect(result.asBigInt()).toEqual(u128_max);
+ });
+
+ test('should correctly deserialize a boolean type', () => {
+ // byte array of length 0002
+ const input = new Uint8Array([1]);
+ const reader = new BinaryReader(input);
+ const adapter: BinaryAdapter = new BinaryAdapter(reader);
+ const result = AlgebraicValue.deserialize(
+ AlgebraicType.createBoolType(),
+ adapter
+ );
+
+ expect(result.asBoolean()).toEqual(true);
+ });
+
+ test('should correctly deserialize a string type', () => {
+ // byte array of length 0002
+ const text = 'zażółć gęślą jaźń';
+ const encoder = new TextEncoder();
+ const textBytes = encoder.encode(text);
+
+ const input = new Uint8Array(textBytes.length + 4);
+ input.set(new Uint8Array([textBytes.length, 0, 0, 0]));
+ input.set(textBytes, 4);
+
+ const reader = new BinaryReader(input);
+ const adapter: BinaryAdapter = new BinaryAdapter(reader);
+ const result = AlgebraicValue.deserialize(
+ AlgebraicType.createStringType(),
+ adapter
+ );
+
+ expect(result.asString()).toEqual('zażółć gęślą jaźń');
+ });
+ });
+});
diff --git a/sdks/typescript/packages/sdk/tests/binary_read_write.test.ts b/sdks/typescript/packages/sdk/tests/binary_read_write.test.ts
new file mode 100644
index 00000000000..993f6675a9d
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tests/binary_read_write.test.ts
@@ -0,0 +1,111 @@
+import { describe, expect, test } from 'vitest';
+import BinaryReader from '../src/binary_reader';
+import BinaryWriter from '../src/binary_writer';
+
+/*
+// Generated by the following Rust code:
+
+#[cfg(test)]
+mod tests {
+ use rand::{thread_rng, Rng};
+ use spacetimedb_sats::{bsatn, i256, u256};
+
+ #[test]
+ fn make_some_numbers() {
+ let mut rng = thread_rng();
+ let v_u8: u8 = rng.gen();
+ let v_u16: u16 = rng.gen();
+ let v_u32: u32 = rng.gen();
+ let v_u64: u64 = rng.gen();
+ let v_u128: u128 = rng.gen();
+ let v_u256: u256 = u256::from_words(rng.gen(), rng.gen());
+ let v_i8: i8 = rng.gen();
+ let v_i16: i16 = rng.gen();
+ let v_i32: i32 = rng.gen();
+ let v_i64: i64 = rng.gen();
+ let v_i128: i128 = rng.gen();
+ let v_i256: i256 = i256::from_words(rng.gen(), rng.gen());
+
+ println!("['I8', {}, {:?}],", v_i8, bsatn::to_vec(&v_i8).unwrap());
+ println!("['I16', {}, {:?}],", v_i16, bsatn::to_vec(&v_i16).unwrap());
+ println!("['I32', {}, {:?}],", v_i32, bsatn::to_vec(&v_i32).unwrap());
+ println!("['I64', BigInt('{}'), {:?}],", v_i64, bsatn::to_vec(&v_i64).unwrap());
+ println!("['I128', BigInt('{}'), {:?}],", v_i128, bsatn::to_vec(&v_i128).unwrap());
+ println!("['I256', BigInt('{}'), {:?}],", v_i256, bsatn::to_vec(&v_i256).unwrap());
+
+ println!("['U8', {}, {:?}],", v_u8, bsatn::to_vec(&v_u8).unwrap());
+ println!("['U16', {}, {:?}],", v_u16, bsatn::to_vec(&v_u16).unwrap());
+ println!("['U32', {}, {:?}],", v_u32, bsatn::to_vec(&v_u32).unwrap());
+ println!("['U64', BigInt('{}'), {:?}],", v_u64, bsatn::to_vec(&v_u64).unwrap());
+ println!("['U128', BigInt('{}'), {:?}],", v_u128, bsatn::to_vec(&v_u128).unwrap());
+ println!("['U256', BigInt('{}'), {:?}],", v_u256, bsatn::to_vec(&v_u256).unwrap());
+ panic!();
+ }
+}
+*/
+
+let testCases: Array<[string, BigInt | number, Array]> = [
+ ['I8', 48, [48]],
+ ['I16', 2910, [94, 11]],
+ ['I32', -799760706, [190, 158, 84, 208]],
+ [
+ 'I64',
+ BigInt('-1541553498090056195'),
+ [253, 213, 20, 208, 66, 77, 155, 234],
+ ],
+ [
+ 'I128',
+ BigInt('12547586996680216771838914786222604020'),
+ [244, 254, 202, 102, 17, 36, 114, 210, 182, 88, 120, 98, 205, 147, 112, 9],
+ ],
+ [
+ 'I256',
+ BigInt(
+ '35334490670013506332541201493144667192747188790291257662501378603950330458369'
+ ),
+ [
+ 1, 177, 117, 147, 65, 153, 110, 71, 110, 80, 45, 231, 208, 112, 149, 150,
+ 251, 157, 51, 25, 129, 124, 13, 154, 238, 225, 7, 63, 237, 156, 30, 78,
+ ],
+ ],
+ ['U8', 63, [63]],
+ ['U16', 14776, [184, 57]],
+ ['U32', 2260346643, [19, 39, 186, 134]],
+ [
+ 'U64',
+ BigInt('6355943419584016569'),
+ [185, 112, 201, 104, 221, 216, 52, 88],
+ ],
+ [
+ 'U128',
+ BigInt('190443100270131819986139062814080853012'),
+ [20, 100, 201, 134, 99, 82, 196, 32, 34, 79, 25, 142, 199, 1, 70, 143],
+ ],
+ [
+ 'U256',
+ BigInt(
+ '58716185326733447174109779681509939791568291171619953995835894271369692835957'
+ ),
+ [
+ 117, 240, 99, 239, 213, 99, 55, 201, 2, 145, 4, 24, 0, 173, 62, 27, 124,
+ 53, 44, 244, 71, 1, 156, 30, 111, 187, 149, 150, 229, 46, 208, 129,
+ ],
+ ],
+];
+
+describe('BinaryReader/Writer', () => {
+ test('correctly reads/writes little endian values', () => {
+ for (let [name, int, buf] of testCases) {
+ let arr = new Uint8Array(buf);
+ let reader = new BinaryReader(arr);
+
+ let read = reader['read' + name]();
+ expect(read).toEqual(int);
+
+ let writer = new BinaryWriter(0);
+ writer['write' + name](int);
+
+ expect(writer.getBuffer()).toEqual(arr);
+ }
+ });
+});
diff --git a/sdks/typescript/packages/sdk/tests/db_connection.test.ts b/sdks/typescript/packages/sdk/tests/db_connection.test.ts
new file mode 100644
index 00000000000..8339256c9a6
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tests/db_connection.test.ts
@@ -0,0 +1,681 @@
+import {
+ CreatePlayer,
+ DbConnection,
+ Player,
+ Point,
+ User,
+} from '@clockworklabs/test-app/src/module_bindings';
+import { beforeEach, describe, expect, test } from 'vitest';
+import { ConnectionId } from '../src/connection_id';
+import { Timestamp } from '../src/timestamp';
+import { TimeDuration } from '../src/time_duration';
+import { AlgebraicType } from '../src/algebraic_type';
+import { parseValue } from '../src/algebraic_value';
+import BinaryWriter from '../src/binary_writer';
+import * as ws from '../src/client_api';
+import { ReducerEvent } from '../src/db_connection_impl';
+import { Identity } from '../src/identity';
+import WebsocketTestAdapter from '../src/websocket_test_adapter';
+
+const anIdentity = Identity.fromString(
+ '0000000000000000000000000000000000000000000000000000000000000069'
+);
+const bobIdentity = Identity.fromString(
+ '0000000000000000000000000000000000000000000000000000000000000b0b'
+);
+const sallyIdentity = Identity.fromString(
+ '000000000000000000000000000000000000000000000000000000000006a111'
+);
+
+class Deferred {
+ #isResolved: boolean = false;
+ #isRejected: boolean = false;
+ #resolve: (value: T | PromiseLike) => void;
+ #reject: (reason?: any) => void;
+ promise: Promise;
+
+ constructor() {
+ this.promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
+ });
+ }
+
+ // Getter for isResolved
+ get isResolved(): boolean {
+ return this.#isResolved;
+ }
+
+ // Getter for isRejected
+ get isRejected(): boolean {
+ return this.#isRejected;
+ }
+
+ // Resolve method
+ resolve(value: T): void {
+ if (!this.#isResolved && !this.#isRejected) {
+ this.#isResolved = true;
+ this.#resolve(value);
+ }
+ }
+
+ // Reject method
+ reject(reason?: any): void {
+ if (!this.#isResolved && !this.#isRejected) {
+ this.#isRejected = true;
+ this.#reject(reason);
+ }
+ }
+}
+
+beforeEach(() => {});
+
+function encodePlayer(value: Player): Uint8Array {
+ const writer = new BinaryWriter(1024);
+ Player.serialize(writer, value);
+ return writer.getBuffer();
+}
+
+function encodeUser(value: User): Uint8Array {
+ const writer = new BinaryWriter(1024);
+ User.serialize(writer, value);
+ return writer.getBuffer();
+}
+
+function encodeCreatePlayerArgs(name: string, location: Point): Uint8Array {
+ const writer = new BinaryWriter(1024);
+ AlgebraicType.createStringType().serialize(writer, name);
+ Point.serialize(writer, location);
+ return writer.getBuffer();
+}
+
+describe('DbConnection', () => {
+ test('call onConnectError callback after websocket connection failed to be established', async () => {
+ const onConnectErrorPromise = new Deferred();
+
+ let errorCalled = false;
+ let connectCalled = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(() => {
+ return Promise.reject(new Error('Failed to connect'));
+ })
+ .onConnect(() => {
+ connectCalled = true;
+ })
+ .onConnectError(() => {
+ errorCalled = true;
+ onConnectErrorPromise.resolve();
+ })
+ .build();
+
+ await client['wsPromise'];
+ await onConnectErrorPromise.promise;
+ expect(errorCalled).toBeTruthy();
+ expect(connectCalled).toBeFalsy();
+ });
+
+ test('call onConnect callback after getting an identity', async () => {
+ const onConnectPromise = new Deferred();
+
+ const wsAdapter = new WebsocketTestAdapter();
+ let called = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .onConnect(() => {
+ called = true;
+ onConnectPromise.resolve();
+ })
+ .build();
+
+ await client['wsPromise'];
+ wsAdapter.acceptConnection();
+
+ const tokenMessage = ws.ServerMessage.IdentityToken({
+ identity: anIdentity,
+ token: 'a-token',
+ connectionId: ConnectionId.random(),
+ });
+ wsAdapter.sendToClient(tokenMessage);
+
+ await onConnectPromise.promise;
+
+ expect(called).toBeTruthy();
+ });
+
+ test('it calls onInsert callback when a record is added with a subscription update and then with a transaction update', async () => {
+ const wsAdapter = new WebsocketTestAdapter();
+ let called = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .onConnect(() => {
+ called = true;
+ })
+ .build();
+
+ await Promise.race([
+ client['wsPromise'],
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Timeout')), 1000)
+ ),
+ ]);
+ wsAdapter.acceptConnection();
+
+ const tokenMessage = ws.ServerMessage.IdentityToken({
+ identity: anIdentity,
+ token: 'a-token',
+ connectionId: ConnectionId.random(),
+ });
+ wsAdapter.sendToClient(tokenMessage);
+
+ const inserts: {
+ reducerEvent:
+ | ReducerEvent<{
+ name: 'CreatePlayer';
+ args: CreatePlayer;
+ }>
+ | undefined;
+ player: Player;
+ }[] = [];
+
+ const insert1Promise = new Deferred();
+ const insert2Promise = new Deferred();
+
+ client.db.player.onInsert((ctx, player) => {
+ console.log('onInsert called');
+ if (ctx.event.tag === 'Reducer') {
+ inserts.push({ reducerEvent: ctx.event.value, player });
+ } else {
+ inserts.push({ reducerEvent: undefined, player });
+ }
+
+ if (!insert1Promise.isResolved) {
+ insert1Promise.resolve();
+ } else {
+ insert2Promise.resolve();
+ }
+ });
+
+ let reducerCallbackLog: {
+ reducerEvent: ReducerEvent<{
+ name: 'CreatePlayer';
+ args: CreatePlayer;
+ }>;
+ reducerArgs: any[];
+ }[] = [];
+ client.reducers.onCreatePlayer((ctx, name: string, location: Point) => {
+ const reducerEvent = ctx.event;
+ reducerCallbackLog.push({
+ reducerEvent,
+ reducerArgs: [name, location],
+ });
+ });
+
+ const subscriptionMessage: ws.ServerMessage =
+ ws.ServerMessage.InitialSubscription({
+ databaseUpdate: {
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'player',
+ numRows: BigInt(1),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array(),
+ },
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: encodePlayer({
+ ownerId: 'player-1',
+ name: 'drogus',
+ location: { x: 0, y: 0 },
+ }),
+ },
+ }),
+ ],
+ },
+ ],
+ },
+ requestId: 0,
+ totalHostExecutionDuration: new TimeDuration(BigInt(0)),
+ });
+
+ wsAdapter.sendToClient(subscriptionMessage);
+
+ await Promise.race([
+ insert1Promise.promise,
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Timeout')), 1000)
+ ),
+ ]);
+
+ expect(inserts).toHaveLength(1);
+ expect(inserts[0].player.ownerId).toBe('player-1');
+ expect(inserts[0].reducerEvent).toBe(undefined);
+
+ const transactionUpdate = ws.ServerMessage.TransactionUpdate({
+ status: ws.UpdateStatus.Committed({
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'player',
+ numRows: BigInt(2),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array(),
+ },
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: encodePlayer({
+ ownerId: 'player-2',
+ name: 'drogus',
+ location: { x: 2, y: 3 },
+ }),
+ },
+ }),
+ ],
+ },
+ ],
+ }),
+ timestamp: new Timestamp(1681391805281203n),
+ callerIdentity: anIdentity,
+ callerConnectionId: ConnectionId.random(),
+ reducerCall: {
+ reducerName: 'create_player',
+ reducerId: 0,
+ args: encodeCreatePlayerArgs('A Player', { x: 2, y: 3 }),
+ requestId: 0,
+ },
+ energyQuantaUsed: { quanta: BigInt(33841000) },
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+ wsAdapter.sendToClient(transactionUpdate);
+
+ await Promise.race([
+ insert2Promise.promise,
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Timeout')), 1000)
+ ),
+ ]);
+
+ expect(inserts).toHaveLength(2);
+ expect(inserts[1].player.ownerId).toBe('player-2');
+ expect(inserts[1].reducerEvent?.reducer.name).toBe('create_player');
+ expect(inserts[1].reducerEvent?.status.tag).toBe('Committed');
+ expect(inserts[1].reducerEvent?.callerIdentity).toEqual(anIdentity);
+ expect(inserts[1].reducerEvent?.reducer.args).toEqual({
+ name: 'A Player',
+ location: { x: 2, y: 3 },
+ });
+
+ expect(reducerCallbackLog).toHaveLength(1);
+
+ expect(reducerCallbackLog[0].reducerEvent.callerIdentity).toEqual(
+ anIdentity
+ );
+ });
+
+ test('tables should be updated before the reducer callback', async () => {
+ const wsAdapter = new WebsocketTestAdapter();
+ let called = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .onConnect(() => {
+ called = true;
+ })
+ .build();
+
+ await client['wsPromise'];
+ wsAdapter.acceptConnection();
+
+ let callbackLog: string[] = [];
+
+ const updatePromise = new Deferred();
+
+ expect(client.db.player.count()).toBe(0);
+
+ client.reducers.onCreatePlayer((ctx, name, location) => {
+ expect(client.db.player.count()).toBe(1);
+ updatePromise.resolve();
+ });
+
+ const transactionUpdate = ws.ServerMessage.TransactionUpdate({
+ status: ws.UpdateStatus.Committed({
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'player',
+ numRows: BigInt(1),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array(),
+ },
+ // FIXME: this test is evil: an initial subscription can never contain deletes or updates.
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([
+ ...encodePlayer({
+ ownerId: 'player-2',
+ name: 'foo',
+ location: { x: 0, y: 0 },
+ }),
+ ]),
+ },
+ }),
+ ],
+ },
+ ],
+ }),
+ timestamp: new Timestamp(1681391805281203n),
+ callerIdentity: anIdentity,
+ callerConnectionId: ConnectionId.random(),
+ reducerCall: {
+ reducerName: 'create_player',
+ reducerId: 0,
+ args: encodeCreatePlayerArgs('A Player', { x: 2, y: 3 }),
+ requestId: 0,
+ },
+ energyQuantaUsed: { quanta: BigInt(33841000) },
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+ wsAdapter.sendToClient(transactionUpdate);
+
+ await Promise.all([updatePromise.promise]);
+ });
+
+ test('a reducer callback should be called before the database callbacks', async () => {
+ const wsAdapter = new WebsocketTestAdapter();
+ let called = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .onConnect(() => {
+ called = true;
+ })
+ .build();
+
+ await client['wsPromise'];
+ wsAdapter.acceptConnection();
+
+ let callbackLog: string[] = [];
+
+ const insertPromise = new Deferred();
+ const updatePromise = new Deferred();
+
+ client.db.player.onInsert((ctx, player) => {
+ callbackLog.push('Player');
+
+ insertPromise.resolve();
+ });
+
+ client.reducers.onCreatePlayer((ctx, name, location) => {
+ callbackLog.push('CreatePlayerReducer');
+
+ updatePromise.resolve();
+ });
+
+ const transactionUpdate = ws.ServerMessage.TransactionUpdate({
+ status: ws.UpdateStatus.Committed({
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'player',
+ numRows: BigInt(1),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array(),
+ },
+ // FIXME: this test is evil: an initial subscription can never contain deletes or updates.
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([
+ ...encodePlayer({
+ ownerId: 'player-2',
+ name: 'foo',
+ location: { x: 0, y: 0 },
+ }),
+ ]),
+ },
+ }),
+ ],
+ },
+ ],
+ }),
+ timestamp: new Timestamp(1681391805281203n),
+ callerIdentity: anIdentity,
+ callerConnectionId: ConnectionId.random(),
+ reducerCall: {
+ reducerName: 'create_player',
+ reducerId: 0,
+ args: encodeCreatePlayerArgs('A Player', { x: 2, y: 3 }),
+ requestId: 0,
+ },
+ energyQuantaUsed: { quanta: BigInt(33841000) },
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+ wsAdapter.sendToClient(transactionUpdate);
+
+ await Promise.all([insertPromise.promise, updatePromise.promise]);
+
+ expect(callbackLog).toEqual(['CreatePlayerReducer', 'Player']);
+ });
+
+ test('it calls onUpdate callback when a record is added with a subscription update and then with a transaction update when the PK is of type Identity', async () => {
+ const wsAdapter = new WebsocketTestAdapter();
+ let called = false;
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .onConnect(() => {
+ called = true;
+ })
+ .build();
+
+ await client['wsPromise'];
+ wsAdapter.acceptConnection();
+
+ const tokenMessage = ws.ServerMessage.IdentityToken({
+ identity: Identity.fromString(
+ '0000000000000000000000000000000000000000000000000000000000000069'
+ ),
+ token: 'a-token',
+ connectionId: ConnectionId.random(),
+ });
+ wsAdapter.sendToClient(tokenMessage);
+
+ const update1Promise = new Deferred();
+ const initialInsertPromise = new Deferred();
+ const userIdentity = Identity.fromString(
+ '41db74c20cdda916dd2637e5a11b9f31eb1672249aa7172f7e22b4043a6a9008'
+ );
+
+ const initialUser: User = {
+ identity: userIdentity,
+ username: 'originalName',
+ };
+ const updatedUser: User = {
+ identity: userIdentity,
+ username: 'newName',
+ };
+
+ const updates: {
+ oldUser: User;
+ newUser: User;
+ }[] = [];
+ client.db.user.onInsert((ctx, user) => {
+ initialInsertPromise.resolve();
+ console.log('got insert');
+ });
+ client.db.user.onUpdate((_ctx, oldUser, newUser) => {
+ updates.push({
+ oldUser,
+ newUser,
+ });
+ update1Promise.resolve();
+ });
+
+ const subscriptionMessage = ws.ServerMessage.InitialSubscription({
+ databaseUpdate: {
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'user',
+ numRows: BigInt(1),
+ updates: [
+ // pgoldman 2024-06-25: This is weird, `InitialSubscription`s aren't supposed to contain deletes or updates.
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([]),
+ },
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([...encodeUser(initialUser)]),
+ },
+ }),
+ ],
+ },
+ ],
+ },
+ requestId: 0,
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+
+ wsAdapter.sendToClient(subscriptionMessage);
+
+ // await update1Promise.promise;
+ await initialInsertPromise.promise;
+ console.log('First insert is done');
+
+ const transactionUpdate = ws.ServerMessage.TransactionUpdate({
+ status: ws.UpdateStatus.Committed({
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'user',
+ numRows: BigInt(1),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([...encodeUser(initialUser)]),
+ },
+ // FIXME: this test is evil: an initial subscription can never contain deletes or updates.
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([...encodeUser(updatedUser)]),
+ },
+ }),
+ ],
+ },
+ ],
+ }),
+ timestamp: new Timestamp(1681391805281203n),
+ callerIdentity: anIdentity,
+ callerConnectionId: ConnectionId.random(),
+ reducerCall: {
+ reducerName: 'create_player',
+ reducerId: 0,
+ args: encodeCreatePlayerArgs('A Player', { x: 2, y: 3 }),
+ requestId: 0,
+ },
+ energyQuantaUsed: { quanta: BigInt(33841000) },
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+
+ console.log('Sending transaction update');
+ wsAdapter.sendToClient(transactionUpdate);
+
+ await update1Promise.promise;
+
+ expect(updates).toHaveLength(1);
+ expect(updates[0]['oldUser'].username).toBe(initialUser.username);
+ expect(updates[0]['newUser'].username).toBe(updatedUser.username);
+
+ console.log('Users: ', [...client.db.user.iter()]);
+ expect(client.db.user.count()).toBe(1);
+ });
+
+ test('Filtering works', async () => {
+ const wsAdapter = new WebsocketTestAdapter();
+ const client = DbConnection.builder()
+ .withUri('ws://127.0.0.1:1234')
+ .withModuleName('db')
+ .withWSFn(wsAdapter.createWebSocketFn.bind(wsAdapter))
+ .build();
+ await client['wsPromise'];
+ const user1 = { identity: bobIdentity, username: 'bob' };
+ const user2 = {
+ identity: sallyIdentity,
+ username: 'sally',
+ };
+ const binary = [...encodeUser(user1)].concat([...encodeUser(user2)]);
+ const transactionUpdate = ws.ServerMessage.TransactionUpdate({
+ status: ws.UpdateStatus.Committed({
+ tables: [
+ {
+ tableId: 35,
+ tableName: 'user',
+ numRows: BigInt(1),
+ updates: [
+ ws.CompressableQueryUpdate.Uncompressed({
+ deletes: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array([]),
+ },
+ // FIXME: this test is evil: an initial subscription can never contain deletes or updates.
+ inserts: {
+ sizeHint: ws.RowSizeHint.FixedSize(0), // not used
+ rowsData: new Uint8Array(binary),
+ },
+ }),
+ ],
+ },
+ ],
+ }),
+ timestamp: new Timestamp(1681391805281203n),
+ callerIdentity: anIdentity,
+ callerConnectionId: ConnectionId.random(),
+ reducerCall: {
+ reducerName: 'create_player',
+ reducerId: 0,
+ args: encodeCreatePlayerArgs('A Player', { x: 2, y: 3 }),
+ requestId: 0,
+ },
+ energyQuantaUsed: { quanta: BigInt(33841000) },
+ totalHostExecutionDuration: new TimeDuration(BigInt(1234567890)),
+ });
+ const gotAllInserts = new Deferred();
+ var inserts = 0;
+ client.db.user.onInsert((ctx, user) => {
+ inserts++;
+ if (inserts == 2) {
+ gotAllInserts.resolve();
+ }
+ });
+ wsAdapter.sendToClient(transactionUpdate);
+ await gotAllInserts.promise;
+
+ const filteredUser = client.db.user.identity.find(sallyIdentity);
+ expect(filteredUser).not.toBeUndefined;
+ expect(filteredUser!.username).toBe('sally');
+ expect(client.db.user.count()).toBe(2);
+ });
+});
diff --git a/sdks/typescript/packages/sdk/tests/table_cache.test.ts b/sdks/typescript/packages/sdk/tests/table_cache.test.ts
new file mode 100644
index 00000000000..1e666af4e45
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tests/table_cache.test.ts
@@ -0,0 +1,782 @@
+import { Operation, TableCache } from '../src/table_cache';
+import type { TableRuntimeTypeInfo } from '../src/spacetime_module';
+import { describe, expect, test } from 'vitest';
+
+import { Player } from '@clockworklabs/test-app/src/module_bindings';
+
+import { AlgebraicType, ProductTypeElement } from '../src/algebraic_type';
+
+interface ApplyOperations {
+ ops: Operation[];
+ ctx: any;
+}
+
+interface CallbackEvent {
+ type: 'insert' | 'delete' | 'update';
+ ctx: any;
+ row: any;
+ oldRow?: any; // Only there for updates.
+}
+
+function insertEvent(row: any, ctx: any = {}): CallbackEvent {
+ return {
+ type: 'insert',
+ ctx,
+ row,
+ };
+}
+
+function updateEvent(oldRow: any, row: any, ctx: any = {}): CallbackEvent {
+ return {
+ type: 'update',
+ ctx,
+ row,
+ oldRow,
+ };
+}
+
+function deleteEvent(row: any, ctx: any = {}): CallbackEvent {
+ return {
+ type: 'delete',
+ ctx,
+ row,
+ };
+}
+
+interface AssertionInput {
+ // The state of the table cache.
+ tableCache: TableCache;
+ // The sequence of callbacks that were fired from the last applyOperations.
+ callbackHistory: CallbackEvent[];
+}
+
+type Assertion = (AssertionInput) => void;
+
+interface TestStep {
+ // The operations to apply.
+ ops: ApplyOperations;
+ // The assertions to make after applying the operations.
+ assertions: Assertion[];
+}
+
+function runTest(tableCache: TableCache, testSteps: TestStep[]) {
+ const callbackHistory: CallbackEvent[] = [];
+ tableCache.onInsert((ctx, row) => {
+ callbackHistory.push({
+ type: 'insert',
+ ctx,
+ row,
+ });
+ });
+ tableCache.onDelete((ctx, row) => {
+ callbackHistory.push({
+ type: 'delete',
+ ctx,
+ row,
+ });
+ });
+ tableCache.onUpdate((ctx, oldRow, row) => {
+ callbackHistory.push({
+ type: 'update',
+ ctx,
+ row,
+ oldRow,
+ });
+ });
+
+ for (const step of testSteps) {
+ const { ops: applyOperations, assertions } = step;
+ const { ops, ctx } = applyOperations;
+ const callbacks = tableCache.applyOperations(ops, ctx);
+ callbacks.forEach(cb => cb.cb());
+ for (const assertion of assertions) {
+ assertion({ tableCache, callbackHistory });
+ }
+ // Clear the callback history for the next step.
+ callbackHistory.length = 0;
+ }
+}
+
+describe('TableCache', () => {
+ describe('Unindexed player table', () => {
+ const pointType = AlgebraicType.createProductType([
+ new ProductTypeElement('x', AlgebraicType.createU16Type()),
+ new ProductTypeElement('y', AlgebraicType.createU16Type()),
+ ]);
+ const playerType = AlgebraicType.createProductType([
+ new ProductTypeElement('ownerId', AlgebraicType.createStringType()),
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', pointType),
+ ]);
+ const tableTypeInfo: TableRuntimeTypeInfo = {
+ tableName: 'player',
+ rowType: playerType,
+ };
+ const newTable = () => new TableCache(tableTypeInfo);
+ const mkOperation = (type: 'insert' | 'delete', row: Player) => {
+ let rowId = tableTypeInfo.rowType.intoMapKey(row);
+ return {
+ type,
+ rowId,
+ row,
+ };
+ };
+
+ test('Insert one', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Inserting one twice only triggers one event', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player), mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert dupe is a noop', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(0);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert once and delete', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('delete');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert twice and delete', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let mkPlayer = () => ({
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ });
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [
+ mkOperation('insert', mkPlayer()),
+ mkOperation('insert', player),
+ ],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ // We still have one reference left, so it isn't actually deleted.
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(0);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ // Now it is actually deleted.
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('delete');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ // Now we are going to insert again, so we can delete both refs at once.
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory).toEqual([insertEvent(player)]);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory).toEqual([]);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player), mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory).toEqual([deleteEvent(mkPlayer())]);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert one', () => {
+ const tableCache = newTable();
+ let op = mkOperation('insert', {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ });
+ let rowsInserted = 0;
+ let callbacks = tableCache.applyOperations([op], {} as any);
+ tableCache.onInsert((ctx, row) => {
+ rowsInserted++;
+ expect(row).toEqual(op.row);
+ });
+ expect(callbacks.length).toBe(1);
+ expect(tableCache.count()).toBe(1);
+ callbacks.forEach(cb => {
+ cb.cb();
+ });
+ expect(rowsInserted).toBe(1);
+ });
+ });
+ describe('Indexed player table', () => {
+ const pointType = AlgebraicType.createProductType([
+ new ProductTypeElement('x', AlgebraicType.createU16Type()),
+ new ProductTypeElement('y', AlgebraicType.createU16Type()),
+ ]);
+ const playerType = AlgebraicType.createProductType([
+ new ProductTypeElement('ownerId', AlgebraicType.createStringType()),
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', pointType),
+ ]);
+ const tableTypeInfo: TableRuntimeTypeInfo = {
+ tableName: 'player',
+ rowType: playerType,
+ primaryKeyInfo: {
+ colName: 'ownerId',
+ colType: playerType.product.elements[0].algebraicType,
+ },
+ };
+ const newTable = () => new TableCache(tableTypeInfo);
+ const mkOperation = (type: 'insert' | 'delete', row: Player) => {
+ let rowId = tableTypeInfo.primaryKeyInfo!.colType.intoMapKey(
+ row['ownerId']
+ );
+ return {
+ type,
+ rowId,
+ row,
+ };
+ };
+
+ test('Insert one', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Inserting one twice only triggers one event', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player), mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert dupe is a noop', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter().length).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(0);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert once and delete', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('delete');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Update smoke test', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let mkPlayer = (name: string) => ({
+ ownerId: '1',
+ name: name,
+ location: {
+ x: 1,
+ y: 2,
+ },
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', mkPlayer('jeff'))],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(mkPlayer('jeff'));
+ expect(callbackHistory).toEqual([insertEvent(mkPlayer('jeff'))]);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [
+ mkOperation('delete', mkPlayer('jeff')),
+ mkOperation('insert', mkPlayer('jeffv2')),
+ ],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(mkPlayer('jeffv2'));
+ expect(callbackHistory).toEqual([
+ updateEvent(mkPlayer('jeff'), mkPlayer('jeffv2')),
+ ]);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert twice and delete', () => {
+ const tableCache = newTable();
+ const steps: TestStep[] = [];
+ let mkPlayer = () => ({
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ });
+ let player = {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ };
+ steps.push({
+ ops: {
+ ops: [
+ mkOperation('insert', mkPlayer()),
+ mkOperation('insert', player),
+ ],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('insert');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ // We still have one reference left, so it isn't actually deleted.
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory.length).toBe(0);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ // Now it is actually deleted.
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory.length).toBe(1);
+ expect(callbackHistory[0].type).toBe('delete');
+ expect(callbackHistory[0].row).toEqual(player);
+ },
+ ],
+ });
+ // Now we are going to insert again, so we can delete both refs at once.
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory).toEqual([insertEvent(player)]);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('insert', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(1);
+ expect(tableCache.iter()[0]).toEqual(player);
+ expect(callbackHistory).toEqual([]);
+ },
+ ],
+ });
+ steps.push({
+ ops: {
+ ops: [mkOperation('delete', player), mkOperation('delete', player)],
+ ctx: {} as any,
+ },
+ assertions: [
+ ({ tableCache, callbackHistory }) => {
+ expect(tableCache.count()).toBe(0);
+ expect(callbackHistory).toEqual([deleteEvent(mkPlayer())]);
+ },
+ ],
+ });
+ runTest(tableCache, steps);
+ });
+
+ test('Insert one', () => {
+ const tableCache = newTable();
+ let op = mkOperation('insert', {
+ ownerId: '1',
+ name: 'Player 1',
+ location: {
+ x: 1,
+ y: 2,
+ },
+ });
+ let rowsInserted = 0;
+ let callbacks = tableCache.applyOperations([op], {} as any);
+ tableCache.onInsert((ctx, row) => {
+ rowsInserted++;
+ expect(row).toEqual(op.row);
+ });
+ expect(callbacks.length).toBe(1);
+ expect(tableCache.count()).toBe(1);
+ callbacks.forEach(cb => {
+ cb.cb();
+ });
+ expect(rowsInserted).toBe(1);
+ });
+ });
+
+ const pointType = AlgebraicType.createProductType([
+ new ProductTypeElement('x', AlgebraicType.createU16Type()),
+ new ProductTypeElement('y', AlgebraicType.createU16Type()),
+ ]);
+ const playerType = AlgebraicType.createProductType([
+ new ProductTypeElement('ownerId', AlgebraicType.createStringType()),
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', pointType),
+ ]);
+
+ test('should be empty on creation', () => {
+ const tableTypeInfo: TableRuntimeTypeInfo = {
+ tableName: 'player',
+ rowType: playerType,
+ primaryKeyInfo: {
+ colName: 'ownerId',
+ colType: playerType.product.elements[0].algebraicType,
+ },
+ };
+ const tableCache = new TableCache(tableTypeInfo);
+ expect(tableCache.count()).toBe(0);
+ tableCache.applyOperations;
+ });
+});
diff --git a/sdks/typescript/packages/sdk/tests/version.test.ts b/sdks/typescript/packages/sdk/tests/version.test.ts
new file mode 100644
index 00000000000..08d6adc6130
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tests/version.test.ts
@@ -0,0 +1,185 @@
+import { describe, expect, test } from 'vitest';
+import {
+ SemanticVersion,
+ _MINIMUM_CLI_VERSION,
+ ensureMinimumVersionOrThrow,
+} from '../src/version.ts';
+
+describe('SemanticVersion', () => {
+ describe('Minimum version check', () => {
+ test('older versions throw an error', () => {
+ let olderVersions: string[] = ['0.0.1', '0.1.0', '1.1.0'];
+ if (_MINIMUM_CLI_VERSION.major > 0) {
+ let olderVersion = _MINIMUM_CLI_VERSION.clone();
+ olderVersion.major -= 1;
+ olderVersions.push(olderVersion.toString());
+ }
+ if (_MINIMUM_CLI_VERSION.minor > 0) {
+ let olderVersion = _MINIMUM_CLI_VERSION.clone();
+ olderVersion.minor -= 1;
+ olderVersions.push(olderVersion.toString());
+ }
+ if (_MINIMUM_CLI_VERSION.patch > 0) {
+ let olderVersion = _MINIMUM_CLI_VERSION.clone();
+ olderVersion.patch -= 1;
+ olderVersions.push(olderVersion.toString());
+ }
+ if (!_MINIMUM_CLI_VERSION.preRelease) {
+ let olderVersion = _MINIMUM_CLI_VERSION.clone();
+ olderVersion.preRelease = ['alpha'];
+ olderVersions.push(olderVersion.toString());
+ }
+ let olderVersion = _MINIMUM_CLI_VERSION.clone();
+ if (olderVersion.preRelease != null) {
+ if (typeof olderVersion.preRelease[0] === 'number') {
+ olderVersion.preRelease[0] = olderVersion.preRelease[0] - 1;
+ } else {
+ olderVersion.preRelease[0] += 'alpha';
+ }
+ }
+ const errorRegexp = new RegExp(
+ '.*generated with an incompatible version.*'
+ );
+ for (const versionString of olderVersions) {
+ expect(
+ () => ensureMinimumVersionOrThrow(versionString),
+ `Checking ${versionString}`
+ ).toThrow(errorRegexp);
+ }
+ });
+
+ test('newer versions do not throw', () => {
+ let newerVersions: string[] = [_MINIMUM_CLI_VERSION.toString()];
+ let newVersion = _MINIMUM_CLI_VERSION.clone();
+ newVersion.major += 1;
+ newerVersions.push(newVersion.toString());
+ newVersion = _MINIMUM_CLI_VERSION.clone();
+ newVersion.minor += 1;
+ newerVersions.push(newVersion.toString());
+ newVersion = _MINIMUM_CLI_VERSION.clone();
+ newVersion.patch += 1;
+ newerVersions.push(newVersion.toString());
+ newVersion = _MINIMUM_CLI_VERSION.clone();
+ if (newVersion.preRelease != null) {
+ newVersion.preRelease = null;
+ newerVersions.push(newVersion.toString());
+ }
+ const errorRegexp = new RegExp(
+ '.*generated with an incompatible version.*'
+ );
+ for (const versionString of newerVersions) {
+ expect(
+ () => ensureMinimumVersionOrThrow(versionString),
+ `Checking ${versionString}`
+ ).not.toThrow();
+ }
+ });
+ });
+ describe('Parsing semantic version strings', () => {
+ test('valid versions', () => {
+ // This is a dummy test to ensure that the test suite runs
+ // and that the TableCache is working as expected.
+ // You can add more tests here to cover different scenarios.
+ //parseVersionString('1.0.0');
+ let tests: [string, SemanticVersion][] = [
+ ['1.0.0', new SemanticVersion(1, 0, 0)],
+ ['1.0.0-alpha', new SemanticVersion(1, 0, 0, ['alpha'])],
+ ['1.0.0-alpha.1', new SemanticVersion(1, 0, 0, ['alpha', 1])],
+ [
+ '1.0.0-alpha.20.beta',
+ new SemanticVersion(1, 0, 0, ['alpha', 20, 'beta']),
+ ],
+ ['1.0.0-alpha.beta', new SemanticVersion(1, 0, 0, ['alpha', 'beta'])],
+ ['0.2.1', new SemanticVersion(0, 2, 1)],
+ ['0.0.0', new SemanticVersion(0, 0, 0)],
+ ['10.2.1', new SemanticVersion(10, 2, 1)],
+ [
+ '1.0.0+20130313144700',
+ new SemanticVersion(1, 0, 0, null, '20130313144700'),
+ ],
+ ['1.0.0-alpha+001', new SemanticVersion(1, 0, 0, ['alpha'], '001')],
+ [
+ '1.0.0-alpha.beta+exp.sha.5114f85',
+ new SemanticVersion(1, 0, 0, ['alpha', 'beta'], 'exp.sha.5114f85'),
+ ],
+ [
+ '1.0.0+exp.sha.5114f85',
+ new SemanticVersion(1, 0, 0, null, 'exp.sha.5114f85'),
+ ],
+ ];
+ for (const [versionString, expectedVersion] of tests) {
+ const parsedVersion = SemanticVersion.parseVersionString(versionString);
+ expect(parsedVersion, `Parsing ${versionString}`).toEqual(
+ expectedVersion
+ );
+ }
+ });
+
+ test('invalid version strings should throw an error', () => {
+ let invalidTests: string[] = [
+ '1.0', // Missing patch version
+ '1', // Missing minor and patch versions
+ '1.0.0-', // Trailing hyphen
+ '1.0.0+', // Trailing plus
+ '1.0.0-alpha..1', // Double dots in pre-release
+ '1.0.0+build..info', // Double dots in build metadata
+ '1.0.0-alpha!', // Invalid character in pre-release
+ '1.0.0+build!', // Invalid character in build metadata
+ 'abc.def.ghi', // Completely invalid format
+ '', // Empty string
+ ];
+ for (const versionString of invalidTests) {
+ expect(() => SemanticVersion.parseVersionString(versionString)).toThrow(
+ `Invalid version string: ${versionString}`
+ );
+
+ // Checking the minimum version should also fail.
+ expect(() => ensureMinimumVersionOrThrow(versionString)).toThrow(
+ `Invalid version string: ${versionString}`
+ );
+ }
+ });
+ });
+ describe('Comparing SemanticVersions', () => {
+ function normalizedCompare(
+ version1: SemanticVersion,
+ version2: SemanticVersion
+ ): number {
+ let c = version1.compare(version2);
+ if (c < 0) {
+ return -1;
+ }
+ if (c > 0) {
+ return 1;
+ }
+ return 0;
+ }
+ test('test comparison order', () => {
+ let cases: [string, string, number][] = [
+ ['1.0.0', '1.0.0', 0],
+ ['1.0.0', '1.0.1', -1],
+ ['1.10.0', '1.2.1', 1],
+ ['1.2.1', '1.10.0', -1],
+ ['1.0.1', '1.0.0', 1],
+ ['1.0.0-alpha', '1.0.0-alpha', 0],
+ ['1.0.0-alpha', '1.0.0-beta', -1],
+ ['1.0.0-beta', '1.0.0-alpha', 1],
+ ['1.0.0-alpha', '1.0.0-alpha.beta', -1],
+ ['1.0.0-1', '1.0.0-alpha.beta', -1],
+ ['1.0.0-alpha.1', '1.0.0-alpha.2', -1],
+ ['1.0.0-alpha.beta', '1.0.0-alpha', 1],
+ ['2.0.0-alpha.beta', '2.0.0-alpha.beta', 0],
+ ['2.0.0-alpha.beta+001', '2.0.0-alpha.beta+001', 0],
+ ['2.0.0-alpha.beta+001', '2.0.0-alpha.beta+002', 0], // Build tags don't affect comparison
+ ];
+ for (const [left, right, expectedComparison] of cases) {
+ const parsedLeft = SemanticVersion.parseVersionString(left);
+ const parsedRight = SemanticVersion.parseVersionString(right);
+ expect(
+ normalizedCompare(parsedLeft, parsedRight),
+ 'Comparing ' + left + ' and ' + right
+ ).toEqual(expectedComparison);
+ }
+ });
+ });
+});
diff --git a/sdks/typescript/packages/sdk/tsconfig.json b/sdks/typescript/packages/sdk/tsconfig.json
new file mode 100644
index 00000000000..8c28df00aa8
--- /dev/null
+++ b/sdks/typescript/packages/sdk/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "declaration": true,
+ "emitDeclarationOnly": false,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "verbatimModuleSyntax": true,
+ "allowImportingTsExtensions": true,
+ "strict": true,
+ "noImplicitAny": false,
+ "moduleResolution": "Bundler",
+ "allowSyntheticDefaultImports": true,
+ "isolatedDeclarations": true,
+ "isolatedModules": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "**/__tests__/*", "dist/**/*"]
+}
diff --git a/sdks/typescript/packages/test-app/.gitignore b/sdks/typescript/packages/test-app/.gitignore
new file mode 100644
index 00000000000..a547bf36d8d
--- /dev/null
+++ b/sdks/typescript/packages/test-app/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/sdks/typescript/packages/test-app/CHANGELOG.md b/sdks/typescript/packages/test-app/CHANGELOG.md
new file mode 100644
index 00000000000..f5ae6c467c6
--- /dev/null
+++ b/sdks/typescript/packages/test-app/CHANGELOG.md
@@ -0,0 +1,29 @@
+# @clockworklabs/test-app
+
+## 0.0.1
+
+### Patch Changes
+
+- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`941cf4e`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/941cf4eba6b7df934d74696b373b89cc62764673), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
+ - @clockworklabs/spacetimedb-sdk@1.2.0
+
+## 0.0.3-rc1.0
+
+### Patch Changes
+
+- Updated dependencies [[`cf7b7d8`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/cf7b7d89a1547fb3863f6641f5b2eb40a27c05d8), [`a501f5c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/a501f5ccf9a0a926eb4f345ddeb01ffcb872d67e), [`9032269`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/9032269004d4dae587c39ccd85da0a32fb9a0114), [`6547882`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/6547882bb28ed9a1ca436335745e9997328026ff), [`5d7304b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5d7304bd3e05dd7a032cfb7069aab97b881f0179)]:
+ - @clockworklabs/spacetimedb-sdk@1.0.0-rc1.0
+
+## 0.0.2
+
+### Patch Changes
+
+- Updated dependencies [[`2f6c82c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/2f6c82c724b9f9407c7bedee13252ca8ffab8f7d), [`b9db9b6`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b9db9b6e46d8c98b29327d97c12c07b7a2fc96bf), [`79c278b`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/79c278be71b2dfd82106ada983fd81d395b1d912)]:
+ - @clockworklabs/spacetimedb-sdk@0.12.1
+
+## 0.0.1
+
+### Patch Changes
+
+- Updated dependencies [[`5adb557`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/5adb55776c81d0760cf0268df0fa5dee600f0ef8), [`ab1f463`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/ab1f463d7da6e530a6cd47e2433141bfd16addd1), [`b8c944c`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/b8c944cd23d3b53c72131803a775127bf0a95213), [`17227c0`](https://github.com/clockworklabs/spacetimedb-typescript-sdk/commit/17227c0f65def3a9d5e767756ccf46777210041a)]:
+ - @clockworklabs/spacetimedb-sdk@0.12.0
diff --git a/sdks/typescript/packages/test-app/README.md b/sdks/typescript/packages/test-app/README.md
new file mode 100644
index 00000000000..3feaa9d74ce
--- /dev/null
+++ b/sdks/typescript/packages/test-app/README.md
@@ -0,0 +1,30 @@
+# React + TypeScript + Vite
+
+This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+
+Currently, two official plugins are available:
+
+- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
+- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+
+## Expanding the ESLint configuration
+
+If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
+
+- Configure the top-level `parserOptions` property like this:
+
+```js
+export default {
+ // other rules...
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ project: ['./tsconfig.json', './tsconfig.node.json', './tsconfig.app.json'],
+ tsconfigRootDir: __dirname,
+ },
+};
+```
+
+- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
+- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
+- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
diff --git a/sdks/typescript/packages/test-app/index.html b/sdks/typescript/packages/test-app/index.html
new file mode 100644
index 00000000000..e4b78eae123
--- /dev/null
+++ b/sdks/typescript/packages/test-app/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite + React + TS
+
+
+
+
+
+
diff --git a/sdks/typescript/packages/test-app/package.json b/sdks/typescript/packages/test-app/package.json
new file mode 100644
index 00000000000..a98f2642634
--- /dev/null
+++ b/sdks/typescript/packages/test-app/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@clockworklabs/test-app",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "files": [
+ "src"
+ ],
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview",
+ "spacetime:generate-bindings": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path server",
+ "spacetime:start": "spacetime start server",
+ "spacetime:publish:local": "spacetime publish game --project-path server --server local",
+ "spacetime:publish": "spacetime publish game --project-path server --server testnet"
+ },
+ "dependencies": {
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "@clockworklabs/spacetimedb-sdk": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "typescript": "^5.2.2",
+ "vite": "^5.3.4"
+ }
+}
diff --git a/sdks/typescript/packages/test-app/server/.gitignore b/sdks/typescript/packages/test-app/server/.gitignore
new file mode 100644
index 00000000000..31b13f058aa
--- /dev/null
+++ b/sdks/typescript/packages/test-app/server/.gitignore
@@ -0,0 +1,17 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
+
+# Spacetime ignore
+/.spacetime
\ No newline at end of file
diff --git a/sdks/typescript/packages/test-app/server/Cargo.toml b/sdks/typescript/packages/test-app/server/Cargo.toml
new file mode 100644
index 00000000000..9b278585109
--- /dev/null
+++ b/sdks/typescript/packages/test-app/server/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "spacetime-module"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+spacetimedb = "1.2.0"
+log = "0.4"
+anyhow = "1.0"
diff --git a/sdks/typescript/packages/test-app/server/src/lib.rs b/sdks/typescript/packages/test-app/server/src/lib.rs
new file mode 100644
index 00000000000..4fbd2086d21
--- /dev/null
+++ b/sdks/typescript/packages/test-app/server/src/lib.rs
@@ -0,0 +1,38 @@
+use spacetimedb::{reducer, table, Identity, ReducerContext, SpacetimeType, Table};
+
+#[table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ owner_id: String,
+ name: String,
+ location: Point,
+}
+
+#[derive(SpacetimeType)]
+pub struct Point {
+ pub x: u16,
+ pub y: u16,
+}
+
+#[table(name = user, public)]
+pub struct User {
+ #[primary_key]
+ pub identity: Identity,
+ pub username: String,
+}
+
+#[table(name = unindexed_player, public)]
+pub struct UnindexedPlayer {
+ owner_id: String,
+ name: String,
+ location: Point,
+}
+
+#[reducer]
+pub fn create_player(ctx: &ReducerContext, name: String, location: Point) {
+ ctx.db.player().insert(Player {
+ owner_id: ctx.sender.to_hex().to_string(),
+ name,
+ location,
+ });
+}
diff --git a/sdks/typescript/packages/test-app/src/.gitattributes b/sdks/typescript/packages/test-app/src/.gitattributes
new file mode 100644
index 00000000000..f4d6534ab2c
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/.gitattributes
@@ -0,0 +1 @@
+/module_bindings/** linguist-generated=true
diff --git a/sdks/typescript/packages/test-app/src/App.css b/sdks/typescript/packages/test-app/src/App.css
new file mode 100644
index 00000000000..b9d355df2a5
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/sdks/typescript/packages/test-app/src/App.tsx b/sdks/typescript/packages/test-app/src/App.tsx
new file mode 100644
index 00000000000..725c78fbb06
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/App.tsx
@@ -0,0 +1,57 @@
+import { DbConnection } from './module_bindings';
+import { useEffect, useState } from 'react';
+import './App.css';
+
+function App() {
+ const [connection] = useState(() =>
+ DbConnection.builder()
+ .withUri('ws://localhost:3000')
+ .withModuleName('game')
+ .withLightMode(true)
+ .onDisconnect(() => {
+ console.log('disconnected');
+ })
+ .onConnectError(() => {
+ console.log('client_error');
+ })
+ .onConnect((conn, identity, _token) => {
+ console.log(
+ 'Connected to SpacetimeDB with identity:',
+ identity.toHexString()
+ );
+
+ conn.subscriptionBuilder().subscribe('SELECT * FROM player');
+ })
+ .withToken(
+ 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwMUpCQTBYRzRESFpIWUdQQk5GRFk5RDQ2SiIsImlzcyI6Imh0dHBzOi8vYXV0aC5zdGFnaW5nLnNwYWNldGltZWRiLmNvbSIsImlhdCI6MTczMDgwODUwNSwiZXhwIjoxNzkzODgwNTA1fQ.kGM4HGX0c0twL8NJoSQowzSZa8dc2Ogc-fsvaDK7otUrcdGFsZ3KsNON2eNkFh73FER0hl55_eJStr2tgoPwfTyl_v_TqkY45iUOUlLmHfB-X42cMzpE7PXbR_PKYcp-P-Wa4jGtVl4oF7CvdGKxlhIYEk3e0ElQlA9ThnZN4IEciYV0vwAXGqbaO9SOG8jbrmlmfN7oKgl02EgpodEAHTrnB2mD1qf1YyOw7_9n_EkxJxWLkJf9-nFCVRrbfSLqSJBeE6OKNAu2VLLYrSFE7GkVXNCFVugoCDM2oVJogX75AgzWimrp75QRmLsXbvB-YvvRkQ8Gfb2RZnqCj9kiYg'
+ )
+ .build()
+ );
+
+ useEffect(() => {
+ connection.db.player.onInsert(player => {
+ console.log(player);
+ });
+
+ setTimeout(() => {
+ console.log(Array.from(connection.db.player.iter()));
+ }, 5000);
+ }, [connection]);
+
+ return (
+
+
Typescript SDK Test!
+
{connection.identity?.toHexString()}
+
+
+ connection.reducers.createPlayer('Hello', { x: 10, y: 40 })
+ }
+ >
+ Update
+
+
+ );
+}
+
+export default App;
diff --git a/sdks/typescript/packages/test-app/src/index.css b/sdks/typescript/packages/test-app/src/index.css
new file mode 100644
index 00000000000..6119ad9a8fa
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/index.css
@@ -0,0 +1,68 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+ font-weight: 500;
+ color: #646cff;
+ text-decoration: inherit;
+}
+a:hover {
+ color: #535bf2;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ place-items: center;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+button:hover {
+ border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+ outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ color: #213547;
+ background-color: #ffffff;
+ }
+ a:hover {
+ color: #747bff;
+ }
+ button {
+ background-color: #f9f9f9;
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/main.tsx b/sdks/typescript/packages/test-app/src/main.tsx
new file mode 100644
index 00000000000..93db3799c0c
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App.tsx';
+import './index.css';
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+);
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/create_player_reducer.ts b/sdks/typescript/packages/test-app/src/module_bindings/create_player_reducer.ts
new file mode 100644
index 00000000000..55285852c94
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/create_player_reducer.ts
@@ -0,0 +1,65 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+import { Point as __Point } from './point_type';
+
+export type CreatePlayer = {
+ name: string;
+ location: __Point;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace CreatePlayer {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', __Point.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: CreatePlayer): void {
+ CreatePlayer.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): CreatePlayer {
+ return CreatePlayer.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/index.ts b/sdks/typescript/packages/test-app/src/module_bindings/index.ts
new file mode 100644
index 00000000000..86fcf287d7b
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/index.ts
@@ -0,0 +1,238 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+
+// Import and reexport all reducer arg types
+import { CreatePlayer } from './create_player_reducer.ts';
+export { CreatePlayer };
+
+// Import and reexport all table handle types
+import { PlayerTableHandle } from './player_table.ts';
+export { PlayerTableHandle };
+import { UnindexedPlayerTableHandle } from './unindexed_player_table.ts';
+export { UnindexedPlayerTableHandle };
+import { UserTableHandle } from './user_table.ts';
+export { UserTableHandle };
+
+// Import and reexport all types
+import { Player } from './player_type.ts';
+export { Player };
+import { Point } from './point_type.ts';
+export { Point };
+import { UnindexedPlayer } from './unindexed_player_type.ts';
+export { UnindexedPlayer };
+import { User } from './user_type.ts';
+export { User };
+
+const REMOTE_MODULE = {
+ tables: {
+ player: {
+ tableName: 'player',
+ rowType: Player.getTypeScriptAlgebraicType(),
+ primaryKey: 'ownerId',
+ primaryKeyInfo: {
+ colName: 'ownerId',
+ colType:
+ Player.getTypeScriptAlgebraicType().product.elements[0].algebraicType,
+ },
+ },
+ unindexed_player: {
+ tableName: 'unindexed_player',
+ rowType: UnindexedPlayer.getTypeScriptAlgebraicType(),
+ },
+ user: {
+ tableName: 'user',
+ rowType: User.getTypeScriptAlgebraicType(),
+ primaryKey: 'identity',
+ primaryKeyInfo: {
+ colName: 'identity',
+ colType:
+ User.getTypeScriptAlgebraicType().product.elements[0].algebraicType,
+ },
+ },
+ },
+ reducers: {
+ create_player: {
+ reducerName: 'create_player',
+ argsType: CreatePlayer.getTypeScriptAlgebraicType(),
+ },
+ },
+ versionInfo: {
+ cliVersion: '1.2.0',
+ },
+ // Constructors which are used by the DbConnectionImpl to
+ // extract type information from the generated RemoteModule.
+ //
+ // NOTE: This is not strictly necessary for `eventContextConstructor` because
+ // all we do is build a TypeScript object which we could have done inside the
+ // SDK, but if in the future we wanted to create a class this would be
+ // necessary because classes have methods, so we'll keep it.
+ eventContextConstructor: (imp: DbConnectionImpl, event: Event) => {
+ return {
+ ...(imp as DbConnection),
+ event,
+ };
+ },
+ dbViewConstructor: (imp: DbConnectionImpl) => {
+ return new RemoteTables(imp);
+ },
+ reducersConstructor: (
+ imp: DbConnectionImpl,
+ setReducerFlags: SetReducerFlags
+ ) => {
+ return new RemoteReducers(imp, setReducerFlags);
+ },
+ setReducerFlagsConstructor: () => {
+ return new SetReducerFlags();
+ },
+};
+
+// A type representing all the possible variants of a reducer.
+export type Reducer = never | { name: 'CreatePlayer'; args: CreatePlayer };
+
+export class RemoteReducers {
+ constructor(
+ private connection: DbConnectionImpl,
+ private setCallReducerFlags: SetReducerFlags
+ ) {}
+
+ createPlayer(name: string, location: Point) {
+ const __args = { name, location };
+ let __writer = new BinaryWriter(1024);
+ CreatePlayer.getTypeScriptAlgebraicType().serialize(__writer, __args);
+ let __argsBuffer = __writer.getBuffer();
+ this.connection.callReducer(
+ 'create_player',
+ __argsBuffer,
+ this.setCallReducerFlags.createPlayerFlags
+ );
+ }
+
+ onCreatePlayer(
+ callback: (ctx: ReducerEventContext, name: string, location: Point) => void
+ ) {
+ this.connection.onReducer('create_player', callback);
+ }
+
+ removeOnCreatePlayer(
+ callback: (ctx: ReducerEventContext, name: string, location: Point) => void
+ ) {
+ this.connection.offReducer('create_player', callback);
+ }
+}
+
+export class SetReducerFlags {
+ createPlayerFlags: CallReducerFlags = 'FullUpdate';
+ createPlayer(flags: CallReducerFlags) {
+ this.createPlayerFlags = flags;
+ }
+}
+
+export class RemoteTables {
+ constructor(private connection: DbConnectionImpl) {}
+
+ get player(): PlayerTableHandle {
+ return new PlayerTableHandle(
+ this.connection.clientCache.getOrCreateTable(
+ REMOTE_MODULE.tables.player
+ )
+ );
+ }
+
+ get unindexedPlayer(): UnindexedPlayerTableHandle {
+ return new UnindexedPlayerTableHandle(
+ this.connection.clientCache.getOrCreateTable(
+ REMOTE_MODULE.tables.unindexed_player
+ )
+ );
+ }
+
+ get user(): UserTableHandle {
+ return new UserTableHandle(
+ this.connection.clientCache.getOrCreateTable(
+ REMOTE_MODULE.tables.user
+ )
+ );
+ }
+}
+
+export class SubscriptionBuilder extends SubscriptionBuilderImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {}
+
+export class DbConnection extends DbConnectionImpl<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+> {
+ static builder = (): DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ > => {
+ return new DbConnectionBuilder<
+ DbConnection,
+ ErrorContext,
+ SubscriptionEventContext
+ >(REMOTE_MODULE, (imp: DbConnectionImpl) => imp as DbConnection);
+ };
+ subscriptionBuilder = (): SubscriptionBuilder => {
+ return new SubscriptionBuilder(this);
+ };
+}
+
+export type EventContext = EventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type ReducerEventContext = ReducerEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags,
+ Reducer
+>;
+export type SubscriptionEventContext = SubscriptionEventContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
+export type ErrorContext = ErrorContextInterface<
+ RemoteTables,
+ RemoteReducers,
+ SetReducerFlags
+>;
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/player_table.ts b/sdks/typescript/packages/test-app/src/module_bindings/player_table.ts
new file mode 100644
index 00000000000..7eec606c5d5
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/player_table.ts
@@ -0,0 +1,120 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { Player } from './player_type';
+import { Point as __Point } from './point_type';
+
+import {
+ type EventContext,
+ type Reducer,
+ RemoteReducers,
+ RemoteTables,
+} from '.';
+
+/**
+ * Table handle for the table `player`.
+ *
+ * Obtain a handle from the [`player`] property on [`RemoteTables`],
+ * like `ctx.db.player`.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.player.on_insert(...)`.
+ */
+export class PlayerTableHandle {
+ tableCache: TableCache;
+
+ constructor(tableCache: TableCache) {
+ this.tableCache = tableCache;
+ }
+
+ count(): number {
+ return this.tableCache.count();
+ }
+
+ iter(): Iterable {
+ return this.tableCache.iter();
+ }
+ /**
+ * Access to the `ownerId` unique index on the table `player`,
+ * which allows point queries on the field of the same name
+ * via the [`PlayerOwnerIdUnique.find`] method.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.player.ownerId().find(...)`.
+ *
+ * Get a handle on the `ownerId` unique index on the table `player`.
+ */
+ ownerId = {
+ // Find the subscribed row whose `ownerId` column value is equal to `col_val`,
+ // if such a row is present in the client cache.
+ find: (col_val: string): Player | undefined => {
+ for (let row of this.tableCache.iter()) {
+ if (deepEqual(row.ownerId, col_val)) {
+ return row;
+ }
+ }
+ },
+ };
+
+ onInsert = (cb: (ctx: EventContext, row: Player) => void) => {
+ return this.tableCache.onInsert(cb);
+ };
+
+ removeOnInsert = (cb: (ctx: EventContext, row: Player) => void) => {
+ return this.tableCache.removeOnInsert(cb);
+ };
+
+ onDelete = (cb: (ctx: EventContext, row: Player) => void) => {
+ return this.tableCache.onDelete(cb);
+ };
+
+ removeOnDelete = (cb: (ctx: EventContext, row: Player) => void) => {
+ return this.tableCache.removeOnDelete(cb);
+ };
+
+ // Updates are only defined for tables with primary keys.
+ onUpdate = (
+ cb: (ctx: EventContext, oldRow: Player, newRow: Player) => void
+ ) => {
+ return this.tableCache.onUpdate(cb);
+ };
+
+ removeOnUpdate = (
+ cb: (ctx: EventContext, onRow: Player, newRow: Player) => void
+ ) => {
+ return this.tableCache.removeOnUpdate(cb);
+ };
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/player_type.ts b/sdks/typescript/packages/test-app/src/module_bindings/player_type.ts
new file mode 100644
index 00000000000..884e63551ba
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/player_type.ts
@@ -0,0 +1,66 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { Point as __Point } from './point_type';
+
+export type Player = {
+ ownerId: string;
+ name: string;
+ location: __Point;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace Player {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('ownerId', AlgebraicType.createStringType()),
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', __Point.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: Player): void {
+ Player.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): Player {
+ return Player.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/point_type.ts b/sdks/typescript/packages/test-app/src/module_bindings/point_type.ts
new file mode 100644
index 00000000000..c74f56ad623
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/point_type.ts
@@ -0,0 +1,62 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+export type Point = {
+ x: number;
+ y: number;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace Point {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('x', AlgebraicType.createU16Type()),
+ new ProductTypeElement('y', AlgebraicType.createU16Type()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: Point): void {
+ Point.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): Point {
+ return Point.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_table.ts b/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_table.ts
new file mode 100644
index 00000000000..a0b3c754db9
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_table.ts
@@ -0,0 +1,85 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { UnindexedPlayer } from './unindexed_player_type';
+import { Point as __Point } from './point_type';
+
+import {
+ type EventContext,
+ type Reducer,
+ RemoteReducers,
+ RemoteTables,
+} from '.';
+
+/**
+ * Table handle for the table `unindexed_player`.
+ *
+ * Obtain a handle from the [`unindexedPlayer`] property on [`RemoteTables`],
+ * like `ctx.db.unindexedPlayer`.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.unindexedPlayer.on_insert(...)`.
+ */
+export class UnindexedPlayerTableHandle {
+ tableCache: TableCache;
+
+ constructor(tableCache: TableCache) {
+ this.tableCache = tableCache;
+ }
+
+ count(): number {
+ return this.tableCache.count();
+ }
+
+ iter(): Iterable {
+ return this.tableCache.iter();
+ }
+
+ onInsert = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => {
+ return this.tableCache.onInsert(cb);
+ };
+
+ removeOnInsert = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => {
+ return this.tableCache.removeOnInsert(cb);
+ };
+
+ onDelete = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => {
+ return this.tableCache.onDelete(cb);
+ };
+
+ removeOnDelete = (cb: (ctx: EventContext, row: UnindexedPlayer) => void) => {
+ return this.tableCache.removeOnDelete(cb);
+ };
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_type.ts b/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_type.ts
new file mode 100644
index 00000000000..82f25307017
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/unindexed_player_type.ts
@@ -0,0 +1,69 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { Point as __Point } from './point_type';
+
+export type UnindexedPlayer = {
+ ownerId: string;
+ name: string;
+ location: __Point;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace UnindexedPlayer {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('ownerId', AlgebraicType.createStringType()),
+ new ProductTypeElement('name', AlgebraicType.createStringType()),
+ new ProductTypeElement('location', __Point.getTypeScriptAlgebraicType()),
+ ]);
+ }
+
+ export function serialize(
+ writer: BinaryWriter,
+ value: UnindexedPlayer
+ ): void {
+ UnindexedPlayer.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): UnindexedPlayer {
+ return UnindexedPlayer.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/user_table.ts b/sdks/typescript/packages/test-app/src/module_bindings/user_table.ts
new file mode 100644
index 00000000000..605a14bc304
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/user_table.ts
@@ -0,0 +1,116 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+import { User } from './user_type';
+import {
+ type EventContext,
+ type Reducer,
+ RemoteReducers,
+ RemoteTables,
+} from '.';
+
+/**
+ * Table handle for the table `user`.
+ *
+ * Obtain a handle from the [`user`] property on [`RemoteTables`],
+ * like `ctx.db.user`.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.user.on_insert(...)`.
+ */
+export class UserTableHandle {
+ tableCache: TableCache;
+
+ constructor(tableCache: TableCache) {
+ this.tableCache = tableCache;
+ }
+
+ count(): number {
+ return this.tableCache.count();
+ }
+
+ iter(): Iterable {
+ return this.tableCache.iter();
+ }
+ /**
+ * Access to the `identity` unique index on the table `user`,
+ * which allows point queries on the field of the same name
+ * via the [`UserIdentityUnique.find`] method.
+ *
+ * Users are encouraged not to explicitly reference this type,
+ * but to directly chain method calls,
+ * like `ctx.db.user.identity().find(...)`.
+ *
+ * Get a handle on the `identity` unique index on the table `user`.
+ */
+ identity = {
+ // Find the subscribed row whose `identity` column value is equal to `col_val`,
+ // if such a row is present in the client cache.
+ find: (col_val: Identity): User | undefined => {
+ for (let row of this.tableCache.iter()) {
+ if (deepEqual(row.identity, col_val)) {
+ return row;
+ }
+ }
+ },
+ };
+
+ onInsert = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.onInsert(cb);
+ };
+
+ removeOnInsert = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.removeOnInsert(cb);
+ };
+
+ onDelete = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.onDelete(cb);
+ };
+
+ removeOnDelete = (cb: (ctx: EventContext, row: User) => void) => {
+ return this.tableCache.removeOnDelete(cb);
+ };
+
+ // Updates are only defined for tables with primary keys.
+ onUpdate = (cb: (ctx: EventContext, oldRow: User, newRow: User) => void) => {
+ return this.tableCache.onUpdate(cb);
+ };
+
+ removeOnUpdate = (
+ cb: (ctx: EventContext, onRow: User, newRow: User) => void
+ ) => {
+ return this.tableCache.removeOnUpdate(cb);
+ };
+}
diff --git a/sdks/typescript/packages/test-app/src/module_bindings/user_type.ts b/sdks/typescript/packages/test-app/src/module_bindings/user_type.ts
new file mode 100644
index 00000000000..4a3943a26bf
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/module_bindings/user_type.ts
@@ -0,0 +1,62 @@
+// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
+// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
+
+// This was generated using spacetimedb cli version 1.2.0 (commit fb41e50eb73573b70eea532aeb6158eaac06fae0).
+
+/* eslint-disable */
+/* tslint:disable */
+// @ts-nocheck
+import {
+ AlgebraicType,
+ AlgebraicValue,
+ BinaryReader,
+ BinaryWriter,
+ ConnectionId,
+ DbConnectionBuilder,
+ DbConnectionImpl,
+ Identity,
+ ProductType,
+ ProductTypeElement,
+ SubscriptionBuilderImpl,
+ SumType,
+ SumTypeVariant,
+ TableCache,
+ TimeDuration,
+ Timestamp,
+ deepEqual,
+ type CallReducerFlags,
+ type DbContext,
+ type ErrorContextInterface,
+ type Event,
+ type EventContextInterface,
+ type ReducerEventContextInterface,
+ type SubscriptionEventContextInterface,
+} from '@clockworklabs/spacetimedb-sdk';
+export type User = {
+ identity: Identity;
+ username: string;
+};
+
+/**
+ * A namespace for generated helper functions.
+ */
+export namespace User {
+ /**
+ * A function which returns this type represented as an AlgebraicType.
+ * This function is derived from the AlgebraicType used to generate this type.
+ */
+ export function getTypeScriptAlgebraicType(): AlgebraicType {
+ return AlgebraicType.createProductType([
+ new ProductTypeElement('identity', AlgebraicType.createIdentityType()),
+ new ProductTypeElement('username', AlgebraicType.createStringType()),
+ ]);
+ }
+
+ export function serialize(writer: BinaryWriter, value: User): void {
+ User.getTypeScriptAlgebraicType().serialize(writer, value);
+ }
+
+ export function deserialize(reader: BinaryReader): User {
+ return User.getTypeScriptAlgebraicType().deserialize(reader);
+ }
+}
diff --git a/sdks/typescript/packages/test-app/src/vite-env.d.ts b/sdks/typescript/packages/test-app/src/vite-env.d.ts
new file mode 100644
index 00000000000..11f02fe2a00
--- /dev/null
+++ b/sdks/typescript/packages/test-app/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/sdks/typescript/packages/test-app/tsconfig.app.json b/sdks/typescript/packages/test-app/tsconfig.app.json
new file mode 100644
index 00000000000..d739292ae01
--- /dev/null
+++ b/sdks/typescript/packages/test-app/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/sdks/typescript/packages/test-app/tsconfig.json b/sdks/typescript/packages/test-app/tsconfig.json
new file mode 100644
index 00000000000..ea9d0cd8255
--- /dev/null
+++ b/sdks/typescript/packages/test-app/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
diff --git a/sdks/typescript/packages/test-app/tsconfig.node.json b/sdks/typescript/packages/test-app/tsconfig.node.json
new file mode 100644
index 00000000000..3afdd6e3843
--- /dev/null
+++ b/sdks/typescript/packages/test-app/tsconfig.node.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "noEmit": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/sdks/typescript/packages/test-app/vite.config.ts b/sdks/typescript/packages/test-app/vite.config.ts
new file mode 100644
index 00000000000..627a3196243
--- /dev/null
+++ b/sdks/typescript/packages/test-app/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+});
diff --git a/sdks/typescript/pnpm-lock.yaml b/sdks/typescript/pnpm-lock.yaml
new file mode 100644
index 00000000000..fdf976654bf
--- /dev/null
+++ b/sdks/typescript/pnpm-lock.yaml
@@ -0,0 +1,5495 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ '@changesets/changelog-github':
+ specifier: ^0.5.0
+ version: 0.5.0
+ '@changesets/cli':
+ specifier: ^2.27.7
+ version: 2.27.9
+ brotli-size-cli:
+ specifier: ^1.0.0
+ version: 1.0.0
+ prettier:
+ specifier: ^3.3.3
+ version: 3.3.3
+ terser:
+ specifier: ^5.31.2
+ version: 5.34.1
+ tsup:
+ specifier: ^8.1.0
+ version: 8.3.0(postcss@8.5.1)(tsx@4.19.1)(typescript@5.6.2)
+ tsx:
+ specifier: ^4.17.0
+ version: 4.19.1
+ typescript:
+ specifier: ^5.5.3
+ version: 5.6.2
+ vitest:
+ specifier: ^2.0.3
+ version: 2.1.2(@types/node@22.15.0)(jsdom@26.0.0)(terser@5.34.1)
+
+ examples/quickstart-chat:
+ dependencies:
+ '@clockworklabs/spacetimedb-sdk':
+ specifier: workspace:*
+ version: link:../../packages/sdk
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.17.0
+ version: 9.18.0
+ '@testing-library/jest-dom':
+ specifier: ^6.6.3
+ version: 6.6.3
+ '@testing-library/react':
+ specifier: ^16.2.0
+ version: 16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.0)
+ '@types/jest':
+ specifier: ^29.5.14
+ version: 29.5.14
+ '@types/react':
+ specifier: ^18.3.18
+ version: 18.3.18
+ '@types/react-dom':
+ specifier: ^18.3.5
+ version: 18.3.5(@types/react@18.3.18)
+ '@vitejs/plugin-react':
+ specifier: ^4.3.4
+ version: 4.3.4(vite@6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1))
+ eslint:
+ specifier: ^9.17.0
+ version: 9.18.0
+ eslint-plugin-react-hooks:
+ specifier: ^5.0.0
+ version: 5.1.0(eslint@9.18.0)
+ eslint-plugin-react-refresh:
+ specifier: ^0.4.16
+ version: 0.4.18(eslint@9.18.0)
+ globals:
+ specifier: ^15.14.0
+ version: 15.14.0
+ jsdom:
+ specifier: ^26.0.0
+ version: 26.0.0
+ typescript:
+ specifier: ~5.6.2
+ version: 5.6.2
+ typescript-eslint:
+ specifier: ^8.18.2
+ version: 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ vite:
+ specifier: ^6.0.5
+ version: 6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1)
+
+ examples/repro-07032025:
+ dependencies:
+ '@clockworklabs/spacetimedb-sdk':
+ specifier: workspace:*
+ version: link:../../packages/sdk
+ devDependencies:
+ '@types/node':
+ specifier: ^22.13.9
+ version: 22.15.0
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@types/node@22.15.0)(typescript@5.8.3)
+ typescript:
+ specifier: ^5.8.2
+ version: 5.8.3
+
+ examples/repro2:
+ dependencies:
+ '@clockworklabs/spacetimedb-sdk':
+ specifier: workspace:*
+ version: link:../../packages/sdk
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.17.0
+ version: 9.18.0
+ '@testing-library/jest-dom':
+ specifier: ^6.6.3
+ version: 6.6.3
+ '@testing-library/react':
+ specifier: ^16.2.0
+ version: 16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.0)
+ '@types/jest':
+ specifier: ^29.5.14
+ version: 29.5.14
+ '@types/react':
+ specifier: ^18.3.18
+ version: 18.3.18
+ '@types/react-dom':
+ specifier: ^18.3.5
+ version: 18.3.5(@types/react@18.3.18)
+ '@vitejs/plugin-react':
+ specifier: ^4.3.4
+ version: 4.3.4(vite@6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1))
+ eslint:
+ specifier: ^9.17.0
+ version: 9.18.0
+ eslint-plugin-react-hooks:
+ specifier: ^5.0.0
+ version: 5.1.0(eslint@9.18.0)
+ eslint-plugin-react-refresh:
+ specifier: ^0.4.16
+ version: 0.4.18(eslint@9.18.0)
+ globals:
+ specifier: ^15.14.0
+ version: 15.14.0
+ jsdom:
+ specifier: ^26.0.0
+ version: 26.0.0
+ typescript:
+ specifier: ~5.6.2
+ version: 5.6.2
+ typescript-eslint:
+ specifier: ^8.18.2
+ version: 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ vite:
+ specifier: ^6.0.5
+ version: 6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1)
+
+ packages/sdk:
+ dependencies:
+ base64-js:
+ specifier: ^1.5.1
+ version: 1.5.1
+ devDependencies:
+ '@clockworklabs/test-app':
+ specifier: file:../test-app
+ version: file:packages/test-app
+ tsup:
+ specifier: ^8.1.0
+ version: 8.3.0(postcss@8.5.1)(tsx@4.19.1)(typescript@5.8.3)
+ undici:
+ specifier: ^6.19.2
+ version: 6.19.8
+
+ packages/test-app:
+ dependencies:
+ '@clockworklabs/spacetimedb-sdk':
+ specifier: workspace:*
+ version: link:../sdk
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ devDependencies:
+ '@types/react':
+ specifier: ^18.3.3
+ version: 18.3.11
+ '@types/react-dom':
+ specifier: ^18.3.0
+ version: 18.3.0
+ '@vitejs/plugin-react':
+ specifier: ^4.3.1
+ version: 4.3.2(vite@5.4.8(@types/node@22.15.0)(terser@5.34.1))
+ typescript:
+ specifier: ^5.2.2
+ version: 5.6.2
+ vite:
+ specifier: ^5.3.4
+ version: 5.4.8(@types/node@22.15.0)(terser@5.34.1)
+
+packages:
+
+ '@adobe/css-tools@4.4.1':
+ resolution: {integrity: sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==}
+
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
+ '@asamuzakjp/css-color@2.8.3':
+ resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==}
+
+ '@babel/code-frame@7.25.7':
+ resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/code-frame@7.26.2':
+ resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.25.7':
+ resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.26.5':
+ resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.25.7':
+ resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.26.0':
+ resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.25.7':
+ resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.26.5':
+ resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.25.7':
+ resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.26.5':
+ resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.7':
+ resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.25.7':
+ resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.25.7':
+ resolution: {integrity: sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.26.5':
+ resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-simple-access@7.25.7':
+ resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.7':
+ resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.25.7':
+ resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.25.9':
+ resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.7':
+ resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.25.7':
+ resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.26.0':
+ resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/highlight@7.25.7':
+ resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.25.7':
+ resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/parser@7.26.5':
+ resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.25.7':
+ resolution: {integrity: sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9':
+ resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.25.7':
+ resolution: {integrity: sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9':
+ resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.25.7':
+ resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.25.7':
+ resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.25.9':
+ resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.25.7':
+ resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.26.5':
+ resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.25.7':
+ resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.26.5':
+ resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==}
+ engines: {node: '>=6.9.0'}
+
+ '@changesets/apply-release-plan@7.0.5':
+ resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==}
+
+ '@changesets/assemble-release-plan@6.0.4':
+ resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==}
+
+ '@changesets/changelog-git@0.2.0':
+ resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==}
+
+ '@changesets/changelog-github@0.5.0':
+ resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==}
+
+ '@changesets/cli@2.27.9':
+ resolution: {integrity: sha512-q42a/ZbDnxPpCb5Wkm6tMVIxgeI9C/bexntzTeCFBrQEdpisQqk8kCHllYZMDjYtEc1ZzumbMJAG8H0Z4rdvjg==}
+ hasBin: true
+
+ '@changesets/config@3.0.3':
+ resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==}
+
+ '@changesets/errors@0.2.0':
+ resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==}
+
+ '@changesets/get-dependents-graph@2.1.2':
+ resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==}
+
+ '@changesets/get-github-info@0.6.0':
+ resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==}
+
+ '@changesets/get-release-plan@4.0.4':
+ resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==}
+
+ '@changesets/get-version-range-type@0.4.0':
+ resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==}
+
+ '@changesets/git@3.0.1':
+ resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==}
+
+ '@changesets/logger@0.1.1':
+ resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==}
+
+ '@changesets/parse@0.4.0':
+ resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==}
+
+ '@changesets/pre@2.0.1':
+ resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==}
+
+ '@changesets/read@0.6.1':
+ resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==}
+
+ '@changesets/should-skip-package@0.1.1':
+ resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==}
+
+ '@changesets/types@4.1.0':
+ resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==}
+
+ '@changesets/types@6.0.0':
+ resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==}
+
+ '@changesets/write@0.3.2':
+ resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==}
+
+ '@clockworklabs/test-app@file:packages/test-app':
+ resolution: {directory: packages/test-app, type: directory}
+
+ '@cspotcode/source-map-support@0.8.1':
+ resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
+ engines: {node: '>=12'}
+
+ '@csstools/color-helpers@5.0.1':
+ resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.1':
+ resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-color-parser@3.0.7':
+ resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.4
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4':
+ resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.3
+
+ '@csstools/css-tokenizer@3.0.3':
+ resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
+ engines: {node: '>=18'}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.23.1':
+ resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.24.2':
+ resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.23.1':
+ resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.24.2':
+ resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.23.1':
+ resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.24.2':
+ resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.23.1':
+ resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.24.2':
+ resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.23.1':
+ resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.24.2':
+ resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.23.1':
+ resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.24.2':
+ resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.23.1':
+ resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.24.2':
+ resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.23.1':
+ resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.24.2':
+ resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.23.1':
+ resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.24.2':
+ resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.23.1':
+ resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.24.2':
+ resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.23.1':
+ resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.24.2':
+ resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.23.1':
+ resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.24.2':
+ resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.23.1':
+ resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.24.2':
+ resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.23.1':
+ resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.24.2':
+ resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.23.1':
+ resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.24.2':
+ resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.23.1':
+ resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.24.2':
+ resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.23.1':
+ resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.24.2':
+ resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.24.2':
+ resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.23.1':
+ resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.24.2':
+ resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.23.1':
+ resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-arm64@0.24.2':
+ resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.23.1':
+ resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.24.2':
+ resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.23.1':
+ resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.24.2':
+ resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.23.1':
+ resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.24.2':
+ resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.23.1':
+ resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.24.2':
+ resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.23.1':
+ resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.24.2':
+ resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.4.1':
+ resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.19.1':
+ resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.10.0':
+ resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.2.0':
+ resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.18.0':
+ resolution: {integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.5':
+ resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.2.5':
+ resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@humanfs/core@0.19.1':
+ resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.6':
+ resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.3.1':
+ resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
+ engines: {node: '>=18.18'}
+
+ '@humanwhocodes/retry@0.4.1':
+ resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
+ engines: {node: '>=18.18'}
+
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@jest/expect-utils@29.7.0':
+ resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/schemas@29.6.3':
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jest/types@29.6.3':
+ resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ '@jridgewell/gen-mapping@0.3.5':
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/set-array@1.2.1':
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/source-map@0.3.6':
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
+ '@jridgewell/sourcemap-codec@1.5.0':
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+
+ '@jridgewell/trace-mapping@0.3.9':
+ resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+
+ '@manypkg/find-root@1.1.0':
+ resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
+
+ '@manypkg/get-packages@1.1.3':
+ resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==}
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@rollup/rollup-android-arm-eabi@4.24.0':
+ resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.24.0':
+ resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.24.0':
+ resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.24.0':
+ resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.24.0':
+ resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.24.0':
+ resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.24.0':
+ resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.24.0':
+ resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.24.0':
+ resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.24.0':
+ resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.24.0':
+ resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.24.0':
+ resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.24.0':
+ resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-win32-arm64-msvc@4.24.0':
+ resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.24.0':
+ resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.24.0':
+ resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@sinclair/typebox@0.27.8':
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+
+ '@testing-library/dom@10.4.0':
+ resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.6.3':
+ resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.2.0':
+ resolution: {integrity: sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
+ '@tsconfig/node10@1.0.11':
+ resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
+
+ '@tsconfig/node12@1.0.11':
+ resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
+
+ '@tsconfig/node14@1.0.3':
+ resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
+
+ '@tsconfig/node16@1.0.4':
+ resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
+
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.6.8':
+ resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.20.6':
+ resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+ '@types/istanbul-lib-coverage@2.0.6':
+ resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+ '@types/istanbul-lib-report@3.0.3':
+ resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+ '@types/istanbul-reports@3.0.4':
+ resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+ '@types/jest@29.5.14':
+ resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/node@12.20.55':
+ resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
+
+ '@types/node@22.15.0':
+ resolution: {integrity: sha512-99S8dWD2DkeE6PBaEDw+In3aar7hdoBvjyJMR6vaKBTzpvR0P00ClzJMOoVrj9D2+Sy/YCwACYHnBTpMhg1UCA==}
+
+ '@types/prop-types@15.7.13':
+ resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
+
+ '@types/react-dom@18.3.0':
+ resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
+
+ '@types/react-dom@18.3.5':
+ resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==}
+ peerDependencies:
+ '@types/react': ^18.0.0
+
+ '@types/react@18.3.11':
+ resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==}
+
+ '@types/react@18.3.18':
+ resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==}
+
+ '@types/stack-utils@2.0.3':
+ resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+ '@types/yargs-parser@21.0.3':
+ resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+ '@types/yargs@17.0.33':
+ resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==}
+
+ '@typescript-eslint/eslint-plugin@8.21.0':
+ resolution: {integrity: sha512-eTH+UOR4I7WbdQnG4Z48ebIA6Bgi7WO8HvFEneeYBxG8qCOYgTOFPSg6ek9ITIDvGjDQzWHcoWHCDO2biByNzA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
+ '@typescript-eslint/parser@8.21.0':
+ resolution: {integrity: sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
+ '@typescript-eslint/scope-manager@8.21.0':
+ resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.21.0':
+ resolution: {integrity: sha512-95OsL6J2BtzoBxHicoXHxgk3z+9P3BEcQTpBKriqiYzLKnM2DeSqs+sndMKdamU8FosiadQFT3D+BSL9EKnAJQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
+ '@typescript-eslint/types@8.21.0':
+ resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.21.0':
+ resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.8.0'
+
+ '@typescript-eslint/utils@8.21.0':
+ resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
+ '@typescript-eslint/visitor-keys@8.21.0':
+ resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@vitejs/plugin-react@4.3.2':
+ resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0
+
+ '@vitejs/plugin-react@4.3.4':
+ resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0
+
+ '@vitest/expect@2.1.2':
+ resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==}
+
+ '@vitest/mocker@2.1.2':
+ resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==}
+ peerDependencies:
+ '@vitest/spy': 2.1.2
+ msw: ^2.3.5
+ vite: ^5.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@2.1.2':
+ resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==}
+
+ '@vitest/runner@2.1.2':
+ resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==}
+
+ '@vitest/snapshot@2.1.2':
+ resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==}
+
+ '@vitest/spy@2.1.2':
+ resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==}
+
+ '@vitest/utils@2.1.2':
+ resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn-walk@8.3.4:
+ resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
+ engines: {node: '>=0.4.0'}
+
+ acorn@8.12.1:
+ resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ acorn@8.14.0:
+ resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.3:
+ resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+ engines: {node: '>= 14'}
+
+ ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+
+ ansi-colors@4.1.3:
+ resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
+ engines: {node: '>=6'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+
+ ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
+ arg@4.1.3:
+ resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
+
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ better-path-resolve@1.0.0:
+ resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
+ engines: {node: '>=4'}
+
+ binary-extensions@2.3.0:
+ resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+ engines: {node: '>=8'}
+
+ brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+
+ brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ brotli-size-cli@1.0.0:
+ resolution: {integrity: sha512-eX+2WEZDvjWg045PxVN3eDYWCqyNuKmunjCZX9Ps0wohWMxGfiMP+efWVAuU/2kL81A4qfmHOAWWKFRV56XkCw==}
+ engines: {node: '>=13'}
+ hasBin: true
+
+ brotli-size@4.0.0:
+ resolution: {integrity: sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==}
+ engines: {node: '>= 10.16.0'}
+
+ browserslist@4.24.0:
+ resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ bundle-require@5.0.0:
+ resolution: {integrity: sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ peerDependencies:
+ esbuild: '>=0.18'
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001667:
+ resolution: {integrity: sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==}
+
+ chai@5.1.1:
+ resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
+ engines: {node: '>=12'}
+
+ chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+
+ chalk@3.0.0:
+ resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
+ engines: {node: '>=8'}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chardet@0.7.0:
+ resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
+
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
+ chokidar@3.6.0:
+ resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+ engines: {node: '>= 8.10.0'}
+
+ ci-info@3.9.0:
+ resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==}
+ engines: {node: '>=8'}
+
+ color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+ commander@4.1.1:
+ resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+ engines: {node: '>= 6'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ consola@3.2.3:
+ resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ create-require@1.1.1:
+ resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+
+ cross-spawn@5.1.0:
+ resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+
+ cross-spawn@7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ cssstyle@4.2.1:
+ resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==}
+ engines: {node: '>=18'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
+ dataloader@1.4.0:
+ resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.5.0:
+ resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==}
+
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-indent@6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+
+ diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ diff@4.0.2:
+ resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
+ engines: {node: '>=0.3.1'}
+
+ dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
+ dotenv@8.6.0:
+ resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
+ engines: {node: '>=10'}
+
+ duplexer@0.1.1:
+ resolution: {integrity: sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==}
+
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ electron-to-chromium@1.5.33:
+ resolution: {integrity: sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ enquirer@2.4.1:
+ resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
+ engines: {node: '>=8.6'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.23.1:
+ resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ esbuild@0.24.2:
+ resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-plugin-react-hooks@5.1.0:
+ resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
+
+ eslint-plugin-react-refresh@0.4.18:
+ resolution: {integrity: sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==}
+ peerDependencies:
+ eslint: '>=8.40'
+
+ eslint-scope@8.2.0:
+ resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.0:
+ resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.18.0:
+ resolution: {integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.3.0:
+ resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ esquery@1.6.0:
+ resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
+ expect@29.7.0:
+ resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ extendable-error@0.1.7:
+ resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
+
+ external-editor@3.1.0:
+ resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
+ engines: {node: '>=4'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.17.1:
+ resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
+
+ fdir@6.4.0:
+ resolution: {integrity: sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.3.2:
+ resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
+
+ foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+
+ form-data@4.0.1:
+ resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
+ engines: {node: '>= 6'}
+
+ fs-extra@7.0.1:
+ resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fs-extra@8.1.0:
+ resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+ engines: {node: '>=6 <7 || >=8'}
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
+ get-tsconfig@4.8.1:
+ resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@15.14.0:
+ resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==}
+ engines: {node: '>=18'}
+
+ globby@11.1.0:
+ resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+ engines: {node: '>=10'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graphemer@1.4.0:
+ resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+
+ has-flag@3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ human-id@1.0.2:
+ resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
+
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
+ iconv-lite@0.4.24:
+ resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+ engines: {node: '>=0.10.0'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ is-subdir@1.2.0:
+ resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==}
+ engines: {node: '>=4'}
+
+ is-windows@1.0.2:
+ resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
+ engines: {node: '>=0.10.0'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jest-diff@29.7.0:
+ resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-get-type@29.6.3:
+ resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-matcher-utils@29.7.0:
+ resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-message-util@29.7.0:
+ resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ jest-util@29.7.0:
+ resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ joycon@3.1.1:
+ resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
+ engines: {node: '>=10'}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsdom@26.0.0:
+ resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ jsesc@3.0.2:
+ resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lilconfig@3.1.2:
+ resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
+ engines: {node: '>=14'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ load-tsconfig@0.2.5:
+ resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.sortby@4.7.0:
+ resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+
+ lodash.startcase@4.4.0:
+ resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==}
+
+ lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ loupe@3.1.2:
+ resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
+
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@4.1.5:
+ resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
+ magic-string@0.30.11:
+ resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+
+ make-error@1.3.6:
+ resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+
+ nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ nanoid@3.3.8:
+ resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
+ node-releases@2.0.18:
+ resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
+ nwsapi@2.2.16:
+ resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ os-tmpdir@1.0.2:
+ resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
+ engines: {node: '>=0.10.0'}
+
+ outdent@0.5.0:
+ resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==}
+
+ p-filter@2.1.0:
+ resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
+ engines: {node: '>=8'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ p-map@2.1.0:
+ resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
+ engines: {node: '>=6'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ package-manager-detector@0.2.1:
+ resolution: {integrity: sha512-/hVW2fZvAdEas+wyKh0SnlZ2mx0NIa1+j11YaQkogEJkcMErbwchHCuo8z7lEtajZJQZ6rgZNVTWMVVd71Bjng==}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse5@7.2.1:
+ resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+
+ picocolors@1.1.0:
+ resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.2:
+ resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
+ engines: {node: '>=12'}
+
+ pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+
+ pirates@4.0.6:
+ resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+ engines: {node: '>= 6'}
+
+ postcss-load-config@6.0.1:
+ resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ jiti: '>=1.21.0'
+ postcss: '>=8.0.9'
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+ postcss:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ postcss@8.4.47:
+ resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.1:
+ resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ prettier@3.3.3:
+ resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ pretty-bytes@5.6.0:
+ resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
+ engines: {node: '>=6'}
+
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
+ pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+
+ pseudomap@1.0.2:
+ resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@18.3.1:
+ resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
+ peerDependencies:
+ react: ^18.3.1
+
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ react-refresh@0.14.2:
+ resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
+ engines: {node: '>=0.10.0'}
+
+ react@18.3.1:
+ resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
+ engines: {node: '>=0.10.0'}
+
+ read-yaml-file@1.1.0:
+ resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
+ engines: {node: '>=6'}
+
+ readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
+ regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rollup@4.24.0:
+ resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ sade@1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ scheduler@0.23.2:
+ resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.6.3:
+ resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ shebang-command@1.2.0:
+ resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+ engines: {node: '>=0.10.0'}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@1.0.0:
+ resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+ engines: {node: '>=0.10.0'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.8.0-beta.0:
+ resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
+ engines: {node: '>= 8'}
+
+ spawndamnit@2.0.0:
+ resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==}
+
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.7.0:
+ resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ sucrase@3.35.0:
+ resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+
+ supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ term-size@2.2.1:
+ resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
+ engines: {node: '>=8'}
+
+ terser@5.34.1:
+ resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+
+ thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@0.3.0:
+ resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==}
+
+ tinyglobby@0.2.9:
+ resolution: {integrity: sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw==}
+ engines: {node: '>=12.0.0'}
+
+ tinypool@1.0.1:
+ resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@1.2.0:
+ resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@6.1.75:
+ resolution: {integrity: sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==}
+
+ tldts@6.1.75:
+ resolution: {integrity: sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==}
+ hasBin: true
+
+ tmp@0.0.33:
+ resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
+ engines: {node: '>=0.6.0'}
+
+ to-fast-properties@2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ tough-cookie@5.1.0:
+ resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==}
+ engines: {node: '>=16'}
+
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
+ tr46@1.0.1:
+ resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
+
+ tr46@5.0.0:
+ resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+ engines: {node: '>=18'}
+
+ tree-kill@1.2.2:
+ resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
+ hasBin: true
+
+ ts-api-utils@2.0.0:
+ resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ ts-node@10.9.2:
+ resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': '>=1.2.50'
+ '@swc/wasm': '>=1.2.50'
+ '@types/node': '*'
+ typescript: '>=2.7'
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ '@swc/wasm':
+ optional: true
+
+ tsup@8.3.0:
+ resolution: {integrity: sha512-ALscEeyS03IomcuNdFdc0YWGVIkwH1Ws7nfTbAPuoILvEV2hpGQAY72LIOjglGo4ShWpZfpBqP/jpQVCzqYQag==}
+ engines: {node: '>=18'}
+ hasBin: true
+ peerDependencies:
+ '@microsoft/api-extractor': ^7.36.0
+ '@swc/core': ^1
+ postcss: ^8.4.12
+ typescript: '>=4.5.0'
+ peerDependenciesMeta:
+ '@microsoft/api-extractor':
+ optional: true
+ '@swc/core':
+ optional: true
+ postcss:
+ optional: true
+ typescript:
+ optional: true
+
+ tsx@4.19.1:
+ resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typescript-eslint@8.21.0:
+ resolution: {integrity: sha512-txEKYY4XMKwPXxNkN8+AxAdX6iIJAPiJbHE/FpQccs/sxw8Lf26kqwC3cn0xkHlW8kEbLhkhCsjWuMveaY9Rxw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
+ typescript@5.6.2:
+ resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ undici@6.19.8:
+ resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==}
+ engines: {node: '>=18.17'}
+
+ universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+
+ update-browserslist-db@1.1.1:
+ resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ v8-compile-cache-lib@3.0.1:
+ resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+
+ vite-node@2.1.2:
+ resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
+ vite@5.4.8:
+ resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vite@6.0.11:
+ resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vitest@2.1.2:
+ resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': 2.1.2
+ '@vitest/ui': 2.1.2
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
+ webidl-conversions@4.0.2:
+ resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
+
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.1.0:
+ resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==}
+ engines: {node: '>=18'}
+
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
+ whatwg-url@7.1.0:
+ resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
+
+ which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yallist@2.1.2:
+ resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yn@3.1.1:
+ resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
+ engines: {node: '>=6'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+snapshots:
+
+ '@adobe/css-tools@4.4.1': {}
+
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@asamuzakjp/css-color@2.8.3':
+ dependencies:
+ '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+ lru-cache: 10.4.3
+
+ '@babel/code-frame@7.25.7':
+ dependencies:
+ '@babel/highlight': 7.25.7
+ picocolors: 1.1.0
+
+ '@babel/code-frame@7.26.2':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.25.9
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.25.7': {}
+
+ '@babel/compat-data@7.26.5': {}
+
+ '@babel/core@7.25.7':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.25.7
+ '@babel/generator': 7.25.7
+ '@babel/helper-compilation-targets': 7.25.7
+ '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7)
+ '@babel/helpers': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/template': 7.25.7
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ convert-source-map: 2.0.0
+ debug: 4.3.7
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/core@7.26.0':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.5
+ '@babel/helper-compilation-targets': 7.26.5
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helpers': 7.26.0
+ '@babel/parser': 7.26.5
+ '@babel/template': 7.25.9
+ '@babel/traverse': 7.26.5
+ '@babel/types': 7.26.5
+ convert-source-map: 2.0.0
+ debug: 4.3.7
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.25.7':
+ dependencies:
+ '@babel/types': 7.25.7
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.0.2
+
+ '@babel/generator@7.26.5':
+ dependencies:
+ '@babel/parser': 7.26.5
+ '@babel/types': 7.26.5
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.0.2
+
+ '@babel/helper-compilation-targets@7.25.7':
+ dependencies:
+ '@babel/compat-data': 7.25.7
+ '@babel/helper-validator-option': 7.25.7
+ browserslist: 4.24.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-compilation-targets@7.26.5':
+ dependencies:
+ '@babel/compat-data': 7.26.5
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-module-imports@7.25.7':
+ dependencies:
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.26.5
+ '@babel/types': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.7)':
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/helper-module-imports': 7.25.7
+ '@babel/helper-simple-access': 7.25.7
+ '@babel/helper-validator-identifier': 7.25.7
+ '@babel/traverse': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ '@babel/traverse': 7.26.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.25.7': {}
+
+ '@babel/helper-plugin-utils@7.26.5': {}
+
+ '@babel/helper-simple-access@7.25.7':
+ dependencies:
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.25.7': {}
+
+ '@babel/helper-string-parser@7.25.9': {}
+
+ '@babel/helper-validator-identifier@7.25.7': {}
+
+ '@babel/helper-validator-identifier@7.25.9': {}
+
+ '@babel/helper-validator-option@7.25.7': {}
+
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helpers@7.25.7':
+ dependencies:
+ '@babel/template': 7.25.7
+ '@babel/types': 7.25.7
+
+ '@babel/helpers@7.26.0':
+ dependencies:
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.5
+
+ '@babel/highlight@7.25.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.25.7
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ picocolors: 1.1.0
+
+ '@babel/parser@7.25.7':
+ dependencies:
+ '@babel/types': 7.25.7
+
+ '@babel/parser@7.26.5':
+ dependencies:
+ '@babel/types': 7.26.5
+
+ '@babel/plugin-transform-react-jsx-self@7.25.7(@babel/core@7.25.7)':
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/helper-plugin-utils': 7.25.7
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/plugin-transform-react-jsx-source@7.25.7(@babel/core@7.25.7)':
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/helper-plugin-utils': 7.25.7
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.26.5
+
+ '@babel/runtime@7.25.7':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
+ '@babel/template@7.25.7':
+ dependencies:
+ '@babel/code-frame': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/types': 7.25.7
+
+ '@babel/template@7.25.9':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/parser': 7.26.5
+ '@babel/types': 7.26.5
+
+ '@babel/traverse@7.25.7':
+ dependencies:
+ '@babel/code-frame': 7.25.7
+ '@babel/generator': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/template': 7.25.7
+ '@babel/types': 7.25.7
+ debug: 4.3.7
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/traverse@7.26.5':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.5
+ '@babel/parser': 7.26.5
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.5
+ debug: 4.3.7
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.25.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.7
+ '@babel/helper-validator-identifier': 7.25.7
+ to-fast-properties: 2.0.0
+
+ '@babel/types@7.26.5':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+
+ '@changesets/apply-release-plan@7.0.5':
+ dependencies:
+ '@changesets/config': 3.0.3
+ '@changesets/get-version-range-type': 0.4.0
+ '@changesets/git': 3.0.1
+ '@changesets/should-skip-package': 0.1.1
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+ detect-indent: 6.1.0
+ fs-extra: 7.0.1
+ lodash.startcase: 4.4.0
+ outdent: 0.5.0
+ prettier: 2.8.8
+ resolve-from: 5.0.0
+ semver: 7.6.3
+
+ '@changesets/assemble-release-plan@6.0.4':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.2
+ '@changesets/should-skip-package': 0.1.1
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+ semver: 7.6.3
+
+ '@changesets/changelog-git@0.2.0':
+ dependencies:
+ '@changesets/types': 6.0.0
+
+ '@changesets/changelog-github@0.5.0':
+ dependencies:
+ '@changesets/get-github-info': 0.6.0
+ '@changesets/types': 6.0.0
+ dotenv: 8.6.0
+ transitivePeerDependencies:
+ - encoding
+
+ '@changesets/cli@2.27.9':
+ dependencies:
+ '@changesets/apply-release-plan': 7.0.5
+ '@changesets/assemble-release-plan': 6.0.4
+ '@changesets/changelog-git': 0.2.0
+ '@changesets/config': 3.0.3
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.2
+ '@changesets/get-release-plan': 4.0.4
+ '@changesets/git': 3.0.1
+ '@changesets/logger': 0.1.1
+ '@changesets/pre': 2.0.1
+ '@changesets/read': 0.6.1
+ '@changesets/should-skip-package': 0.1.1
+ '@changesets/types': 6.0.0
+ '@changesets/write': 0.3.2
+ '@manypkg/get-packages': 1.1.3
+ ansi-colors: 4.1.3
+ ci-info: 3.9.0
+ enquirer: 2.4.1
+ external-editor: 3.1.0
+ fs-extra: 7.0.1
+ mri: 1.2.0
+ p-limit: 2.3.0
+ package-manager-detector: 0.2.1
+ picocolors: 1.1.0
+ resolve-from: 5.0.0
+ semver: 7.6.3
+ spawndamnit: 2.0.0
+ term-size: 2.2.1
+
+ '@changesets/config@3.0.3':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/get-dependents-graph': 2.1.2
+ '@changesets/logger': 0.1.1
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+ fs-extra: 7.0.1
+ micromatch: 4.0.8
+
+ '@changesets/errors@0.2.0':
+ dependencies:
+ extendable-error: 0.1.7
+
+ '@changesets/get-dependents-graph@2.1.2':
+ dependencies:
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+ picocolors: 1.1.0
+ semver: 7.6.3
+
+ '@changesets/get-github-info@0.6.0':
+ dependencies:
+ dataloader: 1.4.0
+ node-fetch: 2.7.0
+ transitivePeerDependencies:
+ - encoding
+
+ '@changesets/get-release-plan@4.0.4':
+ dependencies:
+ '@changesets/assemble-release-plan': 6.0.4
+ '@changesets/config': 3.0.3
+ '@changesets/pre': 2.0.1
+ '@changesets/read': 0.6.1
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+
+ '@changesets/get-version-range-type@0.4.0': {}
+
+ '@changesets/git@3.0.1':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@manypkg/get-packages': 1.1.3
+ is-subdir: 1.2.0
+ micromatch: 4.0.8
+ spawndamnit: 2.0.0
+
+ '@changesets/logger@0.1.1':
+ dependencies:
+ picocolors: 1.1.0
+
+ '@changesets/parse@0.4.0':
+ dependencies:
+ '@changesets/types': 6.0.0
+ js-yaml: 3.14.1
+
+ '@changesets/pre@2.0.1':
+ dependencies:
+ '@changesets/errors': 0.2.0
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+ fs-extra: 7.0.1
+
+ '@changesets/read@0.6.1':
+ dependencies:
+ '@changesets/git': 3.0.1
+ '@changesets/logger': 0.1.1
+ '@changesets/parse': 0.4.0
+ '@changesets/types': 6.0.0
+ fs-extra: 7.0.1
+ p-filter: 2.1.0
+ picocolors: 1.1.0
+
+ '@changesets/should-skip-package@0.1.1':
+ dependencies:
+ '@changesets/types': 6.0.0
+ '@manypkg/get-packages': 1.1.3
+
+ '@changesets/types@4.1.0': {}
+
+ '@changesets/types@6.0.0': {}
+
+ '@changesets/write@0.3.2':
+ dependencies:
+ '@changesets/types': 6.0.0
+ fs-extra: 7.0.1
+ human-id: 1.0.2
+ prettier: 2.8.8
+
+ '@clockworklabs/test-app@file:packages/test-app':
+ dependencies:
+ '@clockworklabs/spacetimedb-sdk': link:packages/sdk
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
+ '@cspotcode/source-map-support@0.8.1':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.9
+
+ '@csstools/color-helpers@5.0.1': {}
+
+ '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/color-helpers': 5.0.1
+ '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.3
+
+ '@csstools/css-tokenizer@3.0.3': {}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.23.1':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.24.2':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/android-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.23.1':
+ optional: true
+
+ '@esbuild/android-arm@0.24.2':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.23.1':
+ optional: true
+
+ '@esbuild/android-x64@0.24.2':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.23.1':
+ optional: true
+
+ '@esbuild/darwin-x64@0.24.2':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.23.1':
+ optional: true
+
+ '@esbuild/linux-arm@0.24.2':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.23.1':
+ optional: true
+
+ '@esbuild/linux-ia32@0.24.2':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-loong64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.23.1':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.24.2':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.24.2':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.23.1':
+ optional: true
+
+ '@esbuild/linux-s390x@0.24.2':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.23.1':
+ optional: true
+
+ '@esbuild/linux-x64@0.24.2':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.23.1':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.24.2':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.23.1':
+ optional: true
+
+ '@esbuild/sunos-x64@0.24.2':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.23.1':
+ optional: true
+
+ '@esbuild/win32-arm64@0.24.2':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.23.1':
+ optional: true
+
+ '@esbuild/win32-ia32@0.24.2':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.23.1':
+ optional: true
+
+ '@esbuild/win32-x64@0.24.2':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.4.1(eslint@9.18.0)':
+ dependencies:
+ eslint: 9.18.0
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.19.1':
+ dependencies:
+ '@eslint/object-schema': 2.1.5
+ debug: 4.3.7
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/core@0.10.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.2.0':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.3.7
+ espree: 10.3.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.0
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.18.0': {}
+
+ '@eslint/object-schema@2.1.5': {}
+
+ '@eslint/plugin-kit@0.2.5':
+ dependencies:
+ '@eslint/core': 0.10.0
+ levn: 0.4.1
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.6':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.3.1
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.3.1': {}
+
+ '@humanwhocodes/retry@0.4.1': {}
+
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@jest/expect-utils@29.7.0':
+ dependencies:
+ jest-get-type: 29.6.3
+
+ '@jest/schemas@29.6.3':
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+
+ '@jest/types@29.6.3':
+ dependencies:
+ '@jest/schemas': 29.6.3
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 22.15.0
+ '@types/yargs': 17.0.33
+ chalk: 4.1.2
+
+ '@jridgewell/gen-mapping@0.3.5':
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/set-array@1.2.1': {}
+
+ '@jridgewell/source-map@0.3.6':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
+ '@jridgewell/sourcemap-codec@1.5.0': {}
+
+ '@jridgewell/trace-mapping@0.3.25':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@jridgewell/trace-mapping@0.3.9':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ '@manypkg/find-root@1.1.0':
+ dependencies:
+ '@babel/runtime': 7.25.7
+ '@types/node': 12.20.55
+ find-up: 4.1.0
+ fs-extra: 8.1.0
+
+ '@manypkg/get-packages@1.1.3':
+ dependencies:
+ '@babel/runtime': 7.25.7
+ '@changesets/types': 4.1.0
+ '@manypkg/find-root': 1.1.0
+ fs-extra: 8.1.0
+ globby: 11.1.0
+ read-yaml-file: 1.1.0
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.17.1
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.24.0':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.24.0':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.24.0':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-powerpc64le-gnu@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.24.0':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.24.0':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.24.0':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.24.0':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.24.0':
+ optional: true
+
+ '@sinclair/typebox@0.27.8': {}
+
+ '@testing-library/dom@10.4.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/runtime': 7.25.7
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ chalk: 4.1.2
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.6.3':
+ dependencies:
+ '@adobe/css-tools': 4.4.1
+ aria-query: 5.3.2
+ chalk: 3.0.0
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ lodash: 4.17.21
+ redent: 3.0.0
+
+ '@testing-library/react@16.2.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.25.7
+ '@testing-library/dom': 10.4.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.18
+ '@types/react-dom': 18.3.5(@types/react@18.3.18)
+
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)':
+ dependencies:
+ '@testing-library/dom': 10.4.0
+
+ '@tsconfig/node10@1.0.11': {}
+
+ '@tsconfig/node12@1.0.11': {}
+
+ '@tsconfig/node14@1.0.3': {}
+
+ '@tsconfig/node16@1.0.4': {}
+
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.25.7
+ '@babel/types': 7.25.7
+ '@types/babel__generator': 7.6.8
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.20.6
+
+ '@types/babel__generator@7.6.8':
+ dependencies:
+ '@babel/types': 7.25.7
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.25.7
+ '@babel/types': 7.25.7
+
+ '@types/babel__traverse@7.20.6':
+ dependencies:
+ '@babel/types': 7.25.7
+
+ '@types/estree@1.0.6': {}
+
+ '@types/istanbul-lib-coverage@2.0.6': {}
+
+ '@types/istanbul-lib-report@3.0.3':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+
+ '@types/istanbul-reports@3.0.4':
+ dependencies:
+ '@types/istanbul-lib-report': 3.0.3
+
+ '@types/jest@29.5.14':
+ dependencies:
+ expect: 29.7.0
+ pretty-format: 29.7.0
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/node@12.20.55': {}
+
+ '@types/node@22.15.0':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/prop-types@15.7.13': {}
+
+ '@types/react-dom@18.3.0':
+ dependencies:
+ '@types/react': 18.3.11
+
+ '@types/react-dom@18.3.5(@types/react@18.3.18)':
+ dependencies:
+ '@types/react': 18.3.18
+
+ '@types/react@18.3.11':
+ dependencies:
+ '@types/prop-types': 15.7.13
+ csstype: 3.1.3
+
+ '@types/react@18.3.18':
+ dependencies:
+ '@types/prop-types': 15.7.13
+ csstype: 3.1.3
+
+ '@types/stack-utils@2.0.3': {}
+
+ '@types/yargs-parser@21.0.3': {}
+
+ '@types/yargs@17.0.33':
+ dependencies:
+ '@types/yargs-parser': 21.0.3
+
+ '@typescript-eslint/eslint-plugin@8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.6.2))(eslint@9.18.0)(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ '@typescript-eslint/scope-manager': 8.21.0
+ '@typescript-eslint/type-utils': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.21.0
+ eslint: 9.18.0
+ graphemer: 1.4.0
+ ignore: 5.3.2
+ natural-compare: 1.4.0
+ ts-api-utils: 2.0.0(typescript@5.6.2)
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.21.0
+ '@typescript-eslint/types': 8.21.0
+ '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.6.2)
+ '@typescript-eslint/visitor-keys': 8.21.0
+ debug: 4.3.7
+ eslint: 9.18.0
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.21.0':
+ dependencies:
+ '@typescript-eslint/types': 8.21.0
+ '@typescript-eslint/visitor-keys': 8.21.0
+
+ '@typescript-eslint/type-utils@8.21.0(eslint@9.18.0)(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ debug: 4.3.7
+ eslint: 9.18.0
+ ts-api-utils: 2.0.0(typescript@5.6.2)
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.21.0': {}
+
+ '@typescript-eslint/typescript-estree@8.21.0(typescript@5.6.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.21.0
+ '@typescript-eslint/visitor-keys': 8.21.0
+ debug: 4.3.7
+ fast-glob: 3.3.2
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.3
+ ts-api-utils: 2.0.0(typescript@5.6.2)
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.21.0(eslint@9.18.0)(typescript@5.6.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0)
+ '@typescript-eslint/scope-manager': 8.21.0
+ '@typescript-eslint/types': 8.21.0
+ '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.6.2)
+ eslint: 9.18.0
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.21.0':
+ dependencies:
+ '@typescript-eslint/types': 8.21.0
+ eslint-visitor-keys: 4.2.0
+
+ '@vitejs/plugin-react@4.3.2(vite@5.4.8(@types/node@22.15.0)(terser@5.34.1))':
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/plugin-transform-react-jsx-self': 7.25.7(@babel/core@7.25.7)
+ '@babel/plugin-transform-react-jsx-source': 7.25.7(@babel/core@7.25.7)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.14.2
+ vite: 5.4.8(@types/node@22.15.0)(terser@5.34.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitejs/plugin-react@4.3.4(vite@6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.14.2
+ vite: 6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/expect@2.1.2':
+ dependencies:
+ '@vitest/spy': 2.1.2
+ '@vitest/utils': 2.1.2
+ chai: 5.1.1
+ tinyrainbow: 1.2.0
+
+ '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@22.15.0)(terser@5.34.1))':
+ dependencies:
+ '@vitest/spy': 2.1.2
+ estree-walker: 3.0.3
+ magic-string: 0.30.11
+ optionalDependencies:
+ vite: 5.4.8(@types/node@22.15.0)(terser@5.34.1)
+
+ '@vitest/pretty-format@2.1.2':
+ dependencies:
+ tinyrainbow: 1.2.0
+
+ '@vitest/runner@2.1.2':
+ dependencies:
+ '@vitest/utils': 2.1.2
+ pathe: 1.1.2
+
+ '@vitest/snapshot@2.1.2':
+ dependencies:
+ '@vitest/pretty-format': 2.1.2
+ magic-string: 0.30.11
+ pathe: 1.1.2
+
+ '@vitest/spy@2.1.2':
+ dependencies:
+ tinyspy: 3.0.2
+
+ '@vitest/utils@2.1.2':
+ dependencies:
+ '@vitest/pretty-format': 2.1.2
+ loupe: 3.1.2
+ tinyrainbow: 1.2.0
+
+ acorn-jsx@5.3.2(acorn@8.14.0):
+ dependencies:
+ acorn: 8.14.0
+
+ acorn-walk@8.3.4:
+ dependencies:
+ acorn: 8.14.0
+
+ acorn@8.12.1: {}
+
+ acorn@8.14.0: {}
+
+ agent-base@7.1.3: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-colors@4.1.3: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.1.0: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.1: {}
+
+ any-promise@1.3.0: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ arg@4.1.3: {}
+
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ argparse@2.0.1: {}
+
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ aria-query@5.3.2: {}
+
+ array-union@2.1.0: {}
+
+ assertion-error@2.0.1: {}
+
+ asynckit@0.4.0: {}
+
+ balanced-match@1.0.2: {}
+
+ base64-js@1.5.1: {}
+
+ better-path-resolve@1.0.0:
+ dependencies:
+ is-windows: 1.0.2
+
+ binary-extensions@2.3.0: {}
+
+ brace-expansion@1.1.11:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.1:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ brotli-size-cli@1.0.0:
+ dependencies:
+ brotli-size: 4.0.0
+ pretty-bytes: 5.6.0
+ sade: 1.8.1
+
+ brotli-size@4.0.0:
+ dependencies:
+ duplexer: 0.1.1
+
+ browserslist@4.24.0:
+ dependencies:
+ caniuse-lite: 1.0.30001667
+ electron-to-chromium: 1.5.33
+ node-releases: 2.0.18
+ update-browserslist-db: 1.1.1(browserslist@4.24.0)
+
+ buffer-from@1.1.2: {}
+
+ bundle-require@5.0.0(esbuild@0.23.1):
+ dependencies:
+ esbuild: 0.23.1
+ load-tsconfig: 0.2.5
+
+ cac@6.7.14: {}
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001667: {}
+
+ chai@5.1.1:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.2
+ pathval: 2.0.0
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chardet@0.7.0: {}
+
+ check-error@2.1.1: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ ci-info@3.9.0: {}
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@2.20.3: {}
+
+ commander@4.1.1: {}
+
+ concat-map@0.0.1: {}
+
+ consola@3.2.3: {}
+
+ convert-source-map@2.0.0: {}
+
+ create-require@1.1.1: {}
+
+ cross-spawn@5.1.0:
+ dependencies:
+ lru-cache: 4.1.5
+ shebang-command: 1.2.0
+ which: 1.3.1
+
+ cross-spawn@7.0.3:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css.escape@1.5.1: {}
+
+ cssstyle@4.2.1:
+ dependencies:
+ '@asamuzakjp/css-color': 2.8.3
+ rrweb-cssom: 0.8.0
+
+ csstype@3.1.3: {}
+
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.1.0
+
+ dataloader@1.4.0: {}
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.5.0: {}
+
+ deep-eql@5.0.2: {}
+
+ deep-is@0.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ dequal@2.0.3: {}
+
+ detect-indent@6.1.0: {}
+
+ diff-sequences@29.6.3: {}
+
+ diff@4.0.2: {}
+
+ dir-glob@3.0.1:
+ dependencies:
+ path-type: 4.0.0
+
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
+ dotenv@8.6.0: {}
+
+ duplexer@0.1.1: {}
+
+ eastasianwidth@0.2.0: {}
+
+ electron-to-chromium@1.5.33: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ enquirer@2.4.1:
+ dependencies:
+ ansi-colors: 4.1.3
+ strip-ansi: 6.0.1
+
+ entities@4.5.0: {}
+
+ esbuild@0.21.5:
+ 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
+
+ esbuild@0.23.1:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.23.1
+ '@esbuild/android-arm': 0.23.1
+ '@esbuild/android-arm64': 0.23.1
+ '@esbuild/android-x64': 0.23.1
+ '@esbuild/darwin-arm64': 0.23.1
+ '@esbuild/darwin-x64': 0.23.1
+ '@esbuild/freebsd-arm64': 0.23.1
+ '@esbuild/freebsd-x64': 0.23.1
+ '@esbuild/linux-arm': 0.23.1
+ '@esbuild/linux-arm64': 0.23.1
+ '@esbuild/linux-ia32': 0.23.1
+ '@esbuild/linux-loong64': 0.23.1
+ '@esbuild/linux-mips64el': 0.23.1
+ '@esbuild/linux-ppc64': 0.23.1
+ '@esbuild/linux-riscv64': 0.23.1
+ '@esbuild/linux-s390x': 0.23.1
+ '@esbuild/linux-x64': 0.23.1
+ '@esbuild/netbsd-x64': 0.23.1
+ '@esbuild/openbsd-arm64': 0.23.1
+ '@esbuild/openbsd-x64': 0.23.1
+ '@esbuild/sunos-x64': 0.23.1
+ '@esbuild/win32-arm64': 0.23.1
+ '@esbuild/win32-ia32': 0.23.1
+ '@esbuild/win32-x64': 0.23.1
+
+ esbuild@0.24.2:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.24.2
+ '@esbuild/android-arm': 0.24.2
+ '@esbuild/android-arm64': 0.24.2
+ '@esbuild/android-x64': 0.24.2
+ '@esbuild/darwin-arm64': 0.24.2
+ '@esbuild/darwin-x64': 0.24.2
+ '@esbuild/freebsd-arm64': 0.24.2
+ '@esbuild/freebsd-x64': 0.24.2
+ '@esbuild/linux-arm': 0.24.2
+ '@esbuild/linux-arm64': 0.24.2
+ '@esbuild/linux-ia32': 0.24.2
+ '@esbuild/linux-loong64': 0.24.2
+ '@esbuild/linux-mips64el': 0.24.2
+ '@esbuild/linux-ppc64': 0.24.2
+ '@esbuild/linux-riscv64': 0.24.2
+ '@esbuild/linux-s390x': 0.24.2
+ '@esbuild/linux-x64': 0.24.2
+ '@esbuild/netbsd-arm64': 0.24.2
+ '@esbuild/netbsd-x64': 0.24.2
+ '@esbuild/openbsd-arm64': 0.24.2
+ '@esbuild/openbsd-x64': 0.24.2
+ '@esbuild/sunos-x64': 0.24.2
+ '@esbuild/win32-arm64': 0.24.2
+ '@esbuild/win32-ia32': 0.24.2
+ '@esbuild/win32-x64': 0.24.2
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@2.0.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-plugin-react-hooks@5.1.0(eslint@9.18.0):
+ dependencies:
+ eslint: 9.18.0
+
+ eslint-plugin-react-refresh@0.4.18(eslint@9.18.0):
+ dependencies:
+ eslint: 9.18.0
+
+ eslint-scope@8.2.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.0: {}
+
+ eslint@9.18.0:
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0)
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.19.1
+ '@eslint/core': 0.10.0
+ '@eslint/eslintrc': 3.2.0
+ '@eslint/js': 9.18.0
+ '@eslint/plugin-kit': 0.2.5
+ '@humanfs/node': 0.16.6
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.1
+ '@types/estree': 1.0.6
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.3.7
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.2.0
+ eslint-visitor-keys: 4.2.0
+ espree: 10.3.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.3.0:
+ dependencies:
+ acorn: 8.14.0
+ acorn-jsx: 5.3.2(acorn@8.14.0)
+ eslint-visitor-keys: 4.2.0
+
+ esprima@4.0.1: {}
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.6
+
+ esutils@2.0.3: {}
+
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ expect@29.7.0:
+ dependencies:
+ '@jest/expect-utils': 29.7.0
+ jest-get-type: 29.6.3
+ jest-matcher-utils: 29.7.0
+ jest-message-util: 29.7.0
+ jest-util: 29.7.0
+
+ extendable-error@0.1.7: {}
+
+ external-editor@3.1.0:
+ dependencies:
+ chardet: 0.7.0
+ iconv-lite: 0.4.24
+ tmp: 0.0.33
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.2:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.17.1:
+ dependencies:
+ reusify: 1.0.4
+
+ fdir@6.4.0(picomatch@4.0.2):
+ optionalDependencies:
+ picomatch: 4.0.2
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.2
+ keyv: 4.5.4
+
+ flatted@3.3.2: {}
+
+ foreground-child@3.3.0:
+ dependencies:
+ cross-spawn: 7.0.3
+ signal-exit: 4.1.0
+
+ form-data@4.0.1:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+
+ fs-extra@7.0.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fs-extra@8.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ get-stream@6.0.1: {}
+
+ get-tsconfig@4.8.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@10.4.5:
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ globals@11.12.0: {}
+
+ globals@14.0.0: {}
+
+ globals@15.14.0: {}
+
+ globby@11.1.0:
+ dependencies:
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.2
+ ignore: 5.3.2
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.3
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ human-id@1.0.2: {}
+
+ human-signals@2.1.0: {}
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ import-fresh@3.3.0:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ indent-string@4.0.0: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-number@7.0.0: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ is-stream@2.0.1: {}
+
+ is-subdir@1.2.0:
+ dependencies:
+ better-path-resolve: 1.0.0
+
+ is-windows@1.0.2: {}
+
+ isexe@2.0.0: {}
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jest-diff@29.7.0:
+ dependencies:
+ chalk: 4.1.2
+ diff-sequences: 29.6.3
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
+
+ jest-get-type@29.6.3: {}
+
+ jest-matcher-utils@29.7.0:
+ dependencies:
+ chalk: 4.1.2
+ jest-diff: 29.7.0
+ jest-get-type: 29.6.3
+ pretty-format: 29.7.0
+
+ jest-message-util@29.7.0:
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@jest/types': 29.6.3
+ '@types/stack-utils': 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ micromatch: 4.0.8
+ pretty-format: 29.7.0
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-util@29.7.0:
+ dependencies:
+ '@jest/types': 29.6.3
+ '@types/node': 22.15.0
+ chalk: 4.1.2
+ ci-info: 3.9.0
+ graceful-fs: 4.2.11
+ picomatch: 2.3.1
+
+ joycon@3.1.1: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.1:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsdom@26.0.0:
+ dependencies:
+ cssstyle: 4.2.1
+ data-urls: 5.0.0
+ decimal.js: 10.5.0
+ form-data: 4.0.1
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.16
+ parse5: 7.2.1
+ rrweb-cssom: 0.8.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 5.1.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.1.0
+ ws: 8.18.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ jsesc@3.0.2: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@2.2.3: {}
+
+ jsonfile@4.0.0:
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lilconfig@3.1.2: {}
+
+ lines-and-columns@1.2.4: {}
+
+ load-tsconfig@0.2.5: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ lodash.sortby@4.7.0: {}
+
+ lodash.startcase@4.4.0: {}
+
+ lodash@4.17.21: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ loupe@3.1.2: {}
+
+ lru-cache@10.4.3: {}
+
+ lru-cache@4.1.5:
+ dependencies:
+ pseudomap: 1.0.2
+ yallist: 2.1.2
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lz-string@1.5.0: {}
+
+ magic-string@0.30.11:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+
+ make-error@1.3.6: {}
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mimic-fn@2.1.0: {}
+
+ min-indent@1.0.1: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.11
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.1
+
+ minipass@7.1.2: {}
+
+ mri@1.2.0: {}
+
+ ms@2.1.3: {}
+
+ mz@2.7.0:
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ nanoid@3.3.7: {}
+
+ nanoid@3.3.8: {}
+
+ natural-compare@1.4.0: {}
+
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
+ node-releases@2.0.18: {}
+
+ normalize-path@3.0.0: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
+ nwsapi@2.2.16: {}
+
+ object-assign@4.1.1: {}
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ os-tmpdir@1.0.2: {}
+
+ outdent@0.5.0: {}
+
+ p-filter@2.1.0:
+ dependencies:
+ p-map: 2.1.0
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-map@2.1.0: {}
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ package-manager-detector@0.2.1: {}
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse5@7.2.1:
+ dependencies:
+ entities: 4.5.0
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+
+ path-type@4.0.0: {}
+
+ pathe@1.1.2: {}
+
+ pathval@2.0.0: {}
+
+ picocolors@1.1.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.2: {}
+
+ pify@4.0.1: {}
+
+ pirates@4.0.6: {}
+
+ postcss-load-config@6.0.1(postcss@8.5.1)(tsx@4.19.1):
+ dependencies:
+ lilconfig: 3.1.2
+ optionalDependencies:
+ postcss: 8.5.1
+ tsx: 4.19.1
+
+ postcss@8.4.47:
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.1.0
+ source-map-js: 1.2.1
+
+ postcss@8.5.1:
+ dependencies:
+ nanoid: 3.3.8
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier@2.8.8: {}
+
+ prettier@3.3.3: {}
+
+ pretty-bytes@5.6.0: {}
+
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
+ pretty-format@29.7.0:
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.3.1
+
+ pseudomap@1.0.2: {}
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@18.3.1(react@18.3.1):
+ dependencies:
+ loose-envify: 1.4.0
+ react: 18.3.1
+ scheduler: 0.23.2
+
+ react-is@17.0.2: {}
+
+ react-is@18.3.1: {}
+
+ react-refresh@0.14.2: {}
+
+ react@18.3.1:
+ dependencies:
+ loose-envify: 1.4.0
+
+ read-yaml-file@1.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ js-yaml: 3.14.1
+ pify: 4.0.1
+ strip-bom: 3.0.0
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
+ regenerator-runtime@0.14.1: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ reusify@1.0.4: {}
+
+ rollup@4.24.0:
+ dependencies:
+ '@types/estree': 1.0.6
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.24.0
+ '@rollup/rollup-android-arm64': 4.24.0
+ '@rollup/rollup-darwin-arm64': 4.24.0
+ '@rollup/rollup-darwin-x64': 4.24.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.24.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.24.0
+ '@rollup/rollup-linux-arm64-gnu': 4.24.0
+ '@rollup/rollup-linux-arm64-musl': 4.24.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.24.0
+ '@rollup/rollup-linux-s390x-gnu': 4.24.0
+ '@rollup/rollup-linux-x64-gnu': 4.24.0
+ '@rollup/rollup-linux-x64-musl': 4.24.0
+ '@rollup/rollup-win32-arm64-msvc': 4.24.0
+ '@rollup/rollup-win32-ia32-msvc': 4.24.0
+ '@rollup/rollup-win32-x64-msvc': 4.24.0
+ fsevents: 2.3.3
+
+ rrweb-cssom@0.8.0: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ sade@1.8.1:
+ dependencies:
+ mri: 1.2.0
+
+ safer-buffer@2.1.2: {}
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.23.2:
+ dependencies:
+ loose-envify: 1.4.0
+
+ semver@6.3.1: {}
+
+ semver@7.6.3: {}
+
+ shebang-command@1.2.0:
+ dependencies:
+ shebang-regex: 1.0.0
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@1.0.0: {}
+
+ shebang-regex@3.0.0: {}
+
+ siginfo@2.0.0: {}
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ slash@3.0.0: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ source-map@0.8.0-beta.0:
+ dependencies:
+ whatwg-url: 7.1.0
+
+ spawndamnit@2.0.0:
+ dependencies:
+ cross-spawn: 5.1.0
+ signal-exit: 3.0.7
+
+ sprintf-js@1.0.3: {}
+
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
+ stackback@0.0.2: {}
+
+ std-env@3.7.0: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.0:
+ dependencies:
+ ansi-regex: 6.1.0
+
+ strip-bom@3.0.0: {}
+
+ strip-final-newline@2.0.0: {}
+
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ strip-json-comments@3.1.1: {}
+
+ sucrase@3.35.0:
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ commander: 4.1.1
+ glob: 10.4.5
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.6
+ ts-interface-checker: 0.1.13
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ symbol-tree@3.2.4: {}
+
+ term-size@2.2.1: {}
+
+ terser@5.34.1:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.12.1
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ thenify-all@1.6.0:
+ dependencies:
+ thenify: 3.3.1
+
+ thenify@3.3.1:
+ dependencies:
+ any-promise: 1.3.0
+
+ tinybench@2.9.0: {}
+
+ tinyexec@0.3.0: {}
+
+ tinyglobby@0.2.9:
+ dependencies:
+ fdir: 6.4.0(picomatch@4.0.2)
+ picomatch: 4.0.2
+
+ tinypool@1.0.1: {}
+
+ tinyrainbow@1.2.0: {}
+
+ tinyspy@3.0.2: {}
+
+ tldts-core@6.1.75: {}
+
+ tldts@6.1.75:
+ dependencies:
+ tldts-core: 6.1.75
+
+ tmp@0.0.33:
+ dependencies:
+ os-tmpdir: 1.0.2
+
+ to-fast-properties@2.0.0: {}
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ tough-cookie@5.1.0:
+ dependencies:
+ tldts: 6.1.75
+
+ tr46@0.0.3: {}
+
+ tr46@1.0.1:
+ dependencies:
+ punycode: 2.3.1
+
+ tr46@5.0.0:
+ dependencies:
+ punycode: 2.3.1
+
+ tree-kill@1.2.2: {}
+
+ ts-api-utils@2.0.0(typescript@5.6.2):
+ dependencies:
+ typescript: 5.6.2
+
+ ts-interface-checker@0.1.13: {}
+
+ ts-node@10.9.2(@types/node@22.15.0)(typescript@5.8.3):
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.11
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ '@types/node': 22.15.0
+ acorn: 8.14.0
+ acorn-walk: 8.3.4
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.8.3
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+
+ tsup@8.3.0(postcss@8.5.1)(tsx@4.19.1)(typescript@5.6.2):
+ dependencies:
+ bundle-require: 5.0.0(esbuild@0.23.1)
+ cac: 6.7.14
+ chokidar: 3.6.0
+ consola: 3.2.3
+ debug: 4.3.7
+ esbuild: 0.23.1
+ execa: 5.1.1
+ joycon: 3.1.1
+ picocolors: 1.1.0
+ postcss-load-config: 6.0.1(postcss@8.5.1)(tsx@4.19.1)
+ resolve-from: 5.0.0
+ rollup: 4.24.0
+ source-map: 0.8.0-beta.0
+ sucrase: 3.35.0
+ tinyglobby: 0.2.9
+ tree-kill: 1.2.2
+ optionalDependencies:
+ postcss: 8.5.1
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - jiti
+ - supports-color
+ - tsx
+ - yaml
+
+ tsup@8.3.0(postcss@8.5.1)(tsx@4.19.1)(typescript@5.8.3):
+ dependencies:
+ bundle-require: 5.0.0(esbuild@0.23.1)
+ cac: 6.7.14
+ chokidar: 3.6.0
+ consola: 3.2.3
+ debug: 4.3.7
+ esbuild: 0.23.1
+ execa: 5.1.1
+ joycon: 3.1.1
+ picocolors: 1.1.0
+ postcss-load-config: 6.0.1(postcss@8.5.1)(tsx@4.19.1)
+ resolve-from: 5.0.0
+ rollup: 4.24.0
+ source-map: 0.8.0-beta.0
+ sucrase: 3.35.0
+ tinyglobby: 0.2.9
+ tree-kill: 1.2.2
+ optionalDependencies:
+ postcss: 8.5.1
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - jiti
+ - supports-color
+ - tsx
+ - yaml
+
+ tsx@4.19.1:
+ dependencies:
+ esbuild: 0.23.1
+ get-tsconfig: 4.8.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typescript-eslint@8.21.0(eslint@9.18.0)(typescript@5.6.2):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.21.0(@typescript-eslint/parser@8.21.0(eslint@9.18.0)(typescript@5.6.2))(eslint@9.18.0)(typescript@5.6.2)
+ '@typescript-eslint/parser': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ '@typescript-eslint/utils': 8.21.0(eslint@9.18.0)(typescript@5.6.2)
+ eslint: 9.18.0
+ typescript: 5.6.2
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.6.2: {}
+
+ typescript@5.8.3: {}
+
+ undici-types@6.21.0: {}
+
+ undici@6.19.8: {}
+
+ universalify@0.1.2: {}
+
+ update-browserslist-db@1.1.1(browserslist@4.24.0):
+ dependencies:
+ browserslist: 4.24.0
+ escalade: 3.2.0
+ picocolors: 1.1.0
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ v8-compile-cache-lib@3.0.1: {}
+
+ vite-node@2.1.2(@types/node@22.15.0)(terser@5.34.1):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.7
+ pathe: 1.1.2
+ vite: 5.4.8(@types/node@22.15.0)(terser@5.34.1)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
+ vite@5.4.8(@types/node@22.15.0)(terser@5.34.1):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.4.47
+ rollup: 4.24.0
+ optionalDependencies:
+ '@types/node': 22.15.0
+ fsevents: 2.3.3
+ terser: 5.34.1
+
+ vite@6.0.11(@types/node@22.15.0)(terser@5.34.1)(tsx@4.19.1):
+ dependencies:
+ esbuild: 0.24.2
+ postcss: 8.5.1
+ rollup: 4.24.0
+ optionalDependencies:
+ '@types/node': 22.15.0
+ fsevents: 2.3.3
+ terser: 5.34.1
+ tsx: 4.19.1
+
+ vitest@2.1.2(@types/node@22.15.0)(jsdom@26.0.0)(terser@5.34.1):
+ dependencies:
+ '@vitest/expect': 2.1.2
+ '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@22.15.0)(terser@5.34.1))
+ '@vitest/pretty-format': 2.1.2
+ '@vitest/runner': 2.1.2
+ '@vitest/snapshot': 2.1.2
+ '@vitest/spy': 2.1.2
+ '@vitest/utils': 2.1.2
+ chai: 5.1.1
+ debug: 4.3.7
+ magic-string: 0.30.11
+ pathe: 1.1.2
+ std-env: 3.7.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.0
+ tinypool: 1.0.1
+ tinyrainbow: 1.2.0
+ vite: 5.4.8(@types/node@22.15.0)(terser@5.34.1)
+ vite-node: 2.1.2(@types/node@22.15.0)(terser@5.34.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 22.15.0
+ jsdom: 26.0.0
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@3.0.1: {}
+
+ webidl-conversions@4.0.2: {}
+
+ webidl-conversions@7.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.1.0:
+ dependencies:
+ tr46: 5.0.0
+ webidl-conversions: 7.0.0
+
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
+ whatwg-url@7.1.0:
+ dependencies:
+ lodash.sortby: 4.7.0
+ tr46: 1.0.1
+ webidl-conversions: 4.0.2
+
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ ws@8.18.0: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yallist@2.1.2: {}
+
+ yallist@3.1.1: {}
+
+ yn@3.1.1: {}
+
+ yocto-queue@0.1.0: {}
diff --git a/sdks/typescript/pnpm-workspace.yaml b/sdks/typescript/pnpm-workspace.yaml
new file mode 100644
index 00000000000..02495561277
--- /dev/null
+++ b/sdks/typescript/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+ - 'packages/*'
+ - 'examples/**'
diff --git a/sdks/typescript/tsup.config.ts b/sdks/typescript/tsup.config.ts
new file mode 100644
index 00000000000..779fe900a4e
--- /dev/null
+++ b/sdks/typescript/tsup.config.ts
@@ -0,0 +1,56 @@
+import { defineConfig } from 'tsup';
+
+export default defineConfig([
+ {
+ entryPoints: {
+ index: 'src/index.ts',
+ },
+ format: ['esm'],
+ target: 'es2022',
+ legacyOutput: false,
+ dts: {
+ resolve: true,
+ },
+ clean: true,
+ platform: 'browser',
+ noExternal: ['brotli', 'buffer'],
+ treeshake: 'smallest',
+ external: ['undici'],
+ env: {
+ BROWSER: 'false',
+ },
+ },
+ {
+ entryPoints: {
+ index: 'src/index.ts',
+ },
+ format: ['esm'],
+ target: 'es2022',
+ legacyOutput: false,
+ dts: false,
+ outDir: 'dist/browser',
+ clean: true,
+ platform: 'browser',
+ noExternal: ['brotli', 'buffer'],
+ treeshake: 'smallest',
+ external: ['undici'],
+ env: {
+ BROWSER: 'true',
+ },
+ },
+ {
+ entryPoints: {
+ index: 'src/index.ts',
+ },
+ format: ['esm'],
+ target: 'es2022',
+ outDir: 'dist/min',
+ dts: false,
+ sourcemap: true,
+ noExternal: ['brotli', 'buffer', 'events'],
+ treeshake: 'smallest',
+ minify: 'terser',
+ platform: 'browser',
+ external: ['undici'],
+ },
+]);
diff --git a/sdks/typescript/vitest.config.ts b/sdks/typescript/vitest.config.ts
new file mode 100644
index 00000000000..4ac6027d578
--- /dev/null
+++ b/sdks/typescript/vitest.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ environment: 'node',
+ },
+});