diff --git a/.gitignore b/.gitignore index 06fb301538..0ee47de18f 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ coverage/ /packages/realm/binding/build/ /packages/realm/binding/android/build/ /packages/realm/binding/node/build/ +/packages/realm/binding/wasm/build/ /packages/realm/prebuilds/ /packages/realm/binding/android/src/main/java/io/realm/react/Version.java /packages/realm/binding/android/src/main/jniLibs/ diff --git a/contrib/building.md b/contrib/building.md index 96b28615a9..f4337aee21 100644 --- a/contrib/building.md +++ b/contrib/building.md @@ -320,6 +320,28 @@ npm install path/to/realm-js/packages/realm > [!TIP] > To run any of the `"scripts"` commands from one of the `package.json` files directly from the root, use the `"name"` value from the target `package.json` as such: `npm run --workspace `. +### Building for WASM (Browsers) + +Follow the steps to install the Emscripten SDK (emsdk) on https://emscripten.org/docs/getting_started/downloads.html. + +1. Clone the repository and enter the directory + ``` + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + ``` +2. Pull, install and activate the latest version + ``` + git pull + ./emsdk install latest + ./emsdk activate latest + ``` +3. Follow the instructions to setup environment variables for your shell (ex add this to your `~/.zshenv`) + ```bash + # Emscripten SDK + export EMSDK_QUIET=1 + source "$HOME//emsdk_env.sh" + ``` + ### Cleaning up build files If you need to clean up build files and other untracked files (except for `node_modules` directories), run the following command from the root directory: diff --git a/integration-tests/environments/browser/.eslintrc.cjs b/integration-tests/environments/browser/.eslintrc.cjs new file mode 100644 index 0000000000..d6c9537953 --- /dev/null +++ b/integration-tests/environments/browser/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/integration-tests/environments/browser/index.html b/integration-tests/environments/browser/index.html new file mode 100644 index 0000000000..27fa072c85 --- /dev/null +++ b/integration-tests/environments/browser/index.html @@ -0,0 +1,13 @@ + + + + + + + Realm integration tests + + +
+ + + diff --git a/integration-tests/environments/browser/package.json b/integration-tests/environments/browser/package.json new file mode 100644 index 0000000000..23df6967d1 --- /dev/null +++ b/integration-tests/environments/browser/package.json @@ -0,0 +1,39 @@ +{ + "name": "@realm/browser-tests", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "vite": "vite --port 5173 --force", + "runner": "tsx runner.ts", + "test": "wireit", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + }, + "wireit": { + "test": { + "command": "mocha-remote -- concurrently npm:vite npm:runner", + "dependencies": [ + "../../../packages/realm:build:ts", + "../../../packages/realm:prebuild-wasm" + ] + } + }, + "dependencies": { + "mocha-remote-client": "^1.12.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "mocha-remote-cli": "^1.12.2", + "puppeteer": "^22.12.0", + "vite": "^5.2.0" + } +} \ No newline at end of file diff --git a/integration-tests/environments/browser/runner.ts b/integration-tests/environments/browser/runner.ts new file mode 100644 index 0000000000..71cb3befff --- /dev/null +++ b/integration-tests/environments/browser/runner.ts @@ -0,0 +1,15 @@ +import puppeteer from 'puppeteer'; + +const DEV_TOOLS = process.env.DEV_TOOLS === "true" || process.env.DEV_TOOLS === "1"; + +const browser = await puppeteer.launch({ devtools: DEV_TOOLS }); +const page = await browser.newPage(); + +page.on('console', msg => { + const type = msg.type(); + if (type === "log" || type === "warn" || type === "error" || type === "debug") { + console[type]('[browser]', msg.text()); + } +}); + +await page.goto("http://localhost:5173/"); diff --git a/integration-tests/environments/browser/src/App.css b/integration-tests/environments/browser/src/App.css new file mode 100644 index 0000000000..0581673cb1 --- /dev/null +++ b/integration-tests/environments/browser/src/App.css @@ -0,0 +1,19 @@ +#connection-text { + margin: 1rem; + justify-self: flex-start; +} + +#container { + flex-grow: 1; + display: flex; + flex-direction: column; + justify-content: center; +} + +/* +#status-emoji { +} + +#status-text { +} +*/ diff --git a/integration-tests/environments/browser/src/App.tsx b/integration-tests/environments/browser/src/App.tsx new file mode 100644 index 0000000000..3de91c356b --- /dev/null +++ b/integration-tests/environments/browser/src/App.tsx @@ -0,0 +1,187 @@ +import './App.css' + +import React, { useEffect, useState, createContext, useContext } from "react"; +import { Client as MochaRemoteClient, CustomContext } from "mocha-remote-client"; + +async function loadTests() { + Object.assign(globalThis, { + environment: { ...context, browser: true }, + }); + try { + await import("@realm/integration-tests/browser"); + } catch (err) { + console.error(err); + throw err; + } +} + +export type { CustomContext }; + +export type Status = + | { + kind: "waiting"; + } + | { + kind: "running"; + failures: number; + totalTests: number; + currentTest: string; + currentTestIndex: number; + } + | { + kind: "ended"; + failures: number; + totalTests: number; + } + +export type MochaRemoteProviderProps = React.PropsWithChildren<{ + tests: (context: CustomContext) => Promise | void; +}>; + +export type MochaRemoteContextValue = { + connected: boolean; + status: Status; +}; + +export const MochaRemoteContext = createContext({ + connected: false, + status: { kind: "waiting" }, +}); + +const client = new MochaRemoteClient({ + title: `Browser on ${window.navigator.userAgent}`, + async tests() { + // Adding an async hook before each test to allow the UI to update + beforeEach("async-pause", () => { + return new Promise((resolve) => setTimeout(resolve)); + }); + // Require in the tests + await loadTests(); + }, +}) + +function MochaRemoteProvider({ children, tests }: MochaRemoteProviderProps) { + const [connected, setConnected] = useState(false); + const [status, setStatus] = useState({ kind: "waiting" }); + + useEffect(() => { + client.on("connection", () => { + setConnected(true); + }) + .on("disconnection", () => { + setConnected(false); + }) + .on("running", (runner) => { + // TODO: Fix the types for "runner" + if (runner.total === 0) { + setStatus({ + kind: "ended", + totalTests: 0, + failures: 0, + }); + } + + let currentTestIndex = 0; + + runner.on("test", (test) => { + setStatus({ + kind: "running", + currentTest: test.fullTitle(), + // Compute the current test index - incrementing it if we're running + currentTestIndex: currentTestIndex++, + totalTests: runner.total, + failures: runner.failures, + }); + }).on("end", () => { + setStatus({ + kind: "ended", + totalTests: runner.total, + failures: runner.failures, + }); + }); + }); + // Remove listeners as the component unmounts + return () => { + client.removeAllListeners("connection"); + client.removeAllListeners("disconnection"); + client.removeAllListeners("running"); + }; + }, [setStatus, setConnected, tests]); + + return ( + + {children} + + ); +} + +function useMochaRemoteContext() { + return useContext(MochaRemoteContext); +} + +function getStatusEmoji(status: Status) { + if (status.kind === "running") { + return "🏃"; + } else if (status.kind === "waiting") { + return "⏳"; + } else if (status.kind === "ended" && status.totalTests === 0) { + return "🤷"; + } else if (status.kind === "ended" && status.failures > 0) { + return "❌"; + } else if (status.kind === "ended") { + return "✅"; + } else { + return null; + } +} + +function StatusEmoji(props: React.DetailedHTMLProps, HTMLSpanElement>) { + const { status } = useMochaRemoteContext(); + return {getStatusEmoji(status)} +} + +function getStatusMessage(status: Status) { + if (status.kind === "running") { + return `[${status.currentTestIndex + 1} of ${status.totalTests}] ${status.currentTest}`; + } else if (status.kind === "waiting") { + return "Waiting for server to start tests"; + } else if (status.kind === "ended" && status.failures > 0) { + return `${status.failures} tests failed!`; + } else if (status.kind === "ended") { + return "All tests succeeded!"; + } else { + return null; + } +} + +function StatusText(props: React.DetailedHTMLProps, HTMLSpanElement>) { + const { status } = useMochaRemoteContext(); + return {getStatusMessage(status)} +} + +function getConnectionMessage(connected: boolean) { + if (connected) { + return "🛜 Connected to the Mocha Remote Server"; + } else { + return "🔌 Disconnected from the Mocha Remote Server"; + } +} + +function ConnectionText(props: React.DetailedHTMLProps, HTMLSpanElement>) { + const { connected } = useMochaRemoteContext(); + return {getConnectionMessage(connected)} +} + +function App() { + return ( + + +
+ + +
+
+ ) +} + +export default App diff --git a/integration-tests/environments/browser/src/index.css b/integration-tests/environments/browser/src/index.css new file mode 100644 index 0000000000..201fa57f10 --- /dev/null +++ b/integration-tests/environments/browser/src/index.css @@ -0,0 +1,77 @@ +: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; + flex-direction: column; + min-width: 320px; + min-height: 100vh; + text-align: center; +} + +h1 { + font-size: 1.4em; + 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; + } +} + +#root { + display: flex; + flex-grow: 1; + flex-direction: column; + justify-content: space-between; +} + diff --git a/integration-tests/environments/browser/src/main.tsx b/integration-tests/environments/browser/src/main.tsx new file mode 100644 index 0000000000..2410d5b15c --- /dev/null +++ b/integration-tests/environments/browser/src/main.tsx @@ -0,0 +1,11 @@ +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/integration-tests/environments/browser/src/vite-env.d.ts b/integration-tests/environments/browser/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/integration-tests/environments/browser/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/integration-tests/environments/browser/tsconfig.json b/integration-tests/environments/browser/tsconfig.json new file mode 100644 index 0000000000..3845b7c08b --- /dev/null +++ b/integration-tests/environments/browser/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "types": ["mocha", "mocha-remote-client"], + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/integration-tests/environments/browser/tsconfig.node.json b/integration-tests/environments/browser/tsconfig.node.json new file mode 100644 index 0000000000..39d41f4a9d --- /dev/null +++ b/integration-tests/environments/browser/tsconfig.node.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts", "runner.ts"] +} diff --git a/integration-tests/environments/browser/vite.config.ts b/integration-tests/environments/browser/vite.config.ts new file mode 100644 index 0000000000..0ca5e1e3da --- /dev/null +++ b/integration-tests/environments/browser/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + optimizeDeps: { + include: ["realm"] + }, + build: { + commonjsOptions: { + include: ["realm"], + } + } +}) diff --git a/integration-tests/environments/react-native-test-app/package.json b/integration-tests/environments/react-native-test-app/package.json index 5e0131575a..2c88d81661 100644 --- a/integration-tests/environments/react-native-test-app/package.json +++ b/integration-tests/environments/react-native-test-app/package.json @@ -154,7 +154,6 @@ "@react-native/typescript-config": "0.74.83", "@rnx-kit/metro-config": "^1.3.15", "@types/react": "^18.2.6", - "concurrently": "^8.2.2", "mocha-remote-cli": "^1.12.3", "pod-install": "^0.2.2", "react-native-test-app": "^3.8.7" diff --git a/integration-tests/tests/package.json b/integration-tests/tests/package.json index 28826358f6..df89df4f3e 100644 --- a/integration-tests/tests/package.json +++ b/integration-tests/tests/package.json @@ -5,7 +5,8 @@ "main": "src/index.ts", "exports": { ".": "./src/index.ts", - "./node": "./src/node/index.ts" + "./node": "./src/node/index.ts", + "./browser": "./src/browser/index.ts" }, "private": true, "scripts": { diff --git a/integration-tests/tests/src/browser/index.ts b/integration-tests/tests/src/browser/index.ts new file mode 100644 index 0000000000..046dc18e94 --- /dev/null +++ b/integration-tests/tests/src/browser/index.ts @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +if (typeof navigator.userAgent !== "string") { + throw new Error("This file is only supposed to be imported from a browser environment!"); +} + +import { ready } from "realm"; +await ready; + +// Import all the regular tests first +import "./setup-globals"; + +import "../setup-globals"; +// import "../utils/chai-plugin.test"; +import "../utils/listener-stub.test"; +// import "../utils/promise-handle.test"; +// import "../utils/sequence.test"; +// import "../mocha-internals.test"; diff --git a/integration-tests/tests/src/browser/setup-globals.ts b/integration-tests/tests/src/browser/setup-globals.ts new file mode 100644 index 0000000000..a1275d9a53 --- /dev/null +++ b/integration-tests/tests/src/browser/setup-globals.ts @@ -0,0 +1,35 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2020 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +/* eslint-disable no-restricted-globals */ + +Object.assign(globalThis, { + fs: { + exists(path: string) { + throw new Error("Not implemented"); + }, + }, + path: { + dirname(path: string) { + throw new Error("Not implemented"); + }, + resolve(...paths: string[]) { + throw new Error("Not implemented"); + }, + }, +}); diff --git a/integration-tests/tests/src/typings.d.ts b/integration-tests/tests/src/typings.d.ts index 5ff4744775..6022be7898 100644 --- a/integration-tests/tests/src/typings.d.ts +++ b/integration-tests/tests/src/typings.d.ts @@ -107,6 +107,10 @@ type KnownEnvironment = { * React native specific variable injected by the runner, to signal if the tests are ran by the legacy chrome debugger (i.e. in a browser). * @deprecated Since we no longer support the legacy chrome debugger. */ chromeDebugging?: true; + /** + * Browser specific variable injected by the runner, to signal if we're running inside a Browser with WebAssembly. + */ + browser?: true; }; type Environment = KnownEnvironment & Record; diff --git a/integration-tests/tests/tsconfig.browser.json b/integration-tests/tests/tsconfig.browser.json new file mode 100644 index 0000000000..d7ac7ee56f --- /dev/null +++ b/integration-tests/tests/tsconfig.browser.json @@ -0,0 +1,30 @@ +{ + "extends": "@tsconfig/recommended", + "compilerOptions": { + "composite": true, + "module": "es2022", + "moduleResolution": "Bundler", + "outDir": "dist", + "strictFunctionTypes": false, + "useDefineForClassFields": false, + "customConditions": [ + "browser" + ], + "types": [ + "mocha", + "chai", + "chai-as-promised" + ], + "lib": [ + "dom", + "es2022" + ] + }, + "include": [ + "src/typings.d.ts", + "src/browser/**/*.ts" + ], + "references": [ + { "path": "./tsconfig.common.json" } + ] + } \ No newline at end of file diff --git a/integration-tests/tests/tsconfig.common.json b/integration-tests/tests/tsconfig.common.json index 00cf6ef486..a3f7628079 100644 --- a/integration-tests/tests/tsconfig.common.json +++ b/integration-tests/tests/tsconfig.common.json @@ -28,6 +28,7 @@ "src/**/*.ts" ], "exclude": [ - "src/node/" + "src/node/", + "src/browser/" ] } diff --git a/integration-tests/tests/tsconfig.json b/integration-tests/tests/tsconfig.json index 1ea283bb59..d9c6e41e8e 100644 --- a/integration-tests/tests/tsconfig.json +++ b/integration-tests/tests/tsconfig.json @@ -2,6 +2,7 @@ "files": [], "references": [ { "path": "./tsconfig.common.json" }, - { "path": "./tsconfig.node.json" } + { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.browser.json" } ] } diff --git a/package-lock.json b/package-lock.json index 1f6304920a..d62a5a704d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "packages/mocha-reporter", "packages/realm-web-integration-tests", "integration-tests/tests", + "integration-tests/environments/browser", "integration-tests/environments/node", "integration-tests/environments/electron", "integration-tests/environments/react-native-test-app", @@ -38,6 +39,7 @@ "@tsconfig/node-lts": "^20.1.1", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", + "concurrently": "^8.2.2", "eslint": "^8.43.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-flowtype": "^8.0.3", @@ -112,6 +114,292 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "integration-tests/environments/browser": { + "name": "@realm/browser-tests", + "version": "0.0.0", + "dependencies": { + "mocha-remote-client": "^1.12.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.66", + "@types/react-dom": "^18.2.22", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.57.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.6", + "mocha-remote-cli": "^1.12.2", + "puppeteer": "^22.12.0", + "vite": "^5.2.0" + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", + "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/type-utils": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/parser": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", + "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/scope-manager": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", + "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/type-utils": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", + "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.13.1", + "@typescript-eslint/utils": "7.13.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/types": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", + "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", + "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/visitor-keys": "7.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/utils": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", + "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.13.1", + "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/typescript-estree": "7.13.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "integration-tests/environments/browser/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", + "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.13.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "integration-tests/environments/browser/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "integration-tests/environments/browser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "integration-tests/environments/browser/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "integration-tests/environments/browser/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "integration-tests/environments/browser/node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "integration-tests/environments/browser/node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "integration-tests/environments/browser/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "integration-tests/environments/electron": { "name": "@realm/electron-tests", "version": "0.1.0", @@ -229,7 +517,6 @@ "@react-native/typescript-config": "0.74.83", "@rnx-kit/metro-config": "^1.3.15", "@types/react": "^18.2.6", - "concurrently": "^8.2.2", "mocha-remote-cli": "^1.12.3", "pod-install": "^0.2.2", "react-native-test-app": "^3.8.7" @@ -1001,9 +1288,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.6.tgz", - "integrity": "sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", "engines": { "node": ">=6.9.0" } @@ -2360,11 +2647,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.7.tgz", + "integrity": "sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2374,11 +2661,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.7.tgz", + "integrity": "sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2851,7 +3138,6 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", - "dev": true, "dependencies": { "@chevrotain/gast": "10.5.0", "@chevrotain/types": "10.5.0", @@ -2862,7 +3148,6 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", - "dev": true, "dependencies": { "@chevrotain/types": "10.5.0", "lodash": "4.17.21" @@ -2871,20 +3156,17 @@ "node_modules/@chevrotain/types": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", - "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==", - "dev": true + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==" }, "node_modules/@chevrotain/utils": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", - "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", - "dev": true + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2896,7 +3178,6 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -3637,9 +3918,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -4592,9 +4873,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.1.0.tgz", - "integrity": "sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.2.3.tgz", + "integrity": "sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==", "dependencies": { "debug": "4.3.4", "extract-zip": "2.0.1", @@ -5188,6 +5469,10 @@ "resolved": "packages/realm/bindgen/vendor/realm-core", "link": true }, + "node_modules/@realm/browser-tests": { + "resolved": "integration-tests/environments/browser", + "link": true + }, "node_modules/@realm/common": { "resolved": "packages/realm-common", "link": true @@ -5456,9 +5741,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", - "integrity": "sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", "cpu": [ "arm" ], @@ -5468,9 +5753,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz", - "integrity": "sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", "cpu": [ "arm64" ], @@ -5480,9 +5765,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz", - "integrity": "sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", "cpu": [ "arm64" ], @@ -5492,9 +5777,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz", - "integrity": "sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", "cpu": [ "x64" ], @@ -5504,9 +5789,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz", - "integrity": "sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", "cpu": [ "arm" ], @@ -5516,9 +5813,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz", - "integrity": "sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", "cpu": [ "arm64" ], @@ -5528,9 +5825,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz", - "integrity": "sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", "cpu": [ "arm64" ], @@ -5539,10 +5836,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz", - "integrity": "sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", "cpu": [ "riscv64" ], @@ -5551,10 +5860,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz", - "integrity": "sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", "cpu": [ "x64" ], @@ -5564,9 +5885,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz", - "integrity": "sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", "cpu": [ "x64" ], @@ -5576,9 +5897,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz", - "integrity": "sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", "cpu": [ "arm64" ], @@ -5588,9 +5909,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz", - "integrity": "sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", "cpu": [ "ia32" ], @@ -5600,9 +5921,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz", - "integrity": "sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "cpu": [ "x64" ], @@ -5915,26 +6236,22 @@ "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" }, "node_modules/@tsconfig/node18": { "version": "18.2.2", @@ -6302,16 +6619,24 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/react": { - "version": "18.2.47", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz", - "integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==", + "version": "18.3.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", + "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", "devOptional": true, "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -6330,12 +6655,6 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==" }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "devOptional": true - }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", @@ -6751,6 +7070,25 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.1.tgz", + "integrity": "sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.24.5", + "@babel/plugin-transform-react-jsx-self": "^7.24.5", + "@babel/plugin-transform-react-jsx-source": "^7.24.1", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -7571,8 +7909,7 @@ "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, "node_modules/argparse": { "version": "2.0.1", @@ -8088,17 +8425,58 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + "node_modules/bare-events": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", + "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "optional": true }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { + "node_modules/bare-fs": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", + "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", + "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", + "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "optional": true, + "dependencies": { + "streamx": "^2.18.0" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { "type": "github", "url": "https://github.com/sponsors/feross" }, @@ -9000,7 +9378,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -9037,7 +9414,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -9120,7 +9496,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -9169,7 +9544,6 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", - "dev": true, "dependencies": { "@chevrotain/cst-dts-gen": "10.5.0", "@chevrotain/gast": "10.5.0", @@ -9241,12 +9615,13 @@ } }, "node_modules/chromium-bidi": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.12.tgz", - "integrity": "sha512-sZMgEBWKbupD0Q7lyFu8AWkrE+rs5ycE12jFkGwIgD/VS8lDPtelPlXM7LYaq4zrkZ/O2L3f4afHUHL0ICdKog==", + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.24.tgz", + "integrity": "sha512-5xQNN2SVBdZv4TxeMLaI+PelrnZsHDhn8h2JtyriLr+0qHcZS8BMuo93qN6J1VmtmrgYP+rmcLHcbpnA8QJh+w==", "dependencies": { "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0" + "urlpattern-polyfill": "10.0.0", + "zod": "3.23.8" }, "peerDependencies": { "devtools-protocol": "*" @@ -9895,7 +10270,6 @@ "version": "8.2.2", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", - "dev": true, "dependencies": { "chalk": "^4.1.2", "date-fns": "^2.30.0", @@ -10069,7 +10443,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -10274,8 +10647,7 @@ "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, "node_modules/cross-env": { "version": "7.0.3", @@ -10295,14 +10667,6 @@ "yarn": ">=1" } }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10414,7 +10778,6 @@ "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -10899,9 +11262,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1249869", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz", - "integrity": "sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg==" + "version": "0.0.1299070", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz", + "integrity": "sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==" }, "node_modules/diff": { "version": "5.0.0", @@ -11139,7 +11502,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -11740,15 +12102,15 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -12031,6 +12393,15 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz", "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==" }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz", + "integrity": "sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -13892,7 +14263,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -14330,9 +14700,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "engines": { "node": ">= 4" } @@ -16853,7 +17223,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -16968,8 +17337,7 @@ "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, "node_modules/make-fetch-happen": { "version": "13.0.1", @@ -18652,7 +19020,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -20180,9 +20547,9 @@ } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -20251,7 +20618,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -20319,7 +20685,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -20343,7 +20708,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -20352,8 +20716,7 @@ "node_modules/path-equal": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.2.5.tgz", - "integrity": "sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==", - "dev": true + "integrity": "sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==" }, "node_modules/path-exists": { "version": "3.0.0", @@ -21500,9 +21863,9 @@ } }, "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dependencies": { "debug": "^4.3.4" }, @@ -21594,14 +21957,15 @@ } }, "node_modules/puppeteer": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.4.1.tgz", - "integrity": "sha512-Mag1wRLanzwS4yEUyrDRBUgsKlH3dpL6oAfVwNHG09oxd0+ySsatMvYj7HwjynWy/S+Hg+XHLgjyC/F6CsL/lg==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.12.0.tgz", + "integrity": "sha512-kyUYI12SyJIjf9UGTnHfhNMYv4oVK321Jb9QZDBiGVNx5453SplvbdKI7UrF+S//3RtCneuUFCyHxnvQXQjpxg==", "hasInstallScript": true, "dependencies": { - "@puppeteer/browsers": "2.1.0", + "@puppeteer/browsers": "2.2.3", "cosmiconfig": "9.0.0", - "puppeteer-core": "22.4.1" + "devtools-protocol": "0.0.1299070", + "puppeteer-core": "22.12.0" }, "bin": { "puppeteer": "lib/esm/puppeteer/node/cli.js" @@ -21611,25 +21975,45 @@ } }, "node_modules/puppeteer-core": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.4.1.tgz", - "integrity": "sha512-l9nf8NcirYOHdID12CIMWyy7dqcJCVtgVS+YAiJuUJHg8+9yjgPiG2PcNhojIEEpCkvw3FxvnyITVfKVmkWpjA==", + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.12.0.tgz", + "integrity": "sha512-9gY+JwBW/Fp3/x9+cOGK7ZcwqjvtvY2xjqRqsAA0B3ZFMzBauVTSZ26iWTmvOQX2sk78TN/rd5rnetxVxmK5CQ==", "dependencies": { - "@puppeteer/browsers": "2.1.0", - "chromium-bidi": "0.5.12", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1249869", - "ws": "8.16.0" + "@puppeteer/browsers": "2.2.3", + "chromium-bidi": "0.5.24", + "debug": "4.3.5", + "devtools-protocol": "0.0.1299070", + "ws": "8.17.1" }, "engines": { "node": ">=18" } }, + "node_modules/puppeteer-core/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -22660,9 +23044,9 @@ } }, "node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "engines": { "node": ">=0.10.0" } @@ -22875,8 +23259,7 @@ "node_modules/regexp-to-ast": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", - "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -23281,9 +23664,9 @@ } }, "node_modules/rollup": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.6.tgz", - "integrity": "sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dependencies": { "@types/estree": "1.0.5" }, @@ -23295,19 +23678,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.9.6", - "@rollup/rollup-android-arm64": "4.9.6", - "@rollup/rollup-darwin-arm64": "4.9.6", - "@rollup/rollup-darwin-x64": "4.9.6", - "@rollup/rollup-linux-arm-gnueabihf": "4.9.6", - "@rollup/rollup-linux-arm64-gnu": "4.9.6", - "@rollup/rollup-linux-arm64-musl": "4.9.6", - "@rollup/rollup-linux-riscv64-gnu": "4.9.6", - "@rollup/rollup-linux-x64-gnu": "4.9.6", - "@rollup/rollup-linux-x64-musl": "4.9.6", - "@rollup/rollup-win32-arm64-msvc": "4.9.6", - "@rollup/rollup-win32-ia32-msvc": "4.9.6", - "@rollup/rollup-win32-x64-msvc": "4.9.6", + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, @@ -23462,7 +23848,6 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -23528,7 +23913,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", - "dev": true, "engines": { "node": ">=10" } @@ -23704,7 +24088,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -24155,7 +24538,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -24280,8 +24662,7 @@ "node_modules/spawn-command": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==" }, "node_modules/spawn-wrap": { "version": "2.0.0", @@ -24512,12 +24893,16 @@ } }, "node_modules/streamx": { - "version": "2.15.6", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", - "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", + "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { @@ -25103,6 +25488,14 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", + "integrity": "sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-encoding": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", @@ -25231,7 +25624,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, "bin": { "tree-kill": "cli.js" } @@ -25246,9 +25638,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "engines": { "node": ">=16" }, @@ -25435,7 +25827,6 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -25478,7 +25869,6 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -25487,7 +25877,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, "engines": { "node": ">=0.3.1" } @@ -25791,7 +26180,6 @@ "version": "0.55.0", "resolved": "https://registry.npmjs.org/typescript-json-schema/-/typescript-json-schema-0.55.0.tgz", "integrity": "sha512-BXaivYecUdiXWWNiUqXgY6A9cMWerwmhtO+lQE7tDZGs7Mf38sORDeQZugfYOZOHPZ9ulsD+w0LWjFDOQoXcwg==", - "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/node": "^16.9.2", @@ -25809,14 +26197,12 @@ "node_modules/typescript-json-schema/node_modules/@types/node": { "version": "16.18.70", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.70.tgz", - "integrity": "sha512-8eIk20G5VVVQNZNouHjLA2b8utE2NvGybLjMaF4lyhA9uhGwnmXF8o+icdXKGSQSNANJewXva/sFUoZLwAaYAg==", - "dev": true + "integrity": "sha512-8eIk20G5VVVQNZNouHjLA2b8utE2NvGybLjMaF4lyhA9uhGwnmXF8o+icdXKGSQSNANJewXva/sFUoZLwAaYAg==" }, "node_modules/typescript-json-schema/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -25836,7 +26222,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -25848,7 +26233,6 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -26072,7 +26456,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -26081,7 +26464,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -26170,8 +26552,7 @@ "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -26217,6 +26598,467 @@ "dev": true, "optional": true }, + "node_modules/vite": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz", + "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/vlq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", @@ -27202,7 +28044,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, "engines": { "node": ">=6" } @@ -27218,6 +28059,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/babel-plugin": { "name": "@realm/babel-plugin", "version": "0.2.0", @@ -28033,7 +28882,6 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/@commander-js/extra-typings/-/extra-typings-11.1.0.tgz", "integrity": "sha512-GuvZ38d23H+7Tz2C9DhzCepivsOsky03s5NI+KCy7ke1FNUvsJ2oO47scQ9YaGGhgjgNW5OYYNSADmbjcSoIhw==", - "dev": true, "peerDependencies": { "commander": "11.1.x" } @@ -28042,7 +28890,6 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, "engines": { "node": ">=16" } @@ -28050,7 +28897,6 @@ "packages/realm/bindgen/vendor/realm-core": { "name": "@realm/bindgen", "version": "0.1.0", - "dev": true, "dependencies": { "@commander-js/extra-typings": "^11.1.0", "@types/node": "^18.15.10", diff --git a/package.json b/package.json index f8eb138298..b488eb0aac 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "packages/mocha-reporter", "packages/realm-web-integration-tests", "integration-tests/tests", + "integration-tests/environments/browser", "integration-tests/environments/node", "integration-tests/environments/electron", "integration-tests/environments/react-native-test-app", @@ -78,6 +79,7 @@ "@tsconfig/node-lts": "^20.1.1", "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", + "concurrently": "^8.2.2", "eslint": "^8.43.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-flowtype": "^8.0.3", diff --git a/packages/realm/bindgen/src/realm_js_wasm_helpers.h b/packages/realm/bindgen/src/realm_js_wasm_helpers.h new file mode 100644 index 0000000000..da52629406 --- /dev/null +++ b/packages/realm/bindgen/src/realm_js_wasm_helpers.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +namespace realm::js::wasm { +namespace { + +REALM_NOINLINE inline emscripten::val toEmscriptenErrorCode(const std::error_code& e) noexcept +{ + REALM_ASSERT_RELEASE(e); + auto jsErr = emscripten::val::global("Error")(emscripten::val(e.message())); + jsErr.set("code", e.value()); + jsErr.set("category", e.category().name()); + + return jsErr; +} +REALM_NOINLINE inline emscripten::val toEmscriptenException(const std::exception& e) noexcept +{ + return emscripten::val::global("Error")(std::string(e.what())); +} +REALM_NOINLINE inline emscripten::val toEmscriptenException(const std::exception_ptr& e) noexcept +{ + try { + std::rethrow_exception(e); + } + catch (const std::exception& e) { + return toEmscriptenException(e); + } + catch (...) { + return emscripten::val::global("Error")(std::string("Unknown error")); + } +} +// Allocate a new C++ buffer big enough to fit the JS buffer +// Create a JS memory view around the C++ buffer +// Call TypedArray.prototype.set to efficiently copy the JS buffer into the C++ buffer via the view +REALM_NOINLINE inline std::string toBinaryData(const emscripten::val array_buffer) noexcept +{ + REALM_ASSERT(array_buffer.instanceof (emscripten::val::global("ArrayBuffer"))); + std::string buf; + buf.resize(array_buffer["byteLength"].as()); + + emscripten::val mv(emscripten::typed_memory_view(buf.length(), buf.data())); + mv.call("set", emscripten::val::global("Uint8Array").new_(array_buffer)); + + return buf; +} +REALM_NOINLINE inline OwnedBinaryData toOwnedBinaryData(const emscripten::val array_buffer) noexcept +{ + REALM_ASSERT(array_buffer.instanceof (emscripten::val::global("ArrayBuffer"))); + auto length = array_buffer["byteLength"].as(); + + std::unique_ptr buf(new char[length]); + + emscripten::val mv(emscripten::typed_memory_view(length, buf.get())); + mv.call("set", emscripten::val::global("Uint8Array").new_(array_buffer)); + + return OwnedBinaryData(std::move(buf), length); +} +} // namespace +} // namespace realm::js::wasm diff --git a/packages/realm/bindgen/src/templates/wasm-wrapper.ts b/packages/realm/bindgen/src/templates/wasm-wrapper.ts new file mode 100644 index 0000000000..a936b891cc --- /dev/null +++ b/packages/realm/bindgen/src/templates/wasm-wrapper.ts @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import { TemplateContext } from "@realm/bindgen/context"; + +import { eslintFormatter } from "../formatters"; +import { generate as generateBase, generateNativeBigIntSupport } from "./base-wrapper"; + +export function generate(context: TemplateContext): void { + const out = context.file("native.wasm.mjs", eslintFormatter); + + out("// This file is generated: Update the spec instead of editing this file directly"); + + out(` + /*global window, FinalizationRegistry*/ + import Module from "../../prebuilds/wasm/realm-js-wasm.mjs"; + const nativeModule = await Module(); // loading WASM + nativeModule.browserInit(); + export const WeakRef = window.WeakRef; + `); + + generateNativeBigIntSupport(out); + + out(` + export const Int64 = NativeBigIntSupport; // Browsers always supports BigInt + `); + + generateBase({ + context, + out, + classExtras(cls) { + return `const _${cls.jsName}_registery = (nativeModule.${cls.jsName}_deleter) ? new FinalizationRegistry(nativeModule.${cls.jsName}_deleter) : undefined;`; + }, + emitConstructor(symb, cls) { + return `constructor(ptr) { this[${symb}] = ptr; + if (_${cls.jsName}_registery) _${cls.jsName}_registery.register(this, ptr); + };`; + }, + }); + + context.file("native.wasm.d.mts", eslintFormatter)("export * from './native'"); + context.file("native.wasm.d.cts", eslintFormatter)("import * as binding from './native'; export = binding;"); +} diff --git a/packages/realm/bindgen/src/templates/wasm.ts b/packages/realm/bindgen/src/templates/wasm.ts new file mode 100644 index 0000000000..8b872be087 --- /dev/null +++ b/packages/realm/bindgen/src/templates/wasm.ts @@ -0,0 +1,942 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// +import { strict as assert } from "assert"; + +import { TemplateContext } from "@realm/bindgen/context"; +import { CppVar, CppFunc, CppFuncProps, CppMethod, CppClass, CppDecls } from "@realm/bindgen/cpp"; +import { + BoundSpec, + Class, + InstanceMethod, + StaticMethod, + Property, + Type, + Primitive, + Pointer, + Template, +} from "@realm/bindgen/bound-model"; + +import { doJsPasses } from "../js-passes"; +import { clangFormat } from "@realm/bindgen/formatter"; + +const emscripten_call_args = new CppVar("const emscripten::val", "args"); + +function tryWrap(body: string) { + return `try { + ${body} + } catch (const std::exception& ex) { + toEmscriptenException(ex).throw_(); + } + `; +} + +class CppEmscriptenFunc extends CppFunc { + constructor( + private addon: BrowserAddon, + name: string, + numberOfArgs: number, + props?: CppFuncProps, + ) { + const vars = new Array(numberOfArgs); + for (let i = 0; i < numberOfArgs; i++) { + vars[i] = new CppVar("const emscripten::val", `arg${i}`); + } + super(name, "emscripten::val", vars, props); + } + + definition() { + return super.definition(` + const auto callBlock = ${this.addon.get()}->startCall(); + ${tryWrap(this.body)} + `); + } +} + +function pushRet(arr: T[], elem: U) { + arr.push(elem); + return elem; +} + +class BrowserAddon extends CppClass { + exports: Record = {}; + classes: string[] = []; + injectables = ["Float", "UUID", "ObjectId", "Decimal128", "EJSON_parse", "EJSON_stringify"]; + + constructor() { + super("RealmAddon"); + this.members.push(new CppVar("std::unique_ptr", "self", { static: true })); + this.members.push(new CppVar("std::deque", "m_string_bufs")); + this.addMethod( + new CppMethod("wrapString", "const std::string&", [new CppVar("std::string", "str")], { + attributes: "inline", + body: `return m_string_bufs.emplace_back(std::move(str));`, + }), + ); + this.addMethod( + new CppMethod("startCall", "auto", [], { + attributes: "inline", + body: `return ContainerResizer(m_string_bufs);`, + }), + ); + } + + generateMembers() { + this.injectables.forEach((t) => this.members.push(new CppVar("emscripten::val", BrowserAddon.memberNameFor(t)))); + this.classes.forEach((t) => + this.members.push(new CppVar("emscripten::val", BrowserAddon.memberNameForExtractor(t))), + ); + this.addMethod( + new CppMethod("injectInjectables", "void", [emscripten_call_args], { + body: ` + ${this.injectables.map((t) => `${BrowserAddon.memberNameFor(t)} = args["${t}"];`).join("\n")} + ${this.classes + .map( + (cls) => + `${BrowserAddon.memberNameForExtractor(cls)} = + ${BrowserAddon.memberNameFor(cls)}["_extract"];`, + ) + .join("\n")} + `, + }), + ); + } + + addFunc(name: string, numberOfArgs: number, props?: CppFuncProps) { + return new CppEmscriptenFunc(this, name, numberOfArgs, props); + } + + addClass(cls: Class) { + this.injectables.push(cls.jsName); + this.classes.push(cls.jsName); + } + + static memberNameForExtractor(cls: string | { jsName: string }) { + if (typeof cls != "string") cls = cls.jsName; + return `m_cls_${cls}_extractor`; + } + static memberNameFor(cls: string | { jsName: string }) { + if (typeof cls != "string") cls = cls.jsName; + return `m_cls_${cls}_ctor`; + } + + accessCtor(cls: string | { jsName: string }) { + return `${this.get()}->${BrowserAddon.memberNameFor(cls)}`; + } + + accessExtractor(cls: string | { jsName: string }) { + return `${this.get()}->${BrowserAddon.memberNameForExtractor(cls)}`; + } + + get() { + return `${this.name}::self`; + } +} + +function convertPrimToEmscripten(addon: BrowserAddon, type: string, expr: string): string { + switch (type) { + case "void": + return `((void)(${expr}), emscripten::val::undefined())`; + + case "bool": + return `emscripten::val(bool(${expr}))`; + + case "float": + return `${addon.accessCtor("Float")}.new_(${convertPrimToEmscripten(addon, "double", expr)})`; + + case "double": + case "int32_t": + return `emscripten::val(${expr})`; + + case "count_t": + return `emscripten::val(std::make_signed_t(${expr}))`; + + case "int64_t": + case "uint64_t": + return `emscripten::val(${expr})`; + + case "std::chrono::milliseconds": + return `emscripten::val(std::chrono::milliseconds(${expr}).count())`; + + case "StringData": + case "std::string_view": + case "std::string": + return `([&] (auto&& sd) { + if(sd.size() == 0) { + return emscripten::val(""); + } else { + return emscripten::val(std::string(sd.data(), sd.size())); + } + }(${expr}))`; + + case "EncryptionKey": //TODO throw Error("EncryptionKey is not supported. Encryption is not supported in WASM"); + return "emscripten::val()"; + case "OwnedBinaryData": + case "BinaryData": + // construct a typed view around the C++ data. the underlying array buffer is the Emscripten heap + // call slice to create a copy and return the new underlying buffer + return `([&] (const auto& bd) -> emscripten::val { + emscripten::val typed_array(emscripten::typed_memory_view(bd.size(), bd.data())); + return typed_array.call("slice")["buffer"]; + }(${expr}))`; + + case "Mixed": + return `EMVAL_FROM_Mixed(${expr})`; + case "QueryArg": + // We _could_ support this, but no reason to. + throw Error("QueryArg should only be used for conversion to C++"); + + case "ObjectId": + case "UUID": + case "Decimal128": + return `${addon.accessCtor(type)}.new_(${convertPrimToEmscripten(addon, "std::string", `${expr}.to_string()`)})`; + + case "EJson": + case "EJsonObj": + case "EJsonArray": + return `${addon.accessCtor("EJSON_parse")}(${convertPrimToEmscripten(addon, "std::string", expr)})`; + + case "bson::BsonArray": + case "bson::BsonDocument": + return convertPrimToEmscripten(addon, "EJsonObj", `bson::Bson(${expr}).to_string()`); + + case "AppError": + // This matches old JS SDK. The C++ type will be changing as part of the unify error handleing project. + return `([&] (const app::AppError& err) { + auto jsErr = emscripten::val::global("Error")(emscripten::val(err.what())); + jsErr.set("code", double(err.code())); + return jsErr; + }(${expr}))`; + case "std::exception_ptr": + return `toEmscriptenException(${expr})`; + case "std::error_code": + return `toEmscriptenErrorCode(${expr})`; + case "Status": + return `([&] (const Status& status) { + if (status.is_ok()) { + return emscripten::val::undefined(); + } else { + return emscripten::val(status.reason().c_str()); + } + }(${expr}))`; + } + assert.fail(`unexpected primitive type '${type}'`); +} +function convertPrimFromEmscripten(addon: BrowserAddon, type: string, expr: string): string { + switch (type) { + case "void": + return `((void)(${expr}))`; + + case "bool": + return `(${expr}).as()`; + + case "double": + return `(${expr}).as()`; + case "float": + return `(${expr}["value"]).as()`; + + case "int32_t": + return `(${expr}).as()`; + + case "count_t": + // NOTE: using Int64 here is important to correctly handle -1.0 aka npos. + // FIXME should use int64_t + return `size_t((${expr}).as())`; + + case "int64_t": + return `${expr}.as()`; + case "uint64_t": + return `${expr}.as()`; + + case "std::chrono::milliseconds": + return `std::chrono::milliseconds(${expr}.as())`; + + case "std::string": + return `${addon.get()}->wrapString((${expr}).as())`; + + case "StringData": + case "std::string_view": + return `${convertPrimFromEmscripten(addon, "std::string", expr)}`; + + case "BinaryData": + return `BinaryData(${addon.get()}->wrapString(toBinaryData(${expr})))`; + case "OwnedBinaryData": + return `toOwnedBinaryData(${expr})`; + + case "EncryptionKey": //TODO assert.fail("Encryption is not supported in WASM."); + return "std::vector()"; + case "Mixed": + return `EMVAL_TO_Mixed(${expr})`; + case "QueryArg": { + const mixed = new Primitive("Mixed"); + return ` + ([&] (const emscripten::val& v) -> ${new Primitive(type).toCpp()} { + if (v.isArray()) { + return ${convertFromEmscripten(addon, new Template("std::vector", [mixed]), "v")}; + } else { + return ${convertFromEmscripten(addon, mixed, "v")}; + } + })(${expr})`; + } + + case "UUID": + case "Decimal128": + case "ObjectId": + return `${type}((${expr}.call("toString").c_str()))`; + + case "EJson": + case "EJsonObj": + case "EJsonArray": + return convertPrimFromEmscripten(addon, "std::string", `${addon.accessCtor("EJSON_stringify")}(${expr})`); + + case "bson::BsonArray": + case "bson::BsonDocument": + return `${type}(bson::parse(${convertPrimFromEmscripten(addon, "EJsonObj", expr)}))`; + + case "AppError": + assert.fail("Cannot convert AppError to C++, only from C++."); + } + assert.fail(`unexpected primitive type '${type}'`); +} + +function convertToEmscripten(addon: BrowserAddon, type: Type, expr: string): string { + const c = convertToEmscripten.bind(null, addon); // shortcut for recursion + switch (type.kind) { + case "Primitive": + return convertPrimToEmscripten(addon, type.name, expr); + case "Pointer": + return `[&] (const auto& ptr){ + if constexpr(requires{ bool(ptr); }) { // support claiming that always-valid iterators are pointers. + REALM_ASSERT(bool(ptr) && "Must mark nullable pointers with Nullable<> in spec"); + } + return ${c(type.type, "*ptr")}; + } (${expr})`; + + case "Opaque": + return `emscripten::val(reinterpret_cast(&(${expr})))`; + + case "Const": + case "Ref": + case "RRef": // Note: not explicitly taking advantage of moveability yet. TODO? + return c(type.type, expr); + + case "KeyType": + return c(type.type, `(${expr}).value`); + + case "Template": + // Most templates only take a single argument so do this here. + const inner = type.args[0]; + switch (type.name) { + case "std::shared_ptr": + if (inner.kind == "Class" && inner.sharedPtrWrapped) return `EMVAL_FROM_SHARED_${inner.name}(${expr})`; + return c(new Pointer(inner), expr); + case "Nullable": { + return `[&] (auto&& val) { return !val ? emscripten::val::null() : ${c(inner, "FWD(val)")}; }(${expr})`; + } + case "std::optional": + return `[&] (auto&& opt) { return !opt ? emscripten::val::undefined() : ${c(inner, "*FWD(opt)")}; }(${expr})`; + case "std::vector": + return `[&] (auto&& vec) { + auto out = emscripten::val::array(); + for (auto&& e : vec) { + out.call("push", ${c(inner, "e")}); + } + return out; + }(${expr})`; + case "std::pair": + case "std::tuple": + return ` + [&] (auto&& tup) { + auto out = emscripten::val::array(); // of size ${type.args.length} + ${type.args + .map((arg, i) => `out.call("push", ${c(arg, `std::get<${i}>(FWD(tup))`)});`) + .join("\n")} + return out; + }(${expr})`; + case "std::map": + case "std::unordered_map": + // Note: currently assuming that key is natively supported by js object setter (string or number). + return ` + [&] (auto&& map) { + auto out = emscripten::val::object(); + for (auto&& [k, v] : map) { + out.set(k, ${c(type.args[1], "v")}); + } + return out; + }(${expr})`; + case "AsyncCallback": + case "util::UniqueFunction": + case "std::function": + assert.equal(inner.kind, "Func"); + return c(inner, `FWD(${expr})`); + case "AsyncResult": + assert.fail("Should never see AsyncResult here"); + } + return assert.fail(`unknown template ${type.name}`); + + case "Class": + assert(!type.sharedPtrWrapped, `should not directly convert from ${type.name} without shared_ptr wrapper`); + return `EMVAL_FROM_CLASS_${type.name}(${expr})`; + + case "Struct": + return `${type.toEmscripten().name}(${expr})`; + + case "Func": + // TODO: see if we want to try to propagate a function name in rather than always making them anonymous. + return ` + [&] (auto&& cb) -> emscripten::val { + if constexpr(std::is_constructible_v) { + REALM_ASSERT(bool(cb) && "Must mark nullable callbacks with Nullable<> in spec"); + } + const auto callBlock = ${addon.get()}->startCall(); + ${tryWrap(` + return ${c( + type.ret, + `cb(${type.args + .map((arg, i) => convertFromEmscripten(addon, arg.type, `arg${i}`)) + .join(", ")})`, + )}; + `)} + }(${expr})`; + + case "Enum": + return `emscripten::val(int(${expr}))`; + + default: + const _exhaustiveCheck: never = type; + return _exhaustiveCheck; + } +} +function convertFromEmscripten(addon: BrowserAddon, type: Type, expr: string): string { + const c = convertFromEmscripten.bind(null, addon); // shortcut for recursion + switch (type.kind) { + case "Primitive": + return convertPrimFromEmscripten(addon, type.name, expr); + case "Pointer": + return `&(${c(type.type, expr)})`; + case "Opaque": + return `(*(reinterpret_cast<${type.name}*>(${expr}.as())))`; + + case "KeyType": + return `${type.name}(${c(type.type, expr)})`; + + case "Const": + case "Ref": + return c(type.type, expr); + + case "RRef": { + // For now, copying. TODO Consider moving instead, although we may want a marker in JS code. + // Also, for now, only doing this if the child is a class, since A) that is where we need it, + // and B) other things may use lambdas which cause compile failures with our `auto(expr)` + // emulation until C++20. + const inner = c(type.type, expr); + return type.type.kind == "Class" ? `REALM_DECAY_COPY(${inner})` : inner; + } + + case "Template": + // Most templates only take a single argument so do this here. + const inner = type.args[0]; + + switch (type.name) { + case "std::shared_ptr": + if (inner.kind == "Class" && inner.sharedPtrWrapped) return `EMVAL_TO_SHARED_${inner.name}(${expr})`; + return `std::make_shared<${inner.toCpp()}>(${c(inner, expr)})`; + case "Nullable": + return `[&] (emscripten::val val) { return val.isNull() ? ${inner.toCpp()}() : ${c( + inner, + "val", + )}; }(${expr})`; + case "std::optional": + return `[&] (emscripten::val val) { + return val.isUndefined() ? ${type.toCpp()}() : ${c(inner, "val")}; + }(${expr})`; + case "std::vector": + return `[&] (const emscripten::val vec) { + assert(vec.isArray()); + auto out = std::vector<${inner.toCpp()}>(); + + const uint32_t length = vec["length"].as(); + out.reserve(length); + for (uint32_t i = 0; i < length; i++) { + out.push_back(${c(inner, "vec[i]")}); + } + return out; + }(${expr})`; + case "std::tuple": + case "std::pair": + const suffix = type.name.split(":")[2]; + const nArgs = type.args.length; + return `[&] (const emscripten::val& arr) { + if (arr["length"].as() != ${nArgs}u) + emscripten::val("Need an array with exactly ${nArgs} elements").throw_(); + return std::make_${suffix}(${type.args.map((arg, i) => c(arg, `arr[${i}u]`))}); + }(${expr})`; + case "std::map": + case "std::unordered_map": + // For now, can only convert string-keyed maps to C++. + // We could also support numbers pretty easily. Anything else will be problematic. + // Consider list-of-pairs for keys that aren't strings or numbers. + assert.deepEqual(type.args[0], new Primitive("std::string")); + return `[&] (const emscripten::val obj) { + auto out = ${type.toCpp()}(); + auto entries = emscripten::val::global("Object")["entries"](obj); + const auto length = entries["length"].as(); + for (uint32_t i = 0; i < length; i++) { + out.insert({ + entries[i][0].as(), + ${c(type.args[1], "entries[i][1]")} + }); + } + return out; + }(${expr})`; + case "AsyncCallback": + case "util::UniqueFunction": + case "std::function": + return `${type.toCpp()}(${c(inner, expr)})`; + } + return assert.fail(`unknown template ${type.name}`); + + case "Class": + if (type.sharedPtrWrapped) return `*EMVAL_TO_SHARED_${type.name}(${expr})`; + return `EMVAL_TO_CLASS_${type.name}(${expr})`; + + case "Struct": + return `${type.fromEmscripten().name}(${expr})`; + + case "Func": + const lambda = ` + [ + _cb = FWD(${expr}) + ] + (${type.args + .map(({ name, type }) => `${type.toCpp()} ${type.isTemplate("IgnoreArgument") ? "" : name}`) + .join(", ")} + ) -> ${type.ret.toCpp()} + { + return ${c( + type.ret, + `_cb( + ${type + .argsSkippingIgnored() + .map(({ name, type }) => convertToEmscripten(addon, type, `FWD(${name})`)) + .join(", ")})`, + )}; + }`; + return lambda; + // if (!type.isOffThread) return lambda; + + // For now assuming that all void-returning functions are "notifications" and don't need to block until done. + // Non-void returning functions *must* block so they have something to return. + // const shouldBlock = !type.ret.isVoid(); + // return shouldBlock ? `schedulerWrapBlockingFunction(${lambda})` : `util::EventLoopDispatcher(${lambda})`; + + case "Enum": + return `${type.cppName}((${expr}).as())`; + + default: + const _exhaustiveCheck: never = type; + return _exhaustiveCheck; + } +} + +declare module "@realm/bindgen/bound-model" { + interface Struct { + toEmscripten: () => CppFunc; + fromEmscripten: () => CppFunc; + } + interface Method { + readonly emscriptenDescriptorType: string; + } +} + +function constCast(obj: T) { + return obj as { -readonly [k in keyof T]: T[k] }; +} + +constCast(InstanceMethod.prototype).emscriptenDescriptorType = "InstanceMethod"; +constCast(StaticMethod.prototype).emscriptenDescriptorType = "StaticMethod"; +constCast(Property.prototype).emscriptenDescriptorType = "InstanceAccessor"; + +class BrowserCppDecls extends CppDecls { + addon = pushRet(this.classes, new BrowserAddon()); + boundSpec: BoundSpec; + methodFunctions: string[] = []; + + constructor(spec: BoundSpec) { + super(); + this.boundSpec = spec; + + for (const enm of spec.enums) { + this.static_asserts.push(`sizeof(${enm.cppName}) <= sizeof(int32_t), "we only support enums up to 32 bits"`); + for (const { name, value } of enm.enumerators) { + this.static_asserts.push(`${enm.cppName}(int(${value})) == ${enm.cppName}::${name}`); + } + } + + for (const struct of spec.records) { + // Lazily create the to/from conversions only as needed. This is important because some structs + // can only be converted in one direction. + let toEmscripten: CppFunc | undefined; + let fromEmscripten: CppFunc | undefined; + + struct.toEmscripten = () => { + if (!toEmscripten) { + toEmscripten = new CppFunc( + `STRUCT_TO_EMVAL_${struct.name}`, + "emscripten::val", + [new CppVar(`const ${struct.cppName}&`, "in")], + { + body: ` + auto out = emscripten::val::object(); + ${struct.fields + .filter((field) => !field.type.isFunction() && field.isOptedInTo) + .map( + (field) => + `out.set("${field.jsName}", ${convertToEmscripten( + this.addon, + field.type, + `in.${field.cppName}`, + )});`, + ) + .join("\n")} + return out; + `, + }, + ); + this.free_funcs.push(toEmscripten); + } + return toEmscripten; + }; + + struct.fromEmscripten = () => { + if (!fromEmscripten) { + for (const field of struct.fields) { + if (field.cppName && field.cppName.endsWith(")")) { + // If this fires, we should consider a way to mark these fields as only being for one-way conversion. + throw new Error( + `Attempting JS->C++ conversion of ${struct.name}::${field.name} which looks like it may be a method`, + ); + } + } + fromEmscripten = new CppFunc( + `STRUCT_FROM_EMVAL_${struct.name}`, + struct.cppName, + [new CppVar("emscripten::val", "val")], + { + body: ` + auto out = ${struct.cppName}(); + ${struct.fields + .filter((field) => field.isOptedInTo) + .map( + (field) => `{ + auto field = val["${field.jsName}"]; + if (!field.isUndefined()) { + // Make functions on structs behave like bound methods. + if (field.instanceof(emscripten::val::global("Function"))) + field = field.call("bind", val); + out.${field.cppName} = ${convertFromEmscripten(this.addon, field.type, "field")}; + } else if constexpr (${field.required ? "true" : "false"}) { + emscripten::val("${struct.jsName}::${field.jsName} is required").throw_(); + } + }`, + ) + .join("\n")} + return out; + `, + }, + ); + this.free_funcs.push(fromEmscripten); + } + return fromEmscripten; + }; + } + + for (const cls of spec.classes) { + assert( + !cls.sharedPtrWrapped || (!cls.base && cls.subclasses.length == 0), + `We don't support mixing sharedPtrWrapped and class hierarchies. ${cls.name} requires this.`, + ); + + this.addon.addClass(cls); + + const baseType = cls.sharedPtrWrapped ? `std::shared_ptr<${cls.cppName}>` : cls.rootBase().cppName; + const derivedType = cls.sharedPtrWrapped ? `std::shared_ptr<${cls.cppName}>` : cls.cppName; + const ptr = (expr: string) => `reinterpret_cast<${baseType}*>(${expr}.as())`; + const casted = (expr: string) => (cls.base ? `static_cast<${derivedType}*>(${ptr(expr)})` : ptr(expr)); + const self = `(${cls.needsDeref ? "**" : "*"}${casted("arg0")})`; + + const selfCheck = (isStatic: boolean) => { + return isStatic ? "" : ""; + }; + + for (const method of cls.methods) { + if (!method.isOptedInTo) continue; + + const argOffset = method.isStatic ? 0 : 1; // `this` takes arg 0 if not static + const args = method.sig.args.map((a, i) => convertFromEmscripten(this.addon, a.type, `arg${i + argOffset}`)); + // console.log(`GENERATING method = ${method.id} length = ${method.sig.args.length + argOffset}\n`); + this.free_funcs.push( + this.addon.addFunc(method.id, method.sig.args.length + argOffset, { + body: ` + ${selfCheck(method.isStatic)} + return ${convertToEmscripten(this.addon, method.sig.ret, method.call({ self }, ...args))}; + `, + }), + ); + this.methodFunctions.push(method.id); + } + + if (cls.iterable) { + this.free_funcs.push( + this.addon.addFunc(cls.iteratorMethodId(), 1, { + body: ` + emscripten::val jsIt = emscripten::val::object(); + auto& self = ${self}; + + std::function incrementIterators = [begin = std::make_move_iterator(self.begin()), end = std::make_move_iterator(self.end())]() mutable { + emscripten::val iteratorResult = emscripten::val::object(); + if (begin == end) { + iteratorResult.set("done", true); + } + else { + iteratorResult.set("value", ${convertToEmscripten(this.addon, cls.iterable, "*begin")}); + ++begin; + } + return iteratorResult; + }; + + // Allocate memory for the lambda on the heap + auto* lambdaPtr = new decltype(incrementIterators)(incrementIterators); + // Get the address of the lambda + std::uintptr_t lambdaAddress = reinterpret_cast(lambdaPtr); + + int jsInternalIteratorCall = EM_ASM_INT({ + var myFunction = function() { + return Module["_internal_iterator"]($0); + }; + return Emval.toHandle(myFunction); + }, lambdaAddress); + jsIt.set("next", emscripten::val::take_ownership((emscripten::EM_VAL)(jsInternalIteratorCall))); + return jsIt; + `, + }), + ); + this.methodFunctions.push(cls.iteratorMethodId()); + } + + const refType = cls.sharedPtrWrapped ? `const ${derivedType}&` : `${derivedType}&`; + const kind = cls.sharedPtrWrapped ? "SHARED" : "CLASS"; + this.free_funcs.push( + new CppFunc(`EMVAL_TO_${kind}_${cls.name}`, refType, [new CppVar("emscripten::val", "val")], { + attributes: "[[maybe_unused]]", + body: ` + emscripten::val external = ${this.addon.accessExtractor(cls)}(val); + const auto ptr = ${casted(`external`)}; + ${ + cls.sharedPtrWrapped + ? `if (!*ptr) emscripten::val("Attempting to use an instanace of ${cls.name} holding a null shared_ptr. Did you call $resetSharedPtr on it already?").throw_();` + : "" + } + return *ptr; + `, + }), + ); + + const nullCheck = cls.sharedPtrWrapped + ? 'REALM_ASSERT(bool(val) && "Must mark nullable pointers with Nullable<> in spec");' + : ""; + + if (!cls.abstract) { + this.free_funcs.push( + new CppFunc(`EMVAL_FROM_${kind}_${cls.name}`, "emscripten::val", [new CppVar(derivedType, "val")], { + attributes: "[[maybe_unused]]", + body: ` + ${nullCheck} + return ${this.addon.accessCtor( + cls, + )}.new_(emscripten::val(reinterpret_cast(new auto(std::move(val))))); + `, + }), + new CppFunc(`${cls.name}_deleter`, "void", [new CppVar("emscripten::val", "pointer")], { + body: + kind === "SHARED" + ? `delete reinterpret_cast*>(pointer.as());` + : `delete reinterpret_cast<${cls.cppName}*>(pointer.as());`, + }), + ); + this.methodFunctions.push(`${cls.name}_deleter`); + } + } + + // Adding internal iterator function + this.free_funcs.push( + new CppFunc("_internal_iterator", "emscripten::val", [new CppVar("std::uintptr_t", "lambdaAddress")], { + body: ` + std::function* func = reinterpret_cast*>(lambdaAddress); + emscripten::val val = (*func)(); + + // Deallocate the lambda from the heap if it's the last element + if (!val["done"].isUndefined()) { + delete func; + } + return val; + `, + }), + ); + + this.free_funcs.push( + new CppFunc("EMVAL_FROM_Mixed", "emscripten::val", [new CppVar("Mixed", "val")], { + body: ` + if (val.is_null()) + return emscripten::val::null(); + switch (val.get_type()) { + ${spec.mixedInfo.getters + .map( + (g) => ` + case DataType::Type::${g.dataType}: + return ${convertToEmscripten(this.addon, g.type, `val.${g.getter}()`)}; + `, + ) + .join("\n")} + // The remaining cases are never stored in a Mixed. + ${spec.mixedInfo.unusedDataTypes.map((t) => `case DataType::Type::${t}: break;`).join("\n")} + } + REALM_UNREACHABLE(); + `, + }), + new CppFunc("EMVAL_TO_Mixed", "Mixed", [new CppVar("emscripten::val", "val")], { + body: ` + auto type = val.typeOf().as(); + if (type == "string") { + return ${convertFromEmscripten(this.addon, spec.types["StringData"], "val")}; + + } else if (type == "boolean") { + return ${convertFromEmscripten(this.addon, spec.types["bool"], "val")}; + + } else if (type == "number") { + return val.as(); + + } else if (type == "bigint") { + return val.as(); + + } else if (type == "object") { + if(val.isNull()) { + return Mixed(); + } + if (val.instanceof(emscripten::val::global("ArrayBuffer"))) { + return ${convertFromEmscripten(this.addon, spec.types["BinaryData"], "val")}; + } + else if (val.instanceof(emscripten::val::global("DataView"))) { + return ${convertFromEmscripten(this.addon, spec.types["BinaryData"], 'val["buffer"]')}; + } + ${ + // This list should be sorted in in roughly the expected frequency since earlier entries will be faster. + [ + ["Obj", "Obj"], + ["Timestamp", "Timestamp"], + ["float", "Float"], + ["ObjLink", "ObjLink"], + ["ObjectId", "ObjectId"], + ["Decimal128", "Decimal128"], + ["UUID", "UUID"], + ] + .map( + ([typeName, jsName]) => + `else if (val.instanceof(${this.addon.accessCtor(jsName)})) { + return ${convertFromEmscripten(this.addon, spec.types[typeName], "val")}; + }`, + ) + .join(" ") + } + + const auto ctorName = + val["constructor"]["name"].as(); + + emscripten::val::global("Error")(util::format("Unable to convert an object with ctor '%1' to a Mixed", ctorName)).throw_(); + } else { + // NOTE: must not treat undefined as null here, because that makes Optional ambiguous. + emscripten::val::global("Error")(util::format("Can't convert %1 to Mixed", type)).throw_(); + } + + REALM_UNREACHABLE(); + `, + }), + ); + + this.addon.generateMembers(); + } + + outputDefsTo(out: (...parts: string[]) => void) { + super.outputDefsTo(out); + out(` + void wasm_init() + { + if (!RealmAddon::self) { + RealmAddon::self = std::make_unique(); + } + } + void injectExternalTypes(emscripten::val val) + { + RealmAddon::self->injectInjectables(val); + } + `); + + // export method functions via embind + out(`\nEMSCRIPTEN_BINDINGS(realm_c_api) {`); + out("\nusing emscripten::function;"); + this.methodFunctions.map((fun: string) => { + out(`\nfunction("${fun}", &${fun});`); + }); + + // this.boundSpec.classes.forEach((c) => { + // out(`\nfunction("${c.jsName}_deleter", &${c.jsName}_deleter);`); + // }); + + out(`\nfunction("_internal_iterator", &_internal_iterator);`); + out(`\nfunction("wasmInit", &wasm_init);`); + out(`\nfunction("injectInjectables", &injectExternalTypes);`); + + out("\n}"); + } +} + +export function generate({ rawSpec, spec, file: makeFile }: TemplateContext): void { + const out = makeFile("wasm_init.cpp", clangFormat); + + // HEADER + out(`// This file is generated: Update the spec instead of editing this file directly`); + + for (const header of rawSpec.headers) { + out(`#include <${header}>`); + } + + out(` + #include + #include + #include + + namespace realm::js::wasm { + namespace { + `); + + new BrowserCppDecls(doJsPasses(spec)).outputDefsTo(out); + + out(` + } // namespace + } // namespace realm::js::wasm + `); +} diff --git a/packages/realm/bindgen/wasm_opt_in_spec.yml b/packages/realm/bindgen/wasm_opt_in_spec.yml new file mode 100644 index 0000000000..19dec01847 --- /dev/null +++ b/packages/realm/bindgen/wasm_opt_in_spec.yml @@ -0,0 +1,621 @@ +# -------------------- +# Description of file: +# -------------------- +# List all the unique names of what to opt in to from the general spec. +# +# The following can be listed: +# * `classes` and their `methods` +# * Methods, static methods, constructors, and properties in the general `spec.yml` +# should all be listed in this opt-in list as `methods`. +# * `records` and their `fields` +# +# If all methods in a class, or all fields of a record, are opted out of, +# the entire class/record should be removed. + +records: + Property: + fields: + - name + - public_name + - type + - object_type + - link_origin_property_name + - is_primary + - is_indexed + - is_fulltext_indexed + - column_key + + ObjectSchema: + fields: + - name + - persisted_properties + - computed_properties + - primary_key + - table_key + - table_type + + RealmConfig: + fields: + - path + - cache + - encryption_key + - fifo_files_fallback_path + - in_memory + - schema + - schema_version + - schema_mode + - disable_format_upgrade + - sync_config + - force_sync_history + - migration_function + - initialization_function + - should_compact_on_launch_function + - automatically_handle_backlinks_in_migrations + + UserIdentity: + fields: + - id + - provider_type + + UserAPIKey: + fields: + - id + - key + - name + - disabled + + SyncConfig: + fields: + - user + - partition_value + - stop_policy + - flx_sync_requested + - error_handler + - custom_http_headers + - client_validate_ssl + - ssl_trust_certificate_path + - ssl_verify_callback + - cancel_waits_on_nonfatal_error + - proxy_config + - client_resync_mode + - notify_before_client_reset + - notify_after_client_reset + + SyncProxyConfig: + fields: + - address + - port + - type + + SyncSubscription: + fields: + - id + - created_at + - updated_at + - name + - object_class_name + - query_string + + ObjectChangeSet: + fields: + - is_deleted + - changed_columns + + CollectionChangeSet: + fields: + - deletions + - insertions + - modifications + - modifications_new + + DictionaryChangeSet: + fields: + - deletions + - insertions + - modifications + + BindingContext: + fields: + - did_change + - before_notify + - schema_did_change + + ResumptionDelayInfo: + fields: + - max_resumption_delay_interval + - resumption_delay_interval + - resumption_delay_backoff_multiplier + - delay_jitter_divisor + + SyncClientTimeouts: + fields: + - connect_timeout + - connection_linger_time + - ping_keepalive_period + - pong_keepalive_timeout + - fast_reconnect_limit + - reconnect_backoff_info + + SyncClientConfig: + fields: + - user_agent_binding_info + - multiplex_sessions + - timeouts + + SyncError: + fields: + - status + - is_fatal + - simple_message + - logURL + - user_info + - is_client_reset_requested + - compensating_writes_info + + Request: + fields: + - method + - url + - timeout_ms + - headers + - body + + Response: + fields: + - http_status_code + - custom_status_code + - headers + - body + + DeviceInfo: + fields: + - platform_version + - sdk_version + - sdk + - device_name + - device_version + - framework_name + - framework_version + - bundle_id + + AppConfig: + fields: + - app_id + - transport + - base_url + - base_file_path + - default_request_timeout_ms + - device_info + - sync_client_config + - metadata_mode + - custom_encryption_key + # - security_access_group + + CompensatingWriteErrorInfo: + fields: + - object_name + - reason + - primary_key + + GeoBox: + fields: + - lo + - hi + + GeoPolygon: + fields: + - points + + GeoCircle: + fields: + - center + - radius_radians + + GeoPoint: + fields: + - latitude + - longitude + - altitude + + SaltedFileIdent: + fields: + - ident + - salt + +classes: + ###################### + # FROM JS EXTRA SPEC # + ###################### + + # These JsPlatformHelpers are used for React Native. + JsPlatformHelpers: + methods: + - default_realm_file_directory + - set_default_realm_file_directory + - ensure_directory_exists_for_file + - copy_bundled_realm_files + - remove_realm_files_from_directory + - remove_file + - remove_directory + - get_cpu_arch + + WeakSyncSession: + methods: + - weak_copy_of + - raw_dereference + + ##################### + # FROM GENERAL SPEC # + ##################### + + Helpers: + methods: + - get_table + - get_keypath_mapping + - results_append_query + - make_object_notifier + - set_binding_context + - get_or_create_object_with_primary_key + # - make_network_transport + - delete_data_for_object + - base64_decode + - make_logger_factory + - make_logger + - simulate_sync_error + - consume_thread_safe_reference_to_shared_realm + - file_exists + - erase_subscription + - get_results_description + # - feed_buffer + - make_ssl_verify_callback + - needs_file_format_upgrade + - sync_user_as_app_user + - app_user_as_sync_user + + LogCategoryRef: + methods: + - set_default_level_threshold + - get_category + + Logger: + methods: + - set_default_logger + + ConstTableRef: + methods: + - get_key + - get_column_type + - get_link_target + - get_object + - try_get_object + - query + - find_primary_key + + TableRef: + methods: + - create_object + - remove_object + - get_link_target + - clear + - get_primary_key_column + + Obj: + methods: + - is_valid + - get_table + - get_key + - get_any + - set_any + - set_collection + - add_int + - get_linked_object + - get_backlink_count + - get_backlink_view + - create_and_set_linked_object + + Timestamp: + methods: + - make + - get_seconds + - get_nanoseconds + + ObjLink: + methods: + - get_table_key + - get_obj_key + + Query: + methods: + - get_table + - get_description + + Results: + methods: + - from_table + - from_table_view + - is_valid + - get_query + - get_object_type + - get_type + - size + - index_of + - index_of_obj + - get_obj + - get_any + - get_list + - get_dictionary + - sort_by_names + - snapshot + - max + - min + - average + - sum + - clear + - add_notification_callback + + Realm: + methods: + - get_shared_realm + - get_synchronized_realm + - get_schema_version + - config + - schema + - schema_version + - is_in_transaction + - is_in_migration + - is_closed + - is_empty + - sync_session + - get_latest_subscription_set + - begin_transaction + - commit_transaction + - cancel_transaction + - update_schema + - compact + - convert + - verify_open + - create_key_path_array + - close + # JS-specific + - DOLLAR_addr + - DOLLAR_resetSharedPtr + + RealmCoordinator: + methods: + - clear_all_caches + + ObjectNotifier: + methods: + - add_callback + + NotificationToken: + methods: + - for_object + - unregister + + Collection: + methods: + - get_object_schema + - get_type + - size + - is_valid + - get_any + - as_results + - snapshot + + List: + methods: + - make + - get_obj + - get_list + - get_dictionary + - move + - remove + - remove_all + - swap + - delete_all + - insert_any + - insert_collection + - insert_embedded + - set_any + - set_embedded + - set_collection + + Set: + methods: + - make + - get_obj + - insert_any + - remove_any + - remove_all + - delete_all + + Dictionary: + methods: + - make + - get_keys + - get_values + - get_list + - get_dictionary + - contains + - add_key_based_notification_callback + - insert_any + - insert_embedded + - insert_collection + - try_get_any + - remove_all + - try_erase + + GoogleAuthCode: + methods: + - make + + GoogleIdToken: + methods: + - make + + AppCredentials: + methods: + - facebook + - anonymous + - apple + - google_auth + - google_id + - custom + - username_password + - function + - api_key + + SyncUser: + methods: + - is_logged_in + + User: + methods: + - has_device_id + - device_id + - user_profile + - identities + - custom_data + - subscribe + - unsubscribe + - path_for_realm + - app + - user_id + - app_id + - legacy_identities + - access_token + - refresh_token + - state + - access_token_refresh_required + - request_refresh_location + - request_access_token + - track_realm + + UserProfile: + methods: + - data + + App: + methods: + - config + - current_user + - all_users + - sync_manager + - get_app + - clear_cached_apps + - log_in_with_credentials + - log_out_user + - refresh_custom_data + - link_user + - switch_user + - remove_user + - delete_user + - usernamePasswordProviderClient + - userAPIKeyProviderClient + - push_notification_client + - subscribe + - unsubscribe + - call_function + - make_streaming_request + - update_base_url + - get_base_url + - immediately_run_file_actions + + # WatchStream: + # methods: + # - make + # - state + # - error + # - next_event + + PushClient: + methods: + - register_device + - deregister_device + + UsernamePasswordProviderClient: + methods: + - register_email + - retry_custom_confirmation + - confirm_user + - resend_confirmation_email + - reset_password + - send_reset_password_email + - call_reset_password_function + + UserAPIKeyProviderClient: + methods: + - create_api_key + - fetch_api_key + - fetch_api_keys + - delete_api_key + - enable_api_key + - disable_api_key + + SyncManager: + methods: + - has_existing_sessions + - set_session_multiplexing + - set_log_level + - set_logger_factory + - set_user_agent + - reconnect + - get_existing_active_session + - get_all_sessions_for + + AsyncOpenTask: + methods: + - start + - cancel + - register_download_progress_notifier + - unregister_download_progress_notifier + # JS-specific + - DOLLAR_resetSharedPtr + + SyncSession: + methods: + - state + - connection_state + - config + - full_realm_url + - wait_for_upload_completion + - wait_for_download_completion + - register_progress_notifier + - unregister_progress_notifier + - register_connection_change_callback + - unregister_connection_change_callback + - revive_if_needed + - force_close + - handle_reconnect + - user + - get_file_ident + # JS-specific + - DOLLAR_resetSharedPtr + + SyncSubscriptionSet: + methods: + - version + - state + - error_str + - size + - make_mutable_copy + - get_state_change_notification + - find_by_name + - find_by_query + - refresh + + MutableSyncSubscriptionSet: + methods: + - clear + - insert_or_assign_by_name + - insert_or_assign_by_query + - erase_by_name + - erase_by_query + - commit + + Geospatial: + methods: + - make_from_circle + - make_from_box + - make_from_polygon diff --git a/packages/realm/binding/wasm/CMakeLists.txt b/packages/realm/binding/wasm/CMakeLists.txt new file mode 100644 index 0000000000..b2649b79d3 --- /dev/null +++ b/packages/realm/binding/wasm/CMakeLists.txt @@ -0,0 +1,64 @@ +# The initial part of this file is basically just copied from the root package CML files. +# TODO: look into how to commonize some of this. + +include(CheckCXXCompilerFlag) + +set(SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../) +set(BINDGEN_DIR ${SDK_DIR}/bindgen) +set(BINDING_DIR ${SDK_DIR}/binding) + +cmake_minimum_required(VERSION 3.17) +project(RealmJS) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# set(CMAKE_CXX_VISIBILITY_PRESET hidden) +# set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(REALM_BUILD_LIB_ONLY ON) +set(REALM_ENABLE_SYNC ON) + +add_subdirectory(${BINDGEN_DIR}/vendor/realm-core realm-core EXCLUDE_FROM_ALL) + +add_library(realm-js OBJECT) +target_link_libraries(realm-js Realm::ObjectStore) +target_compile_options(realm-js PRIVATE -Wall -Wextra) +target_include_directories(realm-js PRIVATE "${BINDGEN_DIR}/src") +target_include_directories(realm-js PRIVATE "${BINDING_DIR}") + +file(GLOB_RECURSE SDK_TS_FILES + LIST_DIRECTORIES false + CONFIGURE_DEPENDS + ${SDK_DIR}/bindgen/src/*.ts +) + +# Each template command should include its file as an explicit dependency. +# This avoids needing to re-run all generators for changes that could only affect one of them. +list(FILTER SDK_TS_FILES EXCLUDE REGEX "templates/[^/]*\.ts$") + +set(JS_SPEC_FILE ${SDK_DIR}/bindgen/js_spec.yml) +set(JS_OPT_IN_FILE ${SDK_DIR}/bindgen/wasm_opt_in_spec.yml) +set(WASM_OUTPUT_DIR ${SDK_DIR}/prebuilds/wasm) + +bindgen( + TEMPLATE ${SDK_DIR}/bindgen/src/templates/wasm.ts + OUTPUTS wasm_init.cpp + OUTDIR ${CMAKE_CURRENT_BINARY_DIR} + SPECS ${JS_SPEC_FILE} + OPTIN ${JS_OPT_IN_FILE} + SOURCES ${SDK_TS_FILES} +) + +target_sources(realm-js PRIVATE wasm_init.cpp ${CMAKE_JS_SRC} ${BINDING_DIR}/wasm/platform.cpp) + +add_executable(realm-js-wasm) +target_link_options(realm-js-wasm PRIVATE -d -sALLOW_MEMORY_GROWTH=1 -sLLD_REPORT_UNDEFINED -sFETCH=1 -lembind -fwasm-exceptions -sEXPORT_ES6=1 -sWASM_BIGINT=1 -sENVIRONMENT=web -sSTACK_SIZE=131072) +target_link_libraries(realm-js-wasm realm-js) + +set_target_properties(realm-js-wasm PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${WASM_OUTPUT_DIR} +) +SET(CMAKE_EXECUTABLE_SUFFIX ".mjs") diff --git a/packages/realm/binding/wasm/platform.cpp b/packages/realm/binding/wasm/platform.cpp new file mode 100644 index 0000000000..56c8b1cfd1 --- /dev/null +++ b/packages/realm/binding/wasm/platform.cpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "../platform.hpp" + +static std::string s_default_realm_directory; + +namespace realm { + +void JsPlatformHelpers::set_default_realm_file_directory(std::string dir) +{ + s_default_realm_directory = dir; +} + +std::string JsPlatformHelpers::default_realm_file_directory() +{ + if (!s_default_realm_directory.empty()) { + return s_default_realm_directory; + } + else { + return std::string(""); + } +} + +void JsPlatformHelpers::ensure_directory_exists_for_file(const std::string&) +{ + throw std::runtime_error("Realm for browser does not support this method."); +} + +void JsPlatformHelpers::copy_bundled_realm_files() +{ + throw std::runtime_error("Realm for browser does not support this method."); +} + +void JsPlatformHelpers::remove_realm_files_from_directory(const std::string&) +{ + throw std::runtime_error("Realm for browser does not support this method."); +} + +void JsPlatformHelpers::remove_directory(const std::string&) +{ + throw std::runtime_error("Realm for browser does not support this method."); +} + +void JsPlatformHelpers::remove_file(const std::string&) +{ + throw std::runtime_error("Realm for browser does not support this method."); +} + +std::string JsPlatformHelpers::get_cpu_arch() +{ + return "N/A WASM"; +} + +} // namespace realm \ No newline at end of file diff --git a/packages/realm/package.json b/packages/realm/package.json index cedf4cadb2..cb05da872d 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -36,11 +36,16 @@ "types": "./dist/public-types/index.d.ts", "main": "./dist/platform/node/index.js", "react-native": "./index.react-native.js", + "browser": "./dist/platform/browser/index.js", "exports": { ".": { "types": "./dist/public-types/index.d.ts", "node": "./dist/platform/node/index.js", - "react-native": "./index.react-native.js" + "react-native": "./index.react-native.js", + "browser": { + "types": "./dist/platform/browser/index.d.ts", + "default": "./dist/platform/browser/index.js" + } }, "./experimental/base-url": { "types": "./dist/public-types/experimental/base-url.d.ts", @@ -80,6 +85,7 @@ "prebuild-apple:simulator": "wireit", "prebuild-android": "wireit", "prebuild-node": "wireit", + "prebuild-wasm": "wireit", "build:ts": "wireit", "build:node": "wireit", "bindgen:jsi": "wireit", @@ -132,6 +138,25 @@ "prebuilds/android" ] }, + "prebuild-node": { + "command": "cross-env-shell prebuild --runtime napi --arch $PREBUILD_ARCH -- --directory binding/node", + "env": { + "PREBUILD_ARCH": { + "external": true, + "default": "undefined" + } + } + }, + "prebuild-wasm": { + "command": "tsx ./src/scripts/build/cli.ts build-wasm", + "files": [ + "bindgen/vendor/realm-core/src", + "src/scripts" + ], + "output": [ + "prebuilds/wasm" + ] + }, "build:ts": { "command": "tsc --build", "dependencies": [ @@ -184,6 +209,27 @@ "src/binding/wrapper.generated.ts" ] }, + "bindgen:generate:wasm-wrapper": { + "command": "realm-bindgen --template bindgen/src/templates/wasm-wrapper.ts --spec bindgen/vendor/realm-core/bindgen/spec.yml --spec bindgen/js_spec.yml --opt-in bindgen/wasm_opt_in_spec.yml --output binding/generated", + "dependencies": [ + "bindgen:generate:spec-schema" + ], + "files": [ + "bindgen/vendor/realm-core/bindgen/spec.yml", + "bindgen/vendor/realm-core/bindgen/src", + "bindgen/js_spec.yml", + "bindgen/wasm_opt_in_spec.yml", + "bindgen/src", + "!bindgen/src/templates", + "bindgen/src/templates/base-wrapper.ts", + "bindgen/src/templates/wasm-wrapper.ts" + ], + "output": [ + "binding/generated/native.wasm.mjs", + "binding/generated/native.wasm.d.mts", + "binding/generated/native.wasm.d.cts" + ] + }, "bindgen:generate:spec-schema": { "command": "typescript-json-schema bindgen/vendor/realm-core/bindgen/tsconfig.json RelaxedSpec --include bindgen/vendor/realm-core/bindgen/src/spec/relaxed-model.ts --out bindgen/vendor/realm-core/bindgen/generated/spec.schema.json --required --noExtraProps", "files": [ @@ -202,15 +248,6 @@ } ] }, - "prebuild-node": { - "command": "cross-env-shell prebuild --runtime napi --arch $PREBUILD_ARCH -- --directory binding/node", - "env": { - "PREBUILD_ARCH": { - "external": true, - "default": "undefined" - } - } - }, "check-types": { "command": "tsc --project tsconfig.public-types-check.json", "dependencies": [ @@ -288,4 +325,4 @@ 6 ] } -} +} \ No newline at end of file diff --git a/packages/realm/src/OrderedCollection.ts b/packages/realm/src/OrderedCollection.ts index 475fb3e22b..c86c0a7335 100644 --- a/packages/realm/src/OrderedCollection.ts +++ b/packages/realm/src/OrderedCollection.ts @@ -38,8 +38,6 @@ import type { SetAccessor } from "./collection-accessors/Set"; const INDEX_NOT_FOUND = -1; -const DEFAULT_COLUMN_KEY = binding.Int64.numToInt(0) as unknown as binding.ColKey; - /** @internal */ export type OrderedCollectionInternal = binding.List | binding.Results | binding.Set; type PropertyType = string; @@ -959,7 +957,7 @@ export abstract class OrderedCollection< } else if (name) { throw new Error(`Cannot get property named '${name}' on a list of primitives`); } else { - return DEFAULT_COLUMN_KEY; + return binding.Int64.numToInt(0) as unknown as binding.ColKey; } } diff --git a/packages/realm/src/Realm.ts b/packages/realm/src/Realm.ts index 748556265c..4a780a47aa 100644 --- a/packages/realm/src/Realm.ts +++ b/packages/realm/src/Realm.ts @@ -22,7 +22,7 @@ import { TypeAssertionError } from "./errors"; import { extendDebug } from "./debug"; import { flags } from "./flags"; import { injectIndirect } from "./indirect"; -import { fs, garbageCollection } from "./platform"; +import { fs, garbageCollection, ready } from "./platform"; import type { Unmanaged } from "./Unmanaged"; import { type AnyRealmObject, RealmObject } from "./Object"; import { type AnyResults, Results } from "./Results"; @@ -104,6 +104,11 @@ type InternalConfig = { * The Realm database. */ export class Realm { + /** + * Await this promise before further interactions with the SDK when on a platform which loads the binding asynchronously. + */ + public static ready = ready; + public static defaultPath = Realm.normalizePath("default.realm"); private static internals = new Set>(); @@ -1384,6 +1389,8 @@ export namespace Realm { export import PushClient = ns.PushClient; } -// Set default logger and log level. -Realm.setLogger(defaultLogger); -Realm.setLogLevel(defaultLoggerLevel); +// Set default logger and log level when the binding to be ready +ready.then(() => { + Realm.setLogger(defaultLogger); + Realm.setLogLevel(defaultLoggerLevel); +}); diff --git a/packages/realm/src/platform.ts b/packages/realm/src/platform.ts index ed6de8360c..2a2e40fce3 100644 --- a/packages/realm/src/platform.ts +++ b/packages/realm/src/platform.ts @@ -17,7 +17,7 @@ //////////////////////////////////////////////////////////////////////////// /** @internal */ -export { binding } from "./platform/binding"; +export { binding, ready } from "./platform/binding"; /** @internal */ export { deviceInfo } from "./platform/device-info"; /** @internal */ diff --git a/packages/realm/src/platform/binding.ts b/packages/realm/src/platform/binding.ts index 9aabe68aaf..308880a911 100644 --- a/packages/realm/src/platform/binding.ts +++ b/packages/realm/src/platform/binding.ts @@ -17,8 +17,4 @@ //////////////////////////////////////////////////////////////////////////// /** @internal */ -export { binding, injectNativeModule } from "../binding/wrapper.generated"; -/** @internal */ -export { NativeBigInt } from "../binding/NativeBigInt"; -/** @internal */ -export { PolyfilledBigInt } from "../binding/PolyfilledBigInt"; +export { binding, injectNativeModule, ready } from "../binding/wrapper.generated"; diff --git a/packages/realm/src/platform/browser/binding.ts b/packages/realm/src/platform/browser/binding.ts new file mode 100644 index 0000000000..67d206b41d --- /dev/null +++ b/packages/realm/src/platform/browser/binding.ts @@ -0,0 +1,22 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import { injectAndPatch } from "../binding"; +const bindingPromise = import("../../../binding/generated/native.wasm.cjs"); + +bindingPromise.then(injectAndPatch); diff --git a/packages/realm/src/platform/browser/device-info.ts b/packages/realm/src/platform/browser/device-info.ts new file mode 100644 index 0000000000..89b4cdbe46 --- /dev/null +++ b/packages/realm/src/platform/browser/device-info.ts @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import { config, version } from "realm/package.json"; + +import { inject } from "../device-info"; + +const userAgent = window.navigator.userAgent; + +inject({ + create() { + return { + sdk: "JS", + sdkVersion: version, + + platform: userAgent, + platformVersion: userAgent, + + deviceName: "unknown", + deviceVersion: "unknown", + + cpuArch: "unknown", + + frameworkName: "WebAssembly", + frameworkVersion: "unknown", + + bundleId: config?.anonymizedBundleId || "unknown", + }; + }, +}); diff --git a/packages/realm/src/platform/browser/fs.ts b/packages/realm/src/platform/browser/fs.ts new file mode 100644 index 0000000000..8c2c0f27bf --- /dev/null +++ b/packages/realm/src/platform/browser/fs.ts @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import { isAbsolute, join } from "path-browserify"; + +import { inject } from "../file-system"; + +inject({ + isAbsolutePath(path) { + return isAbsolute(path); + }, + joinPaths(...segments) { + return join(...segments); + }, + removeFile(path) { + throw new Error("Not yet implemented"); + }, + removeDirectory(path) { + throw new Error("Not yet implemented"); + }, + ensureDirectoryForFile(path) { + throw new Error("Not yet implemented"); + }, + setDefaultDirectoryPath(path) { + throw new Error("Not yet implemented"); + }, + getDefaultDirectoryPath() { + // TODO: Implement this + return "/realm"; + }, + exists(path) { + throw new Error("Not yet implemented"); + }, + copyBundledRealmFiles() { + throw new Error("Not yet implemented"); + }, + removeRealmFilesFromDirectory(path) { + throw new Error("Not yet implemented"); + }, +}); diff --git a/packages/realm/src/platform/browser/index.ts b/packages/realm/src/platform/browser/index.ts new file mode 100644 index 0000000000..c9b91a9ee3 --- /dev/null +++ b/packages/realm/src/platform/browser/index.ts @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import "./binding"; +import "./fs"; +import "./device-info"; +// import "./sync-proxy-config"; + +import { Realm } from "../../Realm"; +export = Realm; diff --git a/packages/realm/src/platform/node/binding.ts b/packages/realm/src/platform/node/binding.ts index c89d7d6f29..d93154a747 100644 --- a/packages/realm/src/platform/node/binding.ts +++ b/packages/realm/src/platform/node/binding.ts @@ -16,7 +16,8 @@ // //////////////////////////////////////////////////////////////////////////// -import { NativeBigInt, type binding, injectNativeModule } from "../binding"; +import { type binding, injectNativeModule } from "../binding"; +import { NativeBigInt } from "../../binding/NativeBigInt"; // eslint-disable-next-line @typescript-eslint/no-var-requires const nativeModule = require("#realm.node"); diff --git a/packages/realm/src/scripts/build/cli.ts b/packages/realm/src/scripts/build/cli.ts index 4812abdc0c..b6ac21316c 100644 --- a/packages/realm/src/scripts/build/cli.ts +++ b/packages/realm/src/scripts/build/cli.ts @@ -31,7 +31,8 @@ import { Option, program } from "@commander-js/extra-typings"; import * as apple from "./apple"; import * as android from "./android"; import * as xcode from "./xcode"; -import { REALM_CORE_PATH, SUPPORTED_CONFIGURATIONS } from "./common"; +import * as wasm from "./wasm"; +import { PACKAGE_PATH, REALM_CORE_PATH, SUPPORTED_CONFIGURATIONS, ensureDirectory } from "./common"; export { program }; @@ -57,9 +58,9 @@ function actionWrapper(action: (...args: Args) => Promis } catch (err) { process.exitCode = 1; if (err instanceof Error) { - console.error(`ERROR: ${err.stack}`); + console.error(err.stack); if (err.cause instanceof Error) { - console.error(`CAUSE: ${err.cause.message}`); + console.error(`(cause): ${err.cause.stack}`); } } else { throw err; @@ -176,6 +177,26 @@ program }), ); +program + .command("build-wasm") + .description("Build native code for WASM") + .option("--clean", "Delete any build directory first", false) + .addOption(configurationOption) + .action( + actionWrapper(({ configuration, clean }) => { + assert(fs.existsSync(REALM_CORE_PATH), `Expected Realm Core at '${REALM_CORE_PATH}'`); + const { CMAKE_PATH: cmakePath = execSync("which cmake", { encoding: "utf8" }).trim() } = env; + + wasm.check(); + const sourcePath = path.relative(PACKAGE_PATH, "binding/wasm"); + const buildPath = path.relative(PACKAGE_PATH, "binding/wasm/build"); + ensureDirectory(buildPath, clean); + wasm.configure({ cmakePath, sourcePath, buildPath, configuration }); + wasm.build({ cmakePath, buildPath }); + + console.log("Great success! 🥳"); + }), + ); if (require.main === module) { program.parse(); } diff --git a/packages/realm/src/scripts/build/wasm.ts b/packages/realm/src/scripts/build/wasm.ts new file mode 100644 index 0000000000..5aef2be5cd --- /dev/null +++ b/packages/realm/src/scripts/build/wasm.ts @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm 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. +// +//////////////////////////////////////////////////////////////////////////// + +import assert from "node:assert"; +import { execSync, spawnSync } from "node:child_process"; +import { Configuration, REALM_CORE_VERSION } from "./common"; + +export function check() { + try { + execSync("which emcc", { encoding: "utf8" }).trim(); + execSync("emcc --check"); + } catch (err) { + throw new Error( + "Failed to locate the Emscripten compiler frontend - did you follow the steps for building WASM in contrib/building.md?", + { cause: err }, + ); + } +} + +type ConfigureOptions = { + cmakePath: string; + sourcePath: string; + buildPath: string; + configuration: Configuration; +}; + +export function configure({ cmakePath, sourcePath, buildPath, configuration }: ConfigureOptions) { + const { status } = spawnSync( + "emcmake", + [ + cmakePath, + "-G", + "Ninja", + "-S", + sourcePath, + "-B", + buildPath, + "-D", + `CMAKE_BUILD_TYPE=${configuration}`, + "-D", + "CMAKE_MAKE_PROGRAM=ninja", + // "-D", + // "CMAKE_C_COMPILER_LAUNCHER=ccache", + // "-D", + // "CMAKE_CXX_COMPILER_LAUNCHER=ccache", + // Realm specific variables below + "-D", + `REALM_VERSION=${REALM_CORE_VERSION}`, + ], + { stdio: "inherit" }, + ); + assert.equal(status, 0, `Expected a clean exit (got status = ${status})`); +} + +type BuildOptions = { + cmakePath: string; + buildPath: string; +}; + +export function build({ cmakePath, buildPath }: BuildOptions) { + const { status } = spawnSync(cmakePath, ["--build", buildPath], { stdio: "inherit" }); + assert.equal(status, 0, `Expected a clean exit (got status = ${status})`); +} diff --git a/packages/realm/tsconfig.browser.json b/packages/realm/tsconfig.browser.json new file mode 100644 index 0000000000..c1feeaf292 --- /dev/null +++ b/packages/realm/tsconfig.browser.json @@ -0,0 +1,20 @@ +{ + "extends": "@tsconfig/recommended", + "compilerOptions": { + "resolveJsonModule": true, + "composite": true, + "outDir": "dist/platform/browser", + "rootDir": "src/platform/browser", + "sourceMap": true, + "noEmit": false, + "allowImportingTsExtensions": false, + "types": ["@realm/fetch"], + "lib": ["ES2020", "DOM"] + }, + "include": [ + "src/platform/browser/**/*" + ], + "references": [ + { "path": "./tsconfig.shared.json" } + ] + } \ No newline at end of file diff --git a/packages/realm/tsconfig.json b/packages/realm/tsconfig.json index 6389d4e475..1d4201c465 100644 --- a/packages/realm/tsconfig.json +++ b/packages/realm/tsconfig.json @@ -4,6 +4,7 @@ { "path": "./tsconfig.shared.json" }, { "path": "./tsconfig.node.json" }, { "path": "./tsconfig.react-native.json" }, + { "path": "./tsconfig.browser.json" }, { "path": "./tsconfig.tests.json" }, { "path": "./tsconfig.scripts.json" }, { "path": "./tsconfig.public-types.json" } diff --git a/packages/realm/tsconfig.shared.json b/packages/realm/tsconfig.shared.json index a313e4ed82..63b83896c1 100644 --- a/packages/realm/tsconfig.shared.json +++ b/packages/realm/tsconfig.shared.json @@ -28,6 +28,7 @@ "src/tests", "src/scripts", "src/platform/node", - "src/platform/react-native" + "src/platform/react-native", + "src/platform/browser" ] } \ No newline at end of file