diff --git a/CHANGELOG.md b/CHANGELOG.md index 5260b91d..55d72719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - **connected-solid:** Read links from Link header of a Solid resource. (#165) +- **vue:** Add [Vue](https://vuejs.org) support with `@ldo/vue` package. ([#171](https://github.com/o-development/ldo/pull/171)) ### Changed diff --git a/dev/solid-vue-vite-app/.gitignore b/dev/solid-vue-vite-app/.gitignore new file mode 100644 index 00000000..f22509c8 --- /dev/null +++ b/dev/solid-vue-vite-app/.gitignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +# Editor directories and files +.vscode/* +#!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ + +# Vite +*.timestamp-*-*.mjs diff --git a/dev/solid-vue-vite-app/README.md b/dev/solid-vue-vite-app/README.md new file mode 100644 index 00000000..493f8a6f --- /dev/null +++ b/dev/solid-vue-vite-app/README.md @@ -0,0 +1,42 @@ +# solid-vue-vite-app + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Recommended Browser Setup + +- Chromium-based browsers (Chrome, Edge, Brave, etc.): + - [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) + - [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters) +- Firefox: + - [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/) + - [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/) + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vite.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +npm run build +``` diff --git a/dev/solid-vue-vite-app/env.d.ts b/dev/solid-vue-vite-app/env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/dev/solid-vue-vite-app/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/dev/solid-vue-vite-app/index.html b/dev/solid-vue-vite-app/index.html new file mode 100644 index 00000000..9d30802c --- /dev/null +++ b/dev/solid-vue-vite-app/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/dev/solid-vue-vite-app/package.json b/dev/solid-vue-vite-app/package.json new file mode 100644 index 00000000..61d255fe --- /dev/null +++ b/dev/solid-vue-vite-app/package.json @@ -0,0 +1,37 @@ +{ + "name": "solid-vue-vite-app", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build", + "build:ldo": "ldo build --input src/_shapes --output src/_ldo" + }, + "dependencies": { + "@ldo/connected-solid": "^1.0.0-alpha.50", + "@ldo/ldo": "^1.0.0-alpha.50", + "@ldo/vue": "^1.0.0-alpha.50", + "vue": "^3.5.40" + }, + "devDependencies": { + "@ldo/cli": "^1.0.0-alpha.50", + "@tsconfig/node24": "^24.0.4", + "@types/jsonld": "^1.5.15", + "@types/node": "^24.13.3", + "@types/shexj": "^2.1.7", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "npm-run-all2": "^9.0.2", + "typescript": "~6.0.0", + "vite": "^8.1.5", + "vite-plugin-vue-devtools": "^8.1.5", + "vue-tsc": "^3.3.7" + }, + "engines": { + "node": "^22.18.0 || >=24.12.0" + } +} diff --git a/dev/solid-vue-vite-app/public/favicon.ico b/dev/solid-vue-vite-app/public/favicon.ico new file mode 100644 index 00000000..df36fcfb Binary files /dev/null and b/dev/solid-vue-vite-app/public/favicon.ico differ diff --git a/dev/solid-vue-vite-app/src/App.vue b/dev/solid-vue-vite-app/src/App.vue new file mode 100644 index 00000000..6a95f755 --- /dev/null +++ b/dev/solid-vue-vite-app/src/App.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/dev/solid-vue-vite-app/src/_ldo/foafProfile.context.ts b/dev/solid-vue-vite-app/src/_ldo/foafProfile.context.ts new file mode 100644 index 00000000..e7a57dfc --- /dev/null +++ b/dev/solid-vue-vite-app/src/_ldo/foafProfile.context.ts @@ -0,0 +1,48 @@ +import type { LdoJsonldContext } from "@ldo/ldo"; + +/** + * ============================================================================= + * foafProfileContext: JSONLD Context for foafProfile + * ============================================================================= + */ +export const foafProfileContext: LdoJsonldContext = { + type: { + "@id": "@type", + "@isCollection": true, + }, + Person: { + "@id": "http://xmlns.com/foaf/0.1/Person", + "@context": { + type: { + "@id": "@type", + "@isCollection": true, + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, + }, + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, +}; diff --git a/dev/solid-vue-vite-app/src/_ldo/foafProfile.schema.ts b/dev/solid-vue-vite-app/src/_ldo/foafProfile.schema.ts new file mode 100644 index 00000000..25243cde --- /dev/null +++ b/dev/solid-vue-vite-app/src/_ldo/foafProfile.schema.ts @@ -0,0 +1,97 @@ +import type { Schema } from "shexj"; + +/** + * ============================================================================= + * foafProfileSchema: ShexJ Schema for foafProfile + * ============================================================================= + */ +export const foafProfileSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://example.com/FoafProfile", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: ["http://xmlns.com/foaf/0.1/Person"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a Person (from foaf)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/name", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Define a person's name.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/img", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Photo link but in string form", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/knows", + valueExpr: "https://example.com/FoafProfile", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A list of WebIds for all the people this user knows.", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + ], +}; diff --git a/dev/solid-vue-vite-app/src/_ldo/foafProfile.shapeTypes.ts b/dev/solid-vue-vite-app/src/_ldo/foafProfile.shapeTypes.ts new file mode 100644 index 00000000..2c52ee86 --- /dev/null +++ b/dev/solid-vue-vite-app/src/_ldo/foafProfile.shapeTypes.ts @@ -0,0 +1,19 @@ +import type { ShapeType } from "@ldo/ldo"; +import { foafProfileSchema } from "./foafProfile.schema"; +import { foafProfileContext } from "./foafProfile.context"; +import type { FoafProfile } from "./foafProfile.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes foafProfile + * ============================================================================= + */ + +/** + * FoafProfile ShapeType + */ +export const FoafProfileShapeType: ShapeType = { + schema: foafProfileSchema, + shape: "https://example.com/FoafProfile", + context: foafProfileContext, +}; diff --git a/dev/solid-vue-vite-app/src/_ldo/foafProfile.typings.ts b/dev/solid-vue-vite-app/src/_ldo/foafProfile.typings.ts new file mode 100644 index 00000000..16843881 --- /dev/null +++ b/dev/solid-vue-vite-app/src/_ldo/foafProfile.typings.ts @@ -0,0 +1,33 @@ +import type { LdoJsonldContext, LdSet } from "@ldo/ldo"; + +/** + * ============================================================================= + * Typescript Typings for foafProfile + * ============================================================================= + */ + +/** + * FoafProfile Type + */ +export interface FoafProfile { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * Defines the node as a Person (from foaf) + */ + type: LdSet<{ + "@id": "Person"; + }>; + /** + * Define a person's name. + */ + name?: string; + /** + * Photo link but in string form + */ + img?: string; + /** + * A list of WebIds for all the people this user knows. + */ + knows?: LdSet; +} diff --git a/dev/solid-vue-vite-app/src/_shapes/foafProfile.shex b/dev/solid-vue-vite-app/src/_shapes/foafProfile.shex new file mode 100644 index 00000000..dff09d16 --- /dev/null +++ b/dev/solid-vue-vite-app/src/_shapes/foafProfile.shex @@ -0,0 +1,19 @@ +# This shape is provided by default as an example +# You can create your own shape to fit your needs using ShEx (https://shex.io) +# Also check out https://shaperepo.com for examples of more shapes. + +PREFIX ex: +PREFIX foaf: +PREFIX rdfs: +PREFIX xsd: + +ex:FoafProfile EXTRA a { + a [ foaf:Person ] + // rdfs:comment "Defines the node as a Person (from foaf)" ; + foaf:name xsd:string ? + // rdfs:comment "Define a person's name." ; + foaf:img xsd:string ? + // rdfs:comment "Photo link but in string form" ; + foaf:knows @ex:FoafProfile * + // rdfs:comment "A list of WebIds for all the people this user knows." ; +} diff --git a/dev/solid-vue-vite-app/src/ldo.ts b/dev/solid-vue-vite-app/src/ldo.ts new file mode 100644 index 00000000..54680072 --- /dev/null +++ b/dev/solid-vue-vite-app/src/ldo.ts @@ -0,0 +1,5 @@ +import { createLdoVueMethods } from "@ldo/vue"; +import { solidConnectedPlugin } from "@ldo/connected-solid"; + +export const { dataset, useSubject, useResource, useMatchSubject } = + createLdoVueMethods([solidConnectedPlugin]); diff --git a/dev/solid-vue-vite-app/src/main.ts b/dev/solid-vue-vite-app/src/main.ts new file mode 100644 index 00000000..b670de8b --- /dev/null +++ b/dev/solid-vue-vite-app/src/main.ts @@ -0,0 +1,4 @@ +import { createApp } from "vue"; +import App from "./App.vue"; + +createApp(App).mount("#app"); diff --git a/dev/solid-vue-vite-app/src/useMatchSubjectTest.vue b/dev/solid-vue-vite-app/src/useMatchSubjectTest.vue new file mode 100644 index 00000000..a77ee401 --- /dev/null +++ b/dev/solid-vue-vite-app/src/useMatchSubjectTest.vue @@ -0,0 +1,61 @@ + + + diff --git a/dev/solid-vue-vite-app/src/vueReadmeTest.vue b/dev/solid-vue-vite-app/src/vueReadmeTest.vue new file mode 100644 index 00000000..4a01df39 --- /dev/null +++ b/dev/solid-vue-vite-app/src/vueReadmeTest.vue @@ -0,0 +1,37 @@ + + + diff --git a/dev/solid-vue-vite-app/tsconfig.app.json b/dev/solid-vue-vite-app/tsconfig.app.json new file mode 100644 index 00000000..c0f2d867 --- /dev/null +++ b/dev/solid-vue-vite-app/tsconfig.app.json @@ -0,0 +1,18 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + // Extra safety for array and object lookups, but may have false positives. + "noUncheckedIndexedAccess": true, + + // Path mapping for cleaner imports. + "paths": { + "@/*": ["./src/*"] + }, + + // `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking. + // Specified here to keep it out of the root directory. + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo" + } +} diff --git a/dev/solid-vue-vite-app/tsconfig.json b/dev/solid-vue-vite-app/tsconfig.json new file mode 100644 index 00000000..66b5e570 --- /dev/null +++ b/dev/solid-vue-vite-app/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/dev/solid-vue-vite-app/tsconfig.node.json b/dev/solid-vue-vite-app/tsconfig.node.json new file mode 100644 index 00000000..c9b2badc --- /dev/null +++ b/dev/solid-vue-vite-app/tsconfig.node.json @@ -0,0 +1,27 @@ +// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping. +{ + "extends": "@tsconfig/node24/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "playwright.config.*", + "eslint.config.*" + ], + "compilerOptions": { + // Most tools use transpilation instead of Node.js's native type-stripping. + // Bundler mode provides a smoother developer experience. + "module": "preserve", + "moduleResolution": "bundler", + + // Include Node.js types and avoid accidentally including other `@types/*` packages. + "types": ["node"], + + // Disable emitting output during `vue-tsc --build`, which is used for type-checking only. + "noEmit": true, + + // `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking. + // Specified here to keep it out of the root directory. + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo" + } +} diff --git a/dev/solid-vue-vite-app/vite.config.ts b/dev/solid-vue-vite-app/vite.config.ts new file mode 100644 index 00000000..74831822 --- /dev/null +++ b/dev/solid-vue-vite-app/vite.config.ts @@ -0,0 +1,15 @@ +import { fileURLToPath, URL } from "node:url"; + +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import vueDevTools from "vite-plugin-vue-devtools"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue(), vueDevTools()], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, +}); diff --git a/package-lock.json b/package-lock.json index 379b43a4..ae1ebce1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -448,6 +448,159 @@ "url": "https://github.com/sponsors/Boshen" } }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "dev/solid-react-vite-app/node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.2", "cpu": [ @@ -478,6 +631,57 @@ "node": "^20.19.0 || >=22.12.0" } }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-react-vite-app/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "dev/solid-react-vite-app/node_modules/@rolldown/plugin-babel": { "version": "0.2.3", "dev": true, @@ -888,6 +1092,329 @@ } } }, + "dev/solid-vue-app": { + "version": "0.0.0", + "extraneous": true, + "dependencies": { + "vue": "^3.5.40" + }, + "devDependencies": { + "@tsconfig/node24": "^24.0.4", + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "npm-run-all2": "^9.0.2", + "typescript": "~6.0.0", + "vite": "^8.1.5", + "vite-plugin-vue-devtools": "^8.1.5", + "vue-tsc": "^3.3.7" + }, + "engines": { + "node": "^22.18.0 || >=24.12.0" + } + }, + "dev/solid-vue-vite-app": { + "version": "0.0.0", + "dependencies": { + "@ldo/connected-solid": "^1.0.0-alpha.50", + "@ldo/ldo": "^1.0.0-alpha.50", + "@ldo/vue": "^1.0.0-alpha.50", + "vue": "^3.5.40" + }, + "devDependencies": { + "@ldo/cli": "^1.0.0-alpha.50", + "@tsconfig/node24": "^24.0.4", + "@types/jsonld": "^1.5.15", + "@types/node": "^24.13.3", + "@types/shexj": "^2.1.7", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "npm-run-all2": "^9.0.2", + "typescript": "~6.0.0", + "vite": "^8.1.5", + "vite-plugin-vue-devtools": "^8.1.5", + "vue-tsc": "^3.3.7" + }, + "engines": { + "node": "^22.18.0 || >=24.12.0" + } + }, + "dev/solid-vue-vite-app/node_modules/@oxc-project/types": { + "version": "0.148.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", + "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "dev/solid-vue-vite-app/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", + "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-vue-vite-app/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", + "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "dev/solid-vue-vite-app/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "dev/solid-vue-vite-app/node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "dev/solid-vue-vite-app/node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "dev/solid-vue-vite-app/node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "dev/solid-vue-vite-app/node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "dev/solid-vue-vite-app/node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "dev/solid-vue-vite-app/node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "dev/solid-vue-vite-app/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "dev/solid-vue-vite-app/node_modules/rolldown": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", + "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.148.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.7", + "@rolldown/binding-android-arm64": "1.2.7", + "@rolldown/binding-darwin-arm64": "1.2.7", + "@rolldown/binding-darwin-x64": "1.2.7", + "@rolldown/binding-freebsd-x64": "1.2.7", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", + "@rolldown/binding-linux-arm64-gnu": "1.2.7", + "@rolldown/binding-linux-arm64-musl": "1.2.7", + "@rolldown/binding-linux-ppc64-gnu": "1.2.7", + "@rolldown/binding-linux-s390x-gnu": "1.2.7", + "@rolldown/binding-linux-x64-gnu": "1.2.7", + "@rolldown/binding-linux-x64-musl": "1.2.7", + "@rolldown/binding-openharmony-arm64": "1.2.7", + "@rolldown/binding-win32-arm64-msvc": "1.2.7", + "@rolldown/binding-win32-x64-msvc": "1.2.7" + } + }, + "dev/solid-vue-vite-app/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "dev/solid-vue-vite-app/node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "dev/solid-vue-vite-app/node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "dev/solid-vue-vite-app/node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "dev": true, @@ -973,11 +1500,13 @@ "license": "ISC" }, "node_modules/@babel/code-frame": { - "version": "7.29.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -1031,12 +1560,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -1046,11 +1577,13 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1093,16 +1626,18 @@ "license": "ISC" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.3", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.29.0", + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { @@ -1160,7 +1695,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -1168,12 +1705,14 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1208,18 +1747,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -1243,13 +1786,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1259,28 +1804,32 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "devOptional": true, + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "devOptional": true, + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1320,11 +1869,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "dev": true, + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -1422,6 +1972,24 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.7.tgz", + "integrity": "sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-decorators": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "dev": true, @@ -1433,6 +2001,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.29.7.tgz", + "integrity": "sha512-9MTTLbF39X6sqM92JPEsoI7++26hjZvzkxKZy64aMhWLH2mPkJ/Q3AV4QLmls3R14FpSpkOwQQfUh962JGQxxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.28.6", "dev": true, @@ -1461,6 +2045,51 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-unicode-sets-regex": { "version": "7.18.6", "dev": true, @@ -2199,6 +2828,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", "dev": true, @@ -2372,29 +3021,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -2402,12 +3055,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "devOptional": true, + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -16519,6 +17173,40 @@ "node": ">=10.0.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.27.7", "cpu": [ @@ -17180,7 +17868,6 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -17679,6 +18366,10 @@ "resolved": "packages/type-traverser", "link": true }, + "node_modules/@ldo/vue": { + "resolved": "packages/vue", + "link": true + }, "node_modules/@lerna/child-process": { "version": "7.4.2", "dev": true, @@ -18174,6 +18865,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, "node_modules/@next/env": { "version": "16.2.6", "license": "MIT" @@ -18212,6 +18925,66 @@ "node": ">= 6" } }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz", + "integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz", + "integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz", + "integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz", + "integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@next/swc-linux-x64-gnu": { "version": "16.2.6", "cpu": [ @@ -18240,6 +19013,36 @@ "node": ">= 10" } }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz", + "integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz", + "integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "license": "MIT", @@ -18749,6 +19552,13 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, "node_modules/@publint/pack": { "version": "0.1.4", "dev": true, @@ -18980,6 +19790,176 @@ "@types/node": "*" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", + "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", + "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", + "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", + "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", + "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", + "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", + "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", + "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", + "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", + "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.0-beta.9-commit.d91dfb5", "cpu": [ @@ -19006,6 +19986,76 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", + "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", + "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", + "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "dev": true, @@ -21242,6 +22292,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@tsconfig/node24": { + "version": "24.0.5", + "resolved": "https://registry.npmjs.org/@tsconfig/node24/-/node24-24.0.5.tgz", + "integrity": "sha512-DWC54fGPuhZgg3Ighg4Y/A/bA3ZdB5hnzAuv/MkQ/X85sVsBsMQxu8t7khutdYeiNksBbFdDyeqv1HSW2Zl8aA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/svelte": { "version": "5.0.8", "dev": true, @@ -21267,6 +22324,17 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/accepts": { "version": "1.3.7", "license": "MIT", @@ -21506,6 +22574,8 @@ }, "node_modules/@types/jsonld": { "version": "1.5.15", + "resolved": "https://registry.npmjs.org/@types/jsonld/-/jsonld-1.5.15.tgz", + "integrity": "sha512-PlAFPZjL+AuGYmwlqwKEL0IMP8M8RexH0NIPGfCVWSQ041H2rR/8OlyZSD7KsCVoN8vCfWdtWDBxX8yBVP+xow==", "license": "MIT" }, "node_modules/@types/keygrip": { @@ -21808,6 +22878,8 @@ }, "node_modules/@types/shexj": { "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/shexj/-/shexj-2.1.7.tgz", + "integrity": "sha512-pu/0vIZxFTMPVjTlo5MJKFkBL/EbAuFhtCXpmBB7ZdUiyNpc6pt8GxfyRPqdf6q2SsWu71a/vbhvGK2IZN2Eug==", "license": "MIT" }, "node_modules/@types/spark-md5": { @@ -21837,6 +22909,15 @@ "version": "10.0.0", "license": "MIT" }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@types/ws": { "version": "8.18.1", "license": "MIT", @@ -22138,6 +23219,30 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue/node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, "node_modules/@vitest/coverage-istanbul": { "version": "3.2.4", "dev": true, @@ -22287,13 +23392,67 @@ "path-browserify": "^1.0.1" } }, - "node_modules/@vue/compiler-core": { - "version": "3.5.33", + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.2", - "@vue/shared": "3.5.33", + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -22301,7 +23460,8 @@ }, "node_modules/@vue/compiler-core/node_modules/entities": { "version": "7.0.1", - "dev": true, + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -22312,18 +23472,104 @@ }, "node_modules/@vue/compiler-core/node_modules/estree-walker": { "version": "2.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.33", + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.28", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/devtools-core": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.2.1.tgz", + "integrity": "sha512-s/VfAY9oDTb/kFEWmy461jaFde2MIV1RO/gi1vwM+PAZBZ/Pc2Ndu3BNBdZUze8QDUuyYvElbEEGA83syjJfzA==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.33", - "@vue/shared": "3.5.33" + "@vue/devtools-kit": "^8.2.1", + "@vue/devtools-shared": "^8.2.1" + }, + "peerDependencies": { + "vue": "^3.0.0" } }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-kit/node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vue/devtools-kit/node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "dev": true, + "license": "MIT" + }, "node_modules/@vue/language-core": { "version": "1.8.27", "dev": true, @@ -22348,11 +23594,73 @@ } } }, + "node_modules/@vue/reactivity": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, "node_modules/@vue/shared": { - "version": "3.5.33", - "dev": true, + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", "license": "MIT" }, + "node_modules/@vue/tsconfig": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz", + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 5.8", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "dev": true, @@ -22546,6 +23854,13 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-colors": { "version": "4.1.3", "dev": true, @@ -23244,6 +24559,21 @@ "version": "1.1.2", "license": "MIT" }, + "node_modules/buffer-image-size": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz", + "integrity": "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/builtins": { "version": "5.1.0", "dev": true, @@ -23252,6 +24582,22 @@ "semver": "^7.0.0" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/byte-size": { "version": "8.1.1", "dev": true, @@ -24472,7 +25818,6 @@ }, "node_modules/csstype": { "version": "3.2.3", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -24675,6 +26020,36 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defaults": { "version": "1.0.4", "dev": true, @@ -25108,6 +26483,16 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/es-abstract": { "version": "1.24.2", "dev": true, @@ -26402,6 +27787,20 @@ "version": "1.0.0", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "license": "MIT", @@ -26930,6 +28329,54 @@ "uglify-js": "^3.1.4" } }, + "node_modules/happy-dom": { + "version": "20.14.3", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.14.3.tgz", + "integrity": "sha512-0KMb/Eh8rsd+aMNkOk5h8LkAjo9Na1aksnEGmsuZf+GXl6UkAdIi0VbUHU7d59lXhNmawODgwwuCMiZpCxwwCw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "buffer-image-size": "^0.6.4", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.21.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/happy-dom/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/happy-dom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "dev": true, @@ -27826,6 +29273,54 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "dev": true, @@ -29300,7 +30795,9 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -29314,21 +30811,177 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], @@ -29348,7 +31001,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], @@ -29367,6 +31022,50 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lines-and-columns": { "version": "2.0.4", "dev": true, @@ -29535,7 +31234,6 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -29753,6 +31451,15 @@ "tslib": "2" } }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "8.1.2", "dev": true, @@ -30159,6 +31866,16 @@ "node": ">=4" } }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" @@ -30241,7 +31958,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.12", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -31115,6 +32834,132 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/npm-run-all2": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-9.0.3.tgz", + "integrity": "sha512-BQAEdU1PtYc48qYRdghW2BVTQT3VqWCoFQmO87NlM1h1PYwMCKQpUWaNyB20V26caNzFsXQDfyOdznLlHCih6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^7.0.0", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^1.0.0", + "read-package-json-fast": "^6.0.0", + "shell-quote": "^1.8.4", + "which": "^7.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-7.0.0.tgz", + "integrity": "sha512-kKvt3m4uwzqL0wlkPd09CmljPJGOZZ4D0fP65sqFSvPkMRKhNi+74MgIJ5QxE6SxqB4t4KyUFGg8+n5zjo6hew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-all2/node_modules/json-parse-even-better-errors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-6.0.0.tgz", + "integrity": "sha512-2/8adwnK1/+Fdjyts4r6wSpfANWw8zdNhU9U/Llk59c6O+DjSisPWPykwoL8gZmocP9Dy64S7oie2g+Mia123A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/npm-run-all2/node_modules/npm-normalize-package-bin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-6.0.0.tgz", + "integrity": "sha512-tdt4aFn9QamlhdN3HV2D2ccpBwO5/fyjjbXUxYA6uBjyekMZcZvDq0aSj9t5Jo+tih6AYFnt/cuIRn9013e0Uw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/npm-run-all2/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/npm-run-all2/node_modules/read-package-json-fast": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-6.0.0.tgz", + "integrity": "sha512-PNaGjoCnw9DBA2Kl8D+8po957z778q/HOPuY2u3Bkw/JO3eC8MDx7jn/PgMtSgpcBbs+6UOjDbwReGpXmRvs0g==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^6.0.0", + "npm-normalize-package-bin": "^6.0.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/npm-run-all2/node_modules/shell-quote": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-7.0.0.tgz", + "integrity": "sha512-RancgH2dmbLdHl6LRhEqvklWMgl/Hdnun0Y90KhBOLkMefg8Qa7/Zel8Sm+8HEcP6DEjzsWzpkuBQEZok58isA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "dev": true, @@ -31428,6 +33273,13 @@ "node": ">=12.20.0" } }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "dev": true, + "license": "MIT" + }, "node_modules/oidc-provider": { "version": "9.8.3", "license": "MIT", @@ -32151,6 +34003,13 @@ "through": "~2.3" } }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "license": "ISC" @@ -32166,6 +34025,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidtree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-1.0.0.tgz", + "integrity": "sha512-avfAvjB9Dd0wdj3rjJX//yS+G79OO0KrS5pJHFJENjYGX6N4SMgEDBBI/yFy0lloOYSaC6XQxzpOAMPfSYFV/Q==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/pify": { "version": "5.0.0", "dev": true, @@ -32245,8 +34117,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "dev": true, + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "funding": [ { "type": "opencollective", @@ -32263,7 +34136,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -32271,6 +34144,19 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "license": "MIT", @@ -34049,6 +35935,159 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/rolldown/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/rolldown/node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.0-rc.17", "cpu": [ @@ -34064,6 +36103,76 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/rolldown/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/rolldown/node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.17", "dev": true, @@ -34117,6 +36226,19 @@ "dev": true, "license": "MIT" }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-async": { "version": "2.4.1", "dev": true, @@ -34852,6 +36974,21 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "license": "MIT" @@ -34929,6 +37066,10 @@ "resolved": "dev/solid-react-vite-app", "link": true }, + "node_modules/solid-vue-vite-app": { + "resolved": "dev/solid-vue-vite-app", + "link": true + }, "node_modules/sorcery": { "version": "0.11.1", "dev": true, @@ -36280,6 +38421,16 @@ "version": "2.0.2", "license": "MIT" }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "dev": true, @@ -36981,7 +39132,7 @@ }, "node_modules/typescript": { "version": "5.9.3", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -37377,6 +39528,36 @@ "node": ">= 0.8" } }, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-utils/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/unrs-resolver": { "version": "1.12.2", "dev": true, @@ -37632,6 +39813,36 @@ } } }, + "node_modules/vite-dev-rpc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-2.0.0.tgz", + "integrity": "sha512-yKwbTwdHKSD2k/aGqyWpPHepo45OQc8lH3/6IfT4ZqeKE26ooKvi4WIEKzqWav8v+9Is8u1k8q54hvOmqASazA==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^4.0.0", + "vite-hot-client": "^2.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 || ^8.0.0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.2.0.tgz", + "integrity": "sha512-76Zs9zrHbH7M7wqeyooGQKdX+yg0pQ0xuQ1PbFp4z5a0Lzn2e5IPFoCswnmqZ4GiwqB4Jo3WcDAMO9jARTJl8w==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0" + } + }, "node_modules/vite-node": { "version": "3.2.4", "dev": true, @@ -37687,6 +39898,114 @@ } } }, + "node_modules/vite-plugin-inspect": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.4.1.tgz", + "integrity": "sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.3.0", + "error-stack-parser-es": "^1.0.5", + "obug": "^2.1.1", + "ohash": "^2.0.11", + "open": "^11.0.0", + "perfect-debounce": "^2.1.0", + "sirv": "^3.0.2", + "unplugin-utils": "^0.3.1", + "vite-dev-rpc": "^2.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0 || ^8.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect/node_modules/open": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.2.tgz", + "integrity": "sha512-RWqF+pBSkqecEvCKOn8QYhaNdRMJDZRIrlS/7rTDdLHaPcfXGCZ/h8zb413NfvdeAV0MR7T1yJcA34/q+CSm1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-vue-devtools": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.2.1.tgz", + "integrity": "sha512-5JLxXWWCo5lJMw16/xVeNvJ8k2zLwZPf1vITLzya/2IePrCBeGe/p/iAokgXHZpEi39fcYtPXmO8SaKeXmqCAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-core": "^8.2.1", + "@vue/devtools-kit": "^8.2.1", + "@vue/devtools-shared": "^8.2.1", + "sirv": "^3.0.2", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-inspector": "^6.0.0" + }, + "engines": { + "node": ">=v14.21.3" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/vite-plugin-vue-inspector": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-6.0.0.tgz", + "integrity": "sha512-OpyITJLgZNibxlrik1EmRtvXHDjLRxNPsWkGFTERZs2LgMEdG4W0WoFt5GIgp3a3jRou+eJR8U1zOBk/XQgEbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/plugin-proposal-decorators": "^7.23.0", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.22.15", + "@vue/babel-plugin-jsx": "^1.1.5", + "@vue/compiler-dom": "^3.3.4", + "kolorist": "^1.8.0", + "magic-string": "^0.30.4" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/vite/node_modules/@esbuild/linux-x64": { "version": "0.25.12", "cpu": [ @@ -37877,6 +40196,34 @@ "dev": true, "license": "MIT" }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/vue-template-compiler": { "version": "2.7.16", "dev": true, @@ -38326,7 +40673,9 @@ } }, "node_modules/ws": { - "version": "8.20.0", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -38344,6 +40693,52 @@ } } }, + "node_modules/wsl-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz", + "integrity": "sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xlsx": { "version": "0.8.8", "dev": true, @@ -39813,6 +42208,26 @@ "uuid": "dist/bin/uuid" } }, + "packages/vue": { + "name": "@ldo/vue", + "version": "1.0.0-alpha.50", + "license": "MIT", + "dependencies": { + "@ldo/connected": "^1.0.0-alpha.50", + "@ldo/jsonld-dataset-proxy": "^1.0.0-alpha.50", + "@ldo/ldo": "^1.0.0-alpha.50", + "@ldo/rdf-utils": "^1.0.0-alpha.50" + }, + "devDependencies": { + "@ldo/connected-solid": "^1.0.0-alpha.50", + "@ldo/test-solid-server": "^1.0.0-alpha.50", + "@types/shexj": "^2.1.7", + "vue": "^3.5.43" + }, + "peerDependencies": { + "vue": "^3.3.0" + } + }, "packages/wac": { "extraneous": true } diff --git a/packages/connected-solid/src/notifications/Websocket2023NotificationSubscription.ts b/packages/connected-solid/src/notifications/Websocket2023NotificationSubscription.ts index 22710130..b7ffc5fa 100644 --- a/packages/connected-solid/src/notifications/Websocket2023NotificationSubscription.ts +++ b/packages/connected-solid/src/notifications/Websocket2023NotificationSubscription.ts @@ -22,7 +22,7 @@ const CHANNEL_TYPE = /** * @internal - * An implementation of the Websocket2023 notifiction spec + * An implementation of the Websocket2023 notification spec */ export class Websocket2023NotificationSubscription extends NotificationSubscription< SolidConnectedPlugin, @@ -106,7 +106,10 @@ export class Websocket2023NotificationSubscription extends NotificationSubscript this.socket.onerror = (err) => { this.onNotificationError( - new UnexpectedResourceError(this.resource, err.error), + new UnexpectedResourceError( + this.resource, + err.error ?? new Error("WebSocket error"), + ), ); }; return; diff --git a/packages/connected/src/linkTraversal/ResourceLinkQuery.ts b/packages/connected/src/linkTraversal/ResourceLinkQuery.ts index 5567b610..bbdf96b6 100644 --- a/packages/connected/src/linkTraversal/ResourceLinkQuery.ts +++ b/packages/connected/src/linkTraversal/ResourceLinkQuery.ts @@ -15,7 +15,7 @@ import type { nodeEventListener } from "@ldo/subscribable-dataset"; import type { Quad } from "@rdfjs/types"; /** - * Represents a query over multiple datasources and constituting muliple + * Represents a query over multiple datasources and constituting multiple * resources. * * @example @@ -36,7 +36,7 @@ import type { Quad } from "@rdfjs/types"; * }, * } * ); - * // Susbscribe to this link query, automaticically updating the dataset when + * // Subscribe to this link query, automatically updating the dataset when * // something from the link query is changed. * await linkQuery.subscribe(); * ``` @@ -102,7 +102,7 @@ export class ResourceLinkQuery< * }, * } * ); - * // Susbscribe to this link query, automaticically updating the dataset when + * // Subscribe to this link query, automatically updating the dataset when * // something from the link query is changed. * const result = await linkQuery.read(); * console.log(result.name); @@ -149,7 +149,7 @@ export class ResourceLinkQuery< * }, * } * ); - * // Susbscribe to this link query, automaticically updating the dataset when + * // Subscribe to this link query, automatically updating the dataset when * // something from the link query is changed. * const unsubscribeId = await linkQuery.subscribe(); * diff --git a/packages/connected/src/types/IConnectedLdoDataset.ts b/packages/connected/src/types/IConnectedLdoDataset.ts index b4e545c5..d904084a 100644 --- a/packages/connected/src/types/IConnectedLdoDataset.ts +++ b/packages/connected/src/types/IConnectedLdoDataset.ts @@ -3,6 +3,7 @@ import type { LdoBase, LdoDataset, ShapeType } from "@ldo/ldo"; import type { ConnectedPlugin } from "./ConnectedPlugin"; import type { InvalidIdentifierResource } from "../InvalidIdentifierResource"; import type { IConnectedLdoBuilder } from "./IConnectedLdoBuilder"; +import type { ConnectedLdoTransactionDataset } from "../ConnectedLdoTransactionDataset"; export type ReturnTypeFromArgs = Func extends ( arg: Arg, @@ -54,7 +55,7 @@ export type GetResourceReturnType< export interface IConnectedLdoDataset extends LdoDataset { /** - * Retireves a representation of a Resource at the given URI. This resource + * Retrieves a representation of a Resource at the given URI. This resource * represents the current state of the resource: whether it is currently * fetched or in the process of fetching as well as some information about it. * @@ -80,7 +81,7 @@ export interface IConnectedLdoDataset ): GetResourceReturnType; /** - * Retireves a representation of all Resources referenced by this dataset + * Retrieves a representation of all Resources referenced by this dataset * This does not necessarily mean that it's been fetched (use the * `getFetchedResources` method for that). It simply means that at one point * it was referenced. @@ -95,7 +96,7 @@ export interface IConnectedLdoDataset getResources(): Plugins[number]["types"]["resource"][]; /** - * Retireves a representation of all Resources that have been fetched. + * Retrieves a representation of all Resources that have been fetched. * * @returns a Resource array * @@ -152,7 +153,7 @@ export interface IConnectedLdoDataset forgetAllResources(): void; /** - * Sets conetext for a specific plugin + * Sets context for a specific plugin * * @param pluginName - the name of the plugin * @param context - the context for this specific plugin @@ -168,4 +169,6 @@ export interface IConnectedLdoDataset usingType( shapeType: ShapeType, ): IConnectedLdoBuilder; + + startTransaction(): ConnectedLdoTransactionDataset; } diff --git a/packages/react/src/methods/useLinkQuery.ts b/packages/react/src/methods/useLinkQuery.ts index 1accb785..75811c5b 100644 --- a/packages/react/src/methods/useLinkQuery.ts +++ b/packages/react/src/methods/useLinkQuery.ts @@ -14,7 +14,7 @@ import { useTrackingProxy } from "../util/useTrackingProxy"; /** * @internal * - * Creates a useMatchSubject function. + * Creates a useLinkQuery function. */ export function createUseLinkQuery( dataset: ConnectedLdoDataset, diff --git a/packages/react/src/methods/useResource.ts b/packages/react/src/methods/useResource.ts index d17420d4..ad74d9c4 100644 --- a/packages/react/src/methods/useResource.ts +++ b/packages/react/src/methods/useResource.ts @@ -57,7 +57,7 @@ export function createUseResource( const resource = useMemo(() => { if (uri) { const resource = dataset.getResource(uri); - // Run read operations if necissary + // Run read operations if necessary if (!options?.suppressInitialRead) { if (options?.reloadOnMount) { resource.read(); @@ -93,7 +93,7 @@ export function createUseResource( // Callback function to force the react dom to reload. const forceReload = useCallback( - // Wrap the resource in a proxy so it's techically a different object + // Wrap the resource in a proxy so it's technically a different object () => { if (resource) setResourceRepresentation(new Proxy(resource, {})); }, diff --git a/packages/svelte/src/methods/useLdo.ts b/packages/svelte/src/methods/useLdo.ts index ea9ca689..5080e1f7 100644 --- a/packages/svelte/src/methods/useLdo.ts +++ b/packages/svelte/src/methods/useLdo.ts @@ -17,7 +17,7 @@ export interface UseLdoMethods { */ dataset: ConnectedLdoDataset; /** - * Retireves a representation of a Resource at the given URI. This resource + * Retrieves a representation of a Resource at the given URI. This resource * represents the current state of the resource: whether it is currently * fetched or in the process of fetching as well as some information about it. * @@ -29,7 +29,7 @@ export interface UseLdoMethods { */ getResource: ConnectedLdoDataset["getResource"]; /** - * Sets conetext for a specific plugin + * Sets context for a specific plugin * * @param pluginName - the name of the plugin * @param context - the context for this specific plugin @@ -66,7 +66,7 @@ export interface UseLdoMethods { ...additionalResources: Plugins[number]["types"]["resource"][] ): Type; /** - * Commits the data of a writable Linke Data Object back to the remote. + * Commits the data of a writable Linked Data Object back to the remote. */ commitData( input: LdoBase, diff --git a/packages/svelte/src/methods/useResource.ts b/packages/svelte/src/methods/useResource.ts index c50b4714..96344c7b 100644 --- a/packages/svelte/src/methods/useResource.ts +++ b/packages/svelte/src/methods/useResource.ts @@ -55,7 +55,7 @@ export function createUseResource( if (uri) { // eslint-disable-next-line @typescript-eslint/no-explicit-any resource = dataset.getResource(uri) as any; - // Run read operations if necissary + // Run read operations if necessary if (!options?.suppressInitialRead) { if (options?.reloadOnMount) { resource!.read(); diff --git a/packages/vue/README.md b/packages/vue/README.md new file mode 100644 index 00000000..d8038cdd --- /dev/null +++ b/packages/vue/README.md @@ -0,0 +1,128 @@ +# @ldo/vue + +`@ldo/vue` provides composables for easily building Linked Data applications using [Vue](https://vuejs.org). + +**Note:** If you're building a [Solid](https://solidproject.org) application with Vue, we recommend that you use [@ldo/solid-vue](https://www.npmjs.com/package/@ldo/solid-vue) instead of this package. + +## Installation + +If you haven't already, [create your Vue project](https://vuejs.org/guide/quick-start.html#creating-a-vue-application). + +Navigate into your project's root folder and run the following command: + +```sh +cd my_project +npx @ldo/cli init +``` + +Now install the `@ldo/vue` library: + +```sh +npm i @ldo/vue +``` + +Also install a connected plugin of your choice: + +```sh +npm i @ldo/connected-solid @ldo/connected-nextgraph +``` + +
+ +Manual Installation + + +If you already have generated ShapeTypes, you may install the `@ldo/ldo` and `@ldo/vue` libraries independently. + +```sh +npm i @ldo/ldo @ldo/vue +``` + +Also install the connected plugin of your choice: + +```sh +npm i @ldo/connected-solid @ldo/connected-nextgraph +``` + +
+ +## Simple Example + +Below is a simple example of @ldo/vue in a real use-case. Assume that a ShapeType was previously generated and placed at `./_ldo/foafProfile.shapeTypes.ts`. + +```ts +// src/ldo.ts + +import { createLdoVueMethods } from "@ldo/vue"; +// replace the plugin with the connected plugin(s) of your choice +import { solidConnectedPlugin } from "@ldo/connected-solid"; + +// create LDO Vue methods that will be used in the whole app +export const { + dataset, + useSubject, + useResource, + useMatchSubject, + useSubscribeToResource, + /* export all you need */ +} = createLdoVueMethods([solidConnectedPlugin]); + +// At some point, you may want to implement some form of authentication +// suitable for your connected plugin. +// In case of Solid plugin, you need to provide authenticated fetch from the current session +dataset.setContext("solid", { fetch: authFetch }); +// We recommend you just use @ldo/solid-vue instead +``` + +```vue + + + +``` + +## API Details + +Vue [composables](https://vuejs.org/guide/reusability/composables.html): + +- [useLdo](https://ldo.js.org/latest/api/vue/useLdo/) +- [useResource](https://ldo.js.org/latest/api/vue/useResource/) +- [useSubject](https://ldo.js.org/latest/api/vue/useSubject/) +- [useMatchSubject](https://ldo.js.org/latest/api/vue/useMatchSubject/) +- [useMatchObject](https://ldo.js.org/latest/api/vue/useMatchObject/) +- [useSubscribeToResource](https://ldo.js.org/latest/api/vue/useSubscribeToResource/) +- [useLinkQuery](https://ldo.js.org/latest/api/vue/useLinkQuery/) +- [useChangeDataset](https://ldo.js.org/latest/api/vue/useChangeDataset/) +- [useChangeSubject](https://ldo.js.org/latest/api/vue/useChangeSubject/) +- [useChangeMatchSubject](https://ldo.js.org/latest/api/vue/useChangeMatchSubject/) +- [useChangeMatchObject](https://ldo.js.org/latest/api/vue/useChangeMatchObject/) + +## Sponsorship + +This project was made possible by a grant from [NGI0 Commons Fund](https://nlnet.nl/commonsfund/) via nlnet. Learn more on the [NLnet project page](https://nlnet.nl/project/LDO-up/). + +[nlnet foundation logo](https://nlnet.nl/) +[NGI Zero Commons Logo](https://nlnet.nl/commonsfund/) + +## License + +MIT diff --git a/packages/vue/package.json b/packages/vue/package.json new file mode 100644 index 00000000..c1f691a8 --- /dev/null +++ b/packages/vue/package.json @@ -0,0 +1,58 @@ +{ + "name": "@ldo/vue", + "version": "1.0.0-alpha.50", + "description": "A Vue library for LDO", + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./package.json": "./package.json" + }, + "scripts": { + "build": "rimraf dist && NODE_OPTIONS=--max-old-space-size=8192 tsdown", + "test": "vitest run --coverage" + }, + "keywords": [ + "ldo", + "linked-data-objects", + "rdf", + "solid-project", + "vuejs" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/o-development/ldo.git" + }, + "author": "Jackson Morgan", + "license": "MIT", + "bugs": { + "url": "https://github.com/o-development/ldo/issues" + }, + "homepage": "https://github.com/o-development/ldo/tree/main/packages/vue#readme", + "dependencies": { + "@ldo/connected": "^1.0.0-alpha.50", + "@ldo/jsonld-dataset-proxy": "^1.0.0-alpha.50", + "@ldo/ldo": "^1.0.0-alpha.50", + "@ldo/rdf-utils": "^1.0.0-alpha.50" + }, + "peerDependencies": { + "vue": "^3.3.0" + }, + "devDependencies": { + "@ldo/connected-solid": "^1.0.0-alpha.50", + "@ldo/test-solid-server": "^1.0.0-alpha.50", + "@types/shexj": "^2.1.7", + "vue": "^3.5.43" + } +} diff --git a/packages/vue/src/createLdoVueComposables.ts b/packages/vue/src/createLdoVueComposables.ts new file mode 100644 index 00000000..7ebeba2b --- /dev/null +++ b/packages/vue/src/createLdoVueComposables.ts @@ -0,0 +1,129 @@ +import { + type ConnectedLdoDataset, + createConnectedLdoDataset, + type ConnectedPlugin, +} from "@ldo/connected"; +import { inject, provide, type InjectionKey } from "vue"; +import { createUseSubject } from "./methods/useSubject"; +import { createUseMatchObject } from "./methods/useMatchObject"; +import { createUseResource } from "./methods/useResource"; +import { createUseMatchSubject } from "./methods/useMatchSubject"; +import { createUseDataset } from "./methods/useDataset"; +import { createUseLdo } from "./methods/useLdo"; +import { createUseSubscribeToResource } from "./methods/useSubscribeToResource"; +import { createUseLinkQuery } from "./methods/useLinkQuery"; +import { createUseChangeDataset } from "./methods/change/useChangeDataset"; +import { createUseChangeMatchObject } from "./methods/change/useChangeMatchObject"; +import { createUseChangeMatchSubject } from "./methods/change/useChangeMatchSubject"; + +/** + * Create ldo composables for Vue, where the dataset is isolated and provided/injected. + * + * You have to run `provideDataset()` in the ancestor component to set up the dataset. + * + * @example + * ```ts + * // in ldoComposables.ts do + * + * export const { provideDataset, useDataset, useSubject, ...etc } = createLdoVueComposables(connectedPlugins); + * export { provideDataset, useDataset } + * + * // provide dataset in parent component + * provideDataset() + * + * // use composables in child component + * const subjectLdo = useSubject(...subjectProps) + * ``` + */ +export function createLdoVueComposables( + plugins: Plugin[], +) { + const datasetInjectionKey: InjectionKey> = + Symbol("Dataset Key"); + + function provideDataset() { + const dataset = createConnectedLdoDataset(plugins); + provide(datasetInjectionKey, dataset); + return dataset; + } + + return { + datasetInjectionKey, + provideDataset, + useDataset: createComposable( + datasetInjectionKey, + createUseDataset, + ), + useLdo: createComposable(datasetInjectionKey, createUseLdo), + useResource: createComposable( + datasetInjectionKey, + createUseResource, + ), + useSubject: createComposable( + datasetInjectionKey, + createUseSubject, + ), + useMatchObject: createComposable( + datasetInjectionKey, + createUseMatchObject, + ), + useMatchSubject: createComposable( + datasetInjectionKey, + createUseMatchSubject, + ), + useSubscribeToResource: createComposable( + datasetInjectionKey, + createUseSubscribeToResource, + ), + useLinkQuery: createComposable( + datasetInjectionKey, + createUseLinkQuery, + ), + useChangeDataset: createComposable( + datasetInjectionKey, + createUseChangeDataset, + ), + useChangeSubject: createComposable( + datasetInjectionKey, + createUseSubject, + ), + useChangeMatchObject: createComposable( + datasetInjectionKey, + createUseChangeMatchObject, + ), + useChangeMatchSubject: createComposable( + datasetInjectionKey, + createUseChangeMatchSubject, + ), + }; +} + +/** + * @internal + * + * A method to create the composable with injected dataset + */ +const createComposable = < + Plugin extends ConnectedPlugin, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + CreateComposable extends (dataset: ConnectedLdoDataset) => any, +>( + key: InjectionKey>, + creator: CreateComposable, +): ReturnType => { + // We return a proxy function matching the exact signature layout of the underlying contract + return function wrappedMethod( + ...props: Parameters> + ): ReturnType> { + // inject the dataset + const dataset = inject(key); + // check that it's actually provided + if (!dataset) + throw new Error( + "Composable must have a dataset provider. Please call provideDataset() in a parent component.", + ); + + // Create and run the composable with the injected dataset + return creator(dataset)(...props); + } as ReturnType; +}; diff --git a/packages/vue/src/createLdoVueMethods.ts b/packages/vue/src/createLdoVueMethods.ts new file mode 100644 index 00000000..2cdba671 --- /dev/null +++ b/packages/vue/src/createLdoVueMethods.ts @@ -0,0 +1,89 @@ +import { + createConnectedLdoDataset, + type ConnectedPlugin, +} from "@ldo/connected"; +import { createUseLdo } from "./methods/useLdo"; +import { createUseMatchObject } from "./methods/useMatchObject"; +import { createUseMatchSubject } from "./methods/useMatchSubject"; +import { createUseResource } from "./methods/useResource"; +import { createUseSubject } from "./methods/useSubject"; +import { createUseSubscribeToResource } from "./methods/useSubscribeToResource"; +import { createUseLinkQuery } from "./methods/useLinkQuery"; +import { createUseChangeDataset } from "./methods/change/useChangeDataset"; +import { createUseChangeSubject } from "./methods/change/useChangeSubject"; +import { createUseChangeMatchObject } from "./methods/change/useChangeMatchObject"; +import { createUseChangeMatchSubject } from "./methods/change/useChangeMatchSubject"; +import { createUseDataset } from "./methods/useDataset"; + +/** + * A function that creates all common vue functions given specific plugin. + * + * @example + * `methods.ts` + * ```tsx + * import { solidConnectedPlugin } from "@ldo/connected-solid"; + * import { nextGraphConnectedPlugin } from "@ldo/connected-nextgraph"; + * import { createLdoVueMethods } from "@ldo/vue"; + * + * // Export the results to be used in the reset of the application + * export const { + * dataset, + * useLdo, + * useMatchObject, + * useMatchSubject, + * useResource, + * useSubject, + * useSubscribeToResource, + * useLinkQuery, + * useChangeDataset, + * useChangeSubject, + * useChangeMatchObject, + * useChangeMatchSubject, + * } = createLdoVueMethods([ + * solidConnectedPlugin, + * nextGraphConnectedPlugin + * ]); + * ``` + * + * `App.vue` + * ```vue + * + * + * + * ``` + */ +export function createLdoVueMethods( + plugins: Plugin[], +) { + const dataset = createConnectedLdoDataset(plugins); + dataset.setMaxListeners(1000); + + return { + dataset, + useDataset: createUseDataset(dataset), + useLdo: createUseLdo(dataset), + useResource: createUseResource(dataset), + useSubject: createUseSubject(dataset), + useMatchObject: createUseMatchObject(dataset), + useMatchSubject: createUseMatchSubject(dataset), + useSubscribeToResource: createUseSubscribeToResource(dataset), + useLinkQuery: createUseLinkQuery(dataset), + useChangeDataset: createUseChangeDataset(dataset), + useChangeSubject: createUseChangeSubject(dataset), + useChangeMatchObject: createUseChangeMatchObject(dataset), + useChangeMatchSubject: createUseChangeMatchSubject(dataset), + }; +} diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts new file mode 100644 index 00000000..9ed3d07e --- /dev/null +++ b/packages/vue/src/index.ts @@ -0,0 +1,19 @@ +export * from "./createLdoVueMethods"; +export * from "./createLdoVueComposables"; + +export * from "./methods/useDataset"; +export * from "./methods/useLdo"; +export * from "./methods/useMatchObject"; +export * from "./methods/useMatchSubject"; +export * from "./methods/useResource"; +export * from "./methods/useSubject"; +export * from "./methods/useSubscribeToResource"; +export * from "./methods/useLinkQuery"; + +// export * from "./methods/change/types"; +export * from "./methods/change/useChangeDataset"; +export * from "./methods/change/useChangeSubject"; +export * from "./methods/change/useChangeMatchObject"; +export * from "./methods/change/useChangeMatchSubject"; + +export * from "./util/useTrackingProxy"; diff --git a/packages/vue/src/methods/change/useChangeDataset.ts b/packages/vue/src/methods/change/useChangeDataset.ts new file mode 100644 index 00000000..b08590b5 --- /dev/null +++ b/packages/vue/src/methods/change/useChangeDataset.ts @@ -0,0 +1,83 @@ +import type { + ConnectedLdoTransactionDataset, + ConnectedPlugin, + IConnectedLdoDataset, +} from "@ldo/connected"; +import { + type MaybeRefOrGetter, + toValue, + shallowRef, + watch, + type Ref, +} from "vue"; + +/** + * @internal + * + * Creates a useChangeDataset composable + */ +export function createUseChangeDataset( + dataset: IConnectedLdoDataset, +) { + /** + * Returns a transaction on the dataset that can be modified and committed. + * + * @example + * ```ts + * const { transactionDataset, setData, commitData } = useChangeDataset(); + * // you must populate the dataset with data in order to change it + * const resource = useResource(resourceUri); + * + * // you can do this in a callback, e. g. form update + * setData((dataset) => { + * // perform operations on the dataset + * dataset.deleteMatches(some, match); + * dataset.add(quad(subject, predicate, object, graph)) + * // etc... + * }); + * + * // save the data + * const result = await results.changeDataset.commitData() + * + * // check the result for errors + * ``` + */ + return function useChangeDataset( + specificDataset?: MaybeRefOrGetter< + IConnectedLdoDataset | undefined + >, + ) { + const transactionDataset = shallowRef< + ConnectedLdoTransactionDataset + >() as Ref>; + + watch( + () => toValue(specificDataset), + (newDataset) => { + transactionDataset.value = (newDataset ?? dataset).startTransaction(); + }, + { deep: false, immediate: true }, + ); + + const setData = ( + changer: (toChange: ConnectedLdoTransactionDataset) => void, + ) => { + const subTransaction = transactionDataset.value.startTransaction(); + changer(subTransaction); + subTransaction.commit(); + }; + + const commitData = async () => { + const result = await transactionDataset.value.commitToRemote(); + if (!result.isError) { + // Replace with a new transaction from the dataset or specificDataset + transactionDataset.value = ( + toValue(specificDataset) ?? dataset + ).startTransaction(); + } + return result; + }; + + return { transactionDataset, setData, commitData }; + }; +} diff --git a/packages/vue/src/methods/change/useChangeMatchObject.ts b/packages/vue/src/methods/change/useChangeMatchObject.ts new file mode 100644 index 00000000..0eec2a15 --- /dev/null +++ b/packages/vue/src/methods/change/useChangeMatchObject.ts @@ -0,0 +1,91 @@ +import type { IConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; +import { type LdSet, write, type LdoBase, type ShapeType } from "@ldo/ldo"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { createUseChangeDataset } from "./useChangeDataset"; +import type { QuadMatch } from "@ldo/rdf-utils"; +import { createProxyInteractOptions } from "@ldo/jsonld-dataset-proxy"; +import { + createUseMatchObject, + type UseMatchObjectOptions, +} from "../useMatchObject"; + +/** + * @internal + * + * Creates a useChangeMatchObject composable + */ +export function createUseChangeMatchObject( + dataset: IConnectedLdoDataset, +) { + const useChangeDataset = createUseChangeDataset(dataset); + const useMatchObject = createUseMatchObject(dataset); + + /** + * Returns a reactive list of matched objects that can be modified and committed + */ + return function useChangeMatchObject( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + predicate?: MaybeRefOrGetter, + graph?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ) { + const resolvedDataset = computed(() => toValue(options)?.dataset); + + const { + transactionDataset, + setData: setDataset, + commitData, + } = useChangeDataset(resolvedDataset); + + const transactionDatasetOption = computed(() => ({ + dataset: transactionDataset.value, + })); + + const ldObject = useMatchObject( + shapeType, + subject, + predicate, + graph, + transactionDatasetOption, + ); + + const setData = < + OtherType extends LdoBase | LdSet | undefined = undefined, + >( + writeResource: Plugins[number]["types"]["resource"], + changer: ( + toChange: OtherType extends undefined ? LdSet : OtherType, + ) => void, + otherType?: OtherType, + ) => { + setDataset((dataset) => { + const ldObject = otherType + ? write(writeResource.uri).usingCopy( + createProxyInteractOptions("dataset", dataset).usingCopy( + otherType, + )[0], + )[0] + : dataset + .usingType(toValue(shapeType)) + .write(writeResource.uri) + .matchObject( + toValue(subject), + toValue(predicate), + toValue(graph), + ); + + changer( + ldObject as OtherType extends undefined ? LdSet : OtherType, + ); + }); + }; + + return { + ldo: ldObject, + setData, + commitData, + transactionDataset, + }; + }; +} diff --git a/packages/vue/src/methods/change/useChangeMatchSubject.ts b/packages/vue/src/methods/change/useChangeMatchSubject.ts new file mode 100644 index 00000000..89267ca9 --- /dev/null +++ b/packages/vue/src/methods/change/useChangeMatchSubject.ts @@ -0,0 +1,91 @@ +import type { IConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; +import { type LdSet, write, type LdoBase, type ShapeType } from "@ldo/ldo"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { createUseChangeDataset } from "./useChangeDataset"; +import type { QuadMatch } from "@ldo/rdf-utils"; +import { createProxyInteractOptions } from "@ldo/jsonld-dataset-proxy"; +import { + createUseMatchSubject, + type UseMatchSubjectOptions, +} from "../useMatchSubject"; + +/** + * @internal + * + * Creates a useChangeMatchSubject composable + */ +export function createUseChangeMatchSubject( + dataset: IConnectedLdoDataset, +) { + const useChangeDataset = createUseChangeDataset(dataset); + const useMatchSubject = createUseMatchSubject(dataset); + + /** + * Returns a reactive list of matched subjects that can be modified and committed + */ + return function useChangeMatchSubject( + shapeType: MaybeRefOrGetter>, + predicate?: MaybeRefOrGetter, + object?: MaybeRefOrGetter, + graph?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ) { + const resolvedDataset = computed(() => toValue(options)?.dataset); + + const { + transactionDataset, + setData: setDataset, + commitData, + } = useChangeDataset(resolvedDataset); + + const transactionDatasetOption = computed(() => ({ + dataset: transactionDataset.value, + })); + + const ldObject = useMatchSubject( + shapeType, + predicate, + object, + graph, + transactionDatasetOption, + ); + + const setData = < + OtherType extends LdoBase | LdSet | undefined = undefined, + >( + writeResource: Plugins[number]["types"]["resource"], + changer: ( + toChange: OtherType extends undefined ? LdSet : OtherType, + ) => void, + otherType?: OtherType, + ) => { + setDataset((dataset) => { + const ldObject = otherType + ? write(writeResource.uri).usingCopy( + createProxyInteractOptions("dataset", dataset).usingCopy( + otherType, + )[0], + )[0] + : dataset + .usingType(toValue(shapeType)) + .write(writeResource.uri) + .matchSubject( + toValue(predicate), + toValue(object), + toValue(graph), + ); + + changer( + ldObject as OtherType extends undefined ? LdSet : OtherType, + ); + }); + }; + + return { + ldo: ldObject, + setData, + commitData, + transactionDataset, + }; + }; +} diff --git a/packages/vue/src/methods/change/useChangeSubject.ts b/packages/vue/src/methods/change/useChangeSubject.ts new file mode 100644 index 00000000..d42b9f7a --- /dev/null +++ b/packages/vue/src/methods/change/useChangeSubject.ts @@ -0,0 +1,76 @@ +import type { IConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; +import { type LdSet, write, type LdoBase, type ShapeType } from "@ldo/ldo"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; +import { createUseChangeDataset } from "./useChangeDataset"; +import type { SubjectNode } from "@ldo/rdf-utils"; +import { createUseSubject, type UseSubjectOptions } from "../useSubject"; +import { createProxyInteractOptions } from "@ldo/jsonld-dataset-proxy"; + +/** + * @internal + * + * Creates a useChangeSubject composable. + */ +export function createUseChangeSubject( + dataset: IConnectedLdoDataset, +) { + const useChangeDataset = createUseChangeDataset(dataset); + const useSubject = createUseSubject(dataset); + + /** + * Returns a reactive subject that can be modified and committed + */ + return function useChangeSubject( + shapeType: MaybeRefOrGetter>, + subject: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ) { + const resolvedDataset = computed(() => toValue(options)?.dataset); + + const { + transactionDataset, + setData: setDataset, + commitData, + } = useChangeDataset(resolvedDataset); + + const subjectTransactionDataset = computed(() => ({ + dataset: transactionDataset.value, + })); + + const ldObject = useSubject(shapeType, subject, subjectTransactionDataset); + + const setData = < + OtherType extends LdoBase | LdSet | undefined = undefined, + >( + writeResource: Plugins[number]["types"]["resource"], + changer: ( + toChange: OtherType extends undefined ? Type : OtherType, + ) => void, + otherType?: OtherType, + ) => { + const subjectValue = toValue(subject); + if (!subjectValue) return; + setDataset((dataset) => { + const ldObject = otherType + ? write(writeResource.uri).usingCopy( + createProxyInteractOptions("dataset", dataset).usingCopy( + otherType, + )[0], + )[0] + : dataset + .usingType(toValue(shapeType)) + .write(writeResource.uri) + .fromSubject(subjectValue); + + changer(ldObject as OtherType extends undefined ? Type : OtherType); + }); + }; + + return { + ldo: ldObject, + setData, + commitData, + transactionDataset, + }; + }; +} diff --git a/packages/vue/src/methods/useDataset.ts b/packages/vue/src/methods/useDataset.ts new file mode 100644 index 00000000..b643dfad --- /dev/null +++ b/packages/vue/src/methods/useDataset.ts @@ -0,0 +1,17 @@ +import type { IConnectedLdoDataset, ConnectedPlugin } from "@ldo/connected"; + +/** + * @internal + * + * Creates a useDataset function + */ +export function createUseDataset( + dataset: IConnectedLdoDataset, +) { + /** + * Returns the global dataset for the application + */ + return function useDataset() { + return dataset; + }; +} diff --git a/packages/vue/src/methods/useLdo.ts b/packages/vue/src/methods/useLdo.ts new file mode 100644 index 00000000..81821ee8 --- /dev/null +++ b/packages/vue/src/methods/useLdo.ts @@ -0,0 +1,152 @@ +/* +This is a copy from @ldo/react library. +It has nothing framework-specific in it. +It's just a wrapper of ldo methods that looks like a composable. +*/ + +import { + changeData, + type ConnectedLdoDataset, + type ConnectedLdoTransactionDataset, + type ConnectedPlugin, +} from "@ldo/connected"; +import { getDataset, type LdoBase, type ShapeType } from "@ldo/ldo"; +import type { SubjectNode } from "@ldo/rdf-utils"; + +/** + * The methods returned by useLdo + */ +export interface UseLdoMethods { + /** + * A ConnectedLdoDataset + */ + dataset: ConnectedLdoDataset; + /** + * Retrieves a representation of a Resource at the given URI. This resource + * represents the current state of the resource: whether it is currently + * fetched or in the process of fetching as well as some information about it. + * + * @param uri - the URI of the resource + * @param pluginName - optionally, force this function to choose a specific + * plugin to use rather than perform content negotiation. + * + * @returns a Resource + */ + getResource: ConnectedLdoDataset["getResource"]; + /** + * Sets context for a specific plugin + * + * @param pluginName - the name of the plugin + * @param context - the context for this specific plugin + */ + setContext: ConnectedLdoDataset["setContext"]; + /** + * Gets a linked data object based on the subject + */ + getSubject( + shapeType: ShapeType, + subject: string | SubjectNode, + ): Type; + /** + * Shorthand for connectedLdoDataset + * .usingType(shapeType) + * .write(...resources.map((r) => r.uri)) + * .fromSubject(subject); + * @param shapeType - The shapeType to represent the data + * @param subject - A subject URI + * @param resources - The resources changes to should written to + */ + createData( + shapeType: ShapeType, + subject: string | SubjectNode, + resource: Plugins[number]["types"]["resource"], + ...additionalResources: Plugins[number]["types"]["resource"][] + ): Type; + /** + * Returns a writable LinkedDataObject given a linked data object + */ + changeData( + input: Type, + resource: Plugins[number]["types"]["resource"], + ...additionalResources: Plugins[number]["types"]["resource"][] + ): Type; + /** + * Commits the data of a writable Linked Data Object back to the remote. + */ + commitData( + input: LdoBase, + ): ReturnType["commitToRemote"]>; +} + +/** + * @internal + * Creates a useLdo composable + */ +export function createUseLdo( + dataset: ConnectedLdoDataset, +) { + const toReturn = { + dataset, + /** + * Gets a resource + */ + getResource: dataset.getResource.bind(dataset), + /** + * Set the context + */ + setContext: dataset.setContext.bind(dataset), + /** + * Returns a Linked Data Object for a subject + * @param shapeType The shape type for the data + * @param subject Subject Node + * @returns A Linked Data Object + */ + getSubject( + shapeType: ShapeType, + subject: string | SubjectNode, + ): Type { + return dataset.usingType(shapeType).fromSubject(subject); + }, + /** + * Begins tracking changes to eventually commit for a new subject + * @param shapeType The shape type that defines the created data + * @param subject The RDF subject for a Linked Data Object + * @param resources Any number of resources to which this data should be written + * @returns A Linked Data Object to modify and commit + */ + createData( + shapeType: ShapeType, + subject: string | SubjectNode, + resource: Plugins[number]["types"]["resource"], + ...additionalResources: Plugins[number]["types"]["resource"][] + ): Type { + return dataset.createData( + shapeType, + subject, + resource, + ...additionalResources, + ); + }, + /** + * Begins tracking changes to eventually commit + * @param input A linked data object to track changes on + * @param resources + */ + changeData: changeData, + /** + * Commits the transaction to the global dataset, syncing all subscribing + * components and Solid Pods + */ + commitData( + input: LdoBase, + ): ReturnType["commitToRemote"]> { + const inputDataset = getDataset( + input, + ) as ConnectedLdoTransactionDataset; + return inputDataset.commitToRemote(); + }, + }; + return function useLdo(): UseLdoMethods { + return toReturn; + }; +} diff --git a/packages/vue/src/methods/useLinkQuery.ts b/packages/vue/src/methods/useLinkQuery.ts new file mode 100644 index 00000000..d024adb3 --- /dev/null +++ b/packages/vue/src/methods/useLinkQuery.ts @@ -0,0 +1,67 @@ +import type { + IConnectedLdoDataset, + ConnectedPlugin, + LQInput, +} from "@ldo/connected"; +import type { LdoBase, ShapeType } from "@ldo/ldo"; +import type { SubjectNode } from "@ldo/rdf-utils"; +import { createUseSubject } from "./useSubject"; +import { type MaybeRefOrGetter, toValue, watch } from "vue"; + +/** + * @internal + * + * Creates a useLinkQuery composable. + */ +export function createUseLinkQuery( + dataset: IConnectedLdoDataset, +) { + /** + * Runs a LDO link query. + * Subscribes to related changes. + * Returns the starting point as a reactive Linked Data Object. + */ + return function useLinkQuery< + Type extends LdoBase, + QueryInput extends LQInput, + >( + shapeType: MaybeRefOrGetter>, + startingResource: MaybeRefOrGetter, + startingSubject: MaybeRefOrGetter, + linkQuery: MaybeRefOrGetter, + ) { + watch( + [ + () => toValue(shapeType), + () => toValue(startingResource), + () => toValue(startingSubject), + () => toValue(linkQuery), + ], + async ( + [shapeType, startingResourceUri, startingSubjectUri, linkQuery], + _, + onCleanup, + ) => { + let finished = false; + const resource = dataset.getResource(startingResourceUri); + const resourceLinkQuery = dataset + .usingType(shapeType) + .startLinkQuery(resource, startingSubjectUri, linkQuery); + + await resourceLinkQuery.subscribe(); + // if already cleaned up, unsubscribe immediately + if (finished) { + await resourceLinkQuery.unsubscribeAll(); + } + + onCleanup(async () => { + finished = true; + await resourceLinkQuery.unsubscribeAll(); + }); + }, + { immediate: true }, + ); + + return createUseSubject(dataset)(shapeType, startingSubject); + }; +} diff --git a/packages/vue/src/methods/useMatchObject.ts b/packages/vue/src/methods/useMatchObject.ts new file mode 100644 index 00000000..aecd7e79 --- /dev/null +++ b/packages/vue/src/methods/useMatchObject.ts @@ -0,0 +1,58 @@ +import type { QuadMatch } from "@ldo/rdf-utils"; +import type { ShapeType, LdoBase, LdSet, LdoBuilder } from "@ldo/ldo"; +import { useTrackingProxy } from "../util/useTrackingProxy"; +import type { ConnectedPlugin, IConnectedLdoDataset } from "@ldo/connected"; +import { computed, toValue, type MaybeRefOrGetter, type Ref } from "vue"; + +export interface UseMatchObjectOptions { + dataset?: IConnectedLdoDataset; +} + +export interface UseMatchObjectComposable { + ( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + predicate?: MaybeRefOrGetter, + graph?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref>; +} + +/** + * @internal + * + * Creates a useMatchObject composable. + */ +export function createUseMatchObject( + dataset: IConnectedLdoDataset, +): UseMatchObjectComposable { + /** + * Returns a reactive LDO set of matching items. + * Triggers a rerender if that data are updated. + */ + return function useMatchObject< + Type extends LdoBase, + CustomPlugins extends ConnectedPlugin[] = Plugins, + >( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + predicate?: MaybeRefOrGetter, + graph?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref> { + const createLdo = computed(() => { + const subjectValue = toValue(subject); + const predicateValue = toValue(predicate); + const graphValue = toValue(graph); + + return (builder: LdoBuilder) => + builder.matchObject(subjectValue, predicateValue, graphValue); + }); + + return useTrackingProxy( + shapeType, + createLdo, + () => toValue(options)?.dataset ?? dataset, + ); + }; +} diff --git a/packages/vue/src/methods/useMatchSubject.ts b/packages/vue/src/methods/useMatchSubject.ts new file mode 100644 index 00000000..556eb295 --- /dev/null +++ b/packages/vue/src/methods/useMatchSubject.ts @@ -0,0 +1,44 @@ +import type { QuadMatch } from "@ldo/rdf-utils"; +import type { ShapeType, LdoBase, LdSet, LdoBuilder } from "@ldo/ldo"; +import { useTrackingProxy } from "../util/useTrackingProxy"; +import type { ConnectedPlugin, IConnectedLdoDataset } from "@ldo/connected"; +import { computed, toValue, type MaybeRefOrGetter, type Ref } from "vue"; + +export interface UseMatchSubjectOptions { + dataset?: IConnectedLdoDataset; +} + +/** + * @internal + * + * Creates a useMatchSubject composable. + */ +export function createUseMatchSubject( + dataset: IConnectedLdoDataset, +) { + /** + * Returns a reactive LDO set of matching linked data objects. + * Triggers a rerender if the data are updated. + */ + return function useMatchSubject( + shapeType: MaybeRefOrGetter>, + predicate?: MaybeRefOrGetter, + object?: MaybeRefOrGetter, + graph?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref> { + const createLdo = computed(() => { + const predicateValue = toValue(predicate); + const objectValue = toValue(object); + const graphValue = toValue(graph); + return (builder: LdoBuilder) => + builder.matchSubject(predicateValue, objectValue, graphValue); + }); + + return useTrackingProxy( + shapeType, + createLdo, + () => toValue(options)?.dataset ?? dataset, + ); + }; +} diff --git a/packages/vue/src/methods/useResource.ts b/packages/vue/src/methods/useResource.ts new file mode 100644 index 00000000..3e9d894e --- /dev/null +++ b/packages/vue/src/methods/useResource.ts @@ -0,0 +1,149 @@ +import type { + ConnectedPlugin, + GetResourceReturnType, + IConnectedLdoDataset, + Resource, +} from "@ldo/connected"; +import { + shallowRef, + watch, + onBeforeUnmount, + type Ref, + triggerRef, + type MaybeRefOrGetter, + toValue, +} from "vue"; + +export interface UseResourceOptions { + pluginName?: Name; + suppressInitialRead?: boolean; + reloadOnMount?: boolean; + subscribe?: boolean; +} + +export type useResourceType = { + < + Name extends Plugins[number]["name"], + Plugin extends Extract, + UriType extends string, + >( + uri: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref>; + < + Name extends Plugins[number]["name"], + Plugin extends Extract, + UriType extends string, + >( + uri?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref | undefined>; +}; + +/** + * @internal + * + * Creates a useResource composable. + */ +export function createUseResource( + dataset: IConnectedLdoDataset, +): useResourceType { + /** + * Returns a reactive resource and triggers a rerender if that resource is updated. + */ + return function useResource< + Name extends Plugins[number]["name"], + Plugin extends Extract, + UriType extends string, + >( + uri?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref | undefined> { + const resourceRef: Ref = shallowRef< + Resource | undefined + >(); + let subscriptionId: string | undefined; + + const onResourceUpdate = () => { + triggerRef(resourceRef); + }; + + watch( + [() => toValue(uri), () => toValue(options)], + async ([newUri, newOptions], [oldUri, oldOptions], onCleanup) => { + // if (newUri === oldUri && newOptions === oldOptions) return; + // TODO handle change in uri vs options more carefully + + let cancelled = false; + let localResource: Resource | undefined; + + onCleanup(async () => { + cancelled = true; + localResource?.off("update", onResourceUpdate); + }); + + const uriChanged = oldUri !== newUri; + const subscriptionTurnedOff = !newOptions?.subscribe; + + if (uriChanged || subscriptionTurnedOff) { + if (subscriptionId) { + const sid = subscriptionId; + await resourceRef.value?.unsubscribeFromNotifications(sid); + // clear old subscription id + // but make sure we don't unintentionally clear a new subscription id + if (sid === subscriptionId) subscriptionId = undefined; + onResourceUpdate(); + } + } + + if (newUri) { + localResource = dataset.getResource(newUri) as Resource; + resourceRef.value = localResource; + if (!newOptions?.suppressInitialRead) { + if (newOptions?.reloadOnMount) { + resourceRef.value!.read(); + } else { + resourceRef.value!.readIfUnfetched(); + } + } + + if (resourceRef.value) { + resourceRef.value.on("update", onResourceUpdate); + // Subscribe to notifications if it's needed + if ( + newOptions?.subscribe && + (uriChanged || !oldOptions?.subscribe) + ) { + const id = await resourceRef.value.subscribeToNotifications(); + onResourceUpdate(); + + if (cancelled) { + await resourceRef.value.unsubscribeFromNotifications(id); + onResourceUpdate(); + } else { + subscriptionId = id; + } + } + } + } else { + resourceRef.value = undefined; + } + }, + { immediate: true }, + ); + + // cleanup + onBeforeUnmount(() => { + resourceRef.value?.off("update", onResourceUpdate); + if (subscriptionId) { + resourceRef.value?.unsubscribeFromNotifications(subscriptionId); + subscriptionId = undefined; + } + }); + + return resourceRef as Ref< + GetResourceReturnType | undefined, + GetResourceReturnType + >; + }; +} diff --git a/packages/vue/src/methods/useSubject.ts b/packages/vue/src/methods/useSubject.ts new file mode 100644 index 00000000..2bbe727c --- /dev/null +++ b/packages/vue/src/methods/useSubject.ts @@ -0,0 +1,58 @@ +import type { SubjectNode } from "@ldo/rdf-utils"; +import type { ShapeType, LdoBase, LdoBuilder } from "@ldo/ldo"; +import { useTrackingProxy } from "../util/useTrackingProxy"; +import type { ConnectedPlugin, IConnectedLdoDataset } from "@ldo/connected"; +import { computed, toValue, type MaybeRefOrGetter, type Ref } from "vue"; + +export interface UseSubjectOptions { + dataset?: IConnectedLdoDataset; +} + +export type useSubjectType = { + ( + shapeType: MaybeRefOrGetter>, + subject: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref; + ( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref; + ( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref; +}; + +/** + * @internal + * + * Creates a useSubject composable. + */ +export function createUseSubject( + dataset: IConnectedLdoDataset, +): useSubjectType { + /** + * Returns a reactive Linked Data Object based on the provided subject. + * Triggers a rerender if the data are updated. + */ + return function useSubject( + shapeType: MaybeRefOrGetter>, + subject?: MaybeRefOrGetter, + options?: MaybeRefOrGetter>, + ): Ref { + const createLdo = computed(() => { + const subjectValue = toValue(subject); + if (!subjectValue) return undefined; + return (builder: LdoBuilder) => builder.fromSubject(subjectValue); + }); + + return useTrackingProxy( + shapeType, + createLdo, + () => toValue(options)?.dataset ?? dataset, + ); + }; +} diff --git a/packages/vue/src/methods/useSubscribeToResource.ts b/packages/vue/src/methods/useSubscribeToResource.ts new file mode 100644 index 00000000..5f314cf8 --- /dev/null +++ b/packages/vue/src/methods/useSubscribeToResource.ts @@ -0,0 +1,66 @@ +import type { ConnectedPlugin, IConnectedLdoDataset } from "@ldo/connected"; +import { onBeforeUnmount, toValue, watch, type MaybeRefOrGetter } from "vue"; + +/** + * @internal + * + * Creates a useSubscribeToResource composable. + */ +export function createUseSubscribeToResource( + dataset: IConnectedLdoDataset, +) { + /** + * Starts and updates subscriptions to a list of resources. + */ + return function useSubscribeToResource( + uris: MaybeRefOrGetter, + ): void { + const currentlySubscribed: Record = {}; + + watch( + () => toValue(uris), + async (newUris) => { + const resources = newUris.map((uri) => dataset.getResource(uri)); + const previousSubscriptions = { ...currentlySubscribed }; + + await Promise.all( + resources.map(async (resource) => { + if (!previousSubscriptions[resource.uri]) { + // Prevent multiple triggers from created subscriptions while waiting + // for connection + currentlySubscribed[resource.uri] = "AWAITING"; + const _result = await resource.readIfUnfetched(); + currentlySubscribed[resource.uri] = + await resource.subscribeToNotifications(); + } else { + delete previousSubscriptions[resource.uri]; + } + }), + ); + + await Promise.all( + Object.entries(previousSubscriptions).map( + async ([resourceUri, subscriptionId]) => { + // Unsubscribe + delete currentlySubscribed[resourceUri]; + const resource = dataset.getResource(resourceUri); + await resource.unsubscribeFromNotifications(subscriptionId); + }, + ), + ); + }, + { deep: true, immediate: true }, // also detect edits of the array in-place + ); + + onBeforeUnmount(async () => { + await Promise.all( + Object.entries(currentlySubscribed).map( + async ([resourceUri, subscriptionId]) => { + const resource = dataset.getResource(resourceUri); + await resource.unsubscribeFromNotifications(subscriptionId); + }, + ), + ); + }); + }; +} diff --git a/packages/vue/src/util/useTrackingProxy.ts b/packages/vue/src/util/useTrackingProxy.ts new file mode 100644 index 00000000..36f65abd --- /dev/null +++ b/packages/vue/src/util/useTrackingProxy.ts @@ -0,0 +1,72 @@ +import type { LdoBuilder } from "@ldo/ldo"; +import type { LdoBase, LdoDataset, ShapeType } from "@ldo/ldo"; +import { createTrackingProxyBuilder } from "@ldo/connected"; +import { + type MaybeRefOrGetter, + type Ref, + shallowRef, + toValue, + triggerRef, + watch, +} from "vue"; + +/** + * @internal + * + * A composable for tracking proxies. + */ +export function useTrackingProxy( + shapeType: MaybeRefOrGetter>, + createLdo: MaybeRefOrGetter<(builder: LdoBuilder) => ReturnType>, + dataset: MaybeRefOrGetter, +): Ref; +export function useTrackingProxy( + shapeType: MaybeRefOrGetter>, + createLdo: MaybeRefOrGetter, + dataset: MaybeRefOrGetter, +): Ref; +export function useTrackingProxy( + shapeType: MaybeRefOrGetter>, + createLdo: MaybeRefOrGetter< + ((builder: LdoBuilder) => ReturnType) | undefined + >, + dataset: MaybeRefOrGetter, +): Ref; +export function useTrackingProxy( + shapeType: MaybeRefOrGetter>, + createLdo: MaybeRefOrGetter< + ((builder: LdoBuilder) => ReturnType) | undefined + >, + dataset: MaybeRefOrGetter, +): Ref { + const linkedDataObject = shallowRef() as Ref< + ReturnType | undefined + >; + + const forceUpdate = () => { + triggerRef(linkedDataObject); + }; + + watch( + [ + () => toValue(shapeType), + () => toValue(createLdo), + () => toValue(dataset), + ], + ([currentShapeType, currentCreateLdo, currentDataset], _, onCleanup) => { + onCleanup(() => { + currentDataset.removeListenerFromAllEvents(forceUpdate); + }); + + const builder = createTrackingProxyBuilder( + currentDataset, + currentShapeType, + forceUpdate, + ); + linkedDataObject.value = currentCreateLdo?.(builder); + }, + { immediate: true }, + ); + + return linkedDataObject; +} diff --git a/packages/vue/test/Composables.test.ts b/packages/vue/test/Composables.test.ts new file mode 100644 index 00000000..8b2fdc7d --- /dev/null +++ b/packages/vue/test/Composables.test.ts @@ -0,0 +1,924 @@ +import { describe, it, beforeEach, expect, vi } from "vitest"; +import { + createLdoVueMethods, + createUseMatchObject, + createUseResource, + createUseSubscribeToResource, + type UseResourceOptions, +} from "../src/index"; +import { + type SolidConnectedPlugin, + solidConnectedPlugin, + type SolidContainerUri, + type SolidLeafUri, +} from "@ldo/connected-solid"; +import { RerenderCount, waitForResource, withSetup } from "./test-utils"; +import assert from "node:assert"; +import { setupServer } from "@ldo/test-solid-server"; +import { nextTick, type Ref, ref } from "vue"; +import { FoafProfileShapeType } from "./_ldo/foafProfile.shapeTypes"; +import { BasicLdSet } from "@ldo/jsonld-dataset-proxy"; +import { literal, namedNode, quad } from "@ldo/rdf-utils"; +import { + ConnectedLdoTransactionDataset, + createConnectedLdoDataset, + type IConnectedLdoDataset, +} from "@ldo/connected"; + +// Only overwrite this if we are in test mode. This is an env var we happen to have in our repo +if (process.env.VITE_IS_TEST === "true") { + class ESBuildAndJSDOMCompatibleTextEncoder extends TextEncoder { + constructor() { + super(); + } + + encode(input: string) { + if (typeof input !== "string") { + throw new TypeError("`input` must be a string"); + } + + const decodedURI = decodeURIComponent(encodeURIComponent(input)); + const arr = new Uint8Array(decodedURI.length); + const chars = decodedURI.split(""); + for (let i = 0; i < chars.length; i++) { + arr[i] = decodedURI[i].charCodeAt(0); + } + return arr; + } + } + + Object.defineProperty(global, "TextEncoder", { + value: ESBuildAndJSDOMCompatibleTextEncoder, + writable: true, + }); +} + +describe("LDO Vue Composables", () => { + let methods: ReturnType>; + const personResourceUri = "http://localhost:3006/directory/person"; + const personUri = `${personResourceUri}#me`; + const otherPersonUri = `${personResourceUri}#i`; + const person2ResourceUri = "http://localhost:3006/directory/person2"; + const person2Uri = `${person2ResourceUri}#me`; + const foafName = namedNode("http://xmlns.com/foaf/0.1/name"); + const foafKnows = namedNode("http://xmlns.com/foaf/0.1/knows"); + + const s = setupServer(3006, { + slug: "directory/", + isContainer: true, + contains: [ + { + slug: "leaf", + isContainer: false, + data: " .", + mimeType: "text/turtle", + }, + { + slug: "person", + isContainer: false, + data: ` + @prefix foaf: . + <> a foaf:PersonalProfileDocument. + <#me> + a foaf:Person; + foaf:name "Name"; + foaf:knows , . + + <#i> a foaf:Person; foaf:name "myname". + `, + mimeType: "text/turtle", + }, + { + slug: "person2", + isContainer: false, + data: ` + @prefix foaf: . + <#me> a foaf:Person; foaf:name "Other Name"; foaf:knows <./person#me>. + `, + mimeType: "text/turtle", + }, + ], + }); + + beforeEach(() => { + methods = createLdoVueMethods([solidConnectedPlugin]); + methods.dataset.setContext("solid", { fetch: s.fetchMock }); + }); + + describe("useTrackingProxy", () => { + it.todo("should return a LinkedDataObject"); + }); + + describe("useResource", () => { + it("should fetch a resource by uri", async () => { + const [result, app] = withSetup(() => + methods.useResource("http://localhost:3006/directory/"), + ); + + expect(result.value.isFetched()).toBe(false); + + await waitForResource(result.value); + + expect(result.value.isFetched()).toBe(true); + + expect(result.value.type).toEqual("SolidContainer"); + + const headersResult = await result.value.getHeaders(); + expect(headersResult.isError).toBe(false); + + const rootContainerResult = await result.value.getRootContainer(); + + assert(!rootContainerResult.isError); + expect(rootContainerResult.uri).toEqual("http://localhost:3006/"); + + app.unmount(); + }); + + it("should fetch the new resource when uri changes", async () => { + const uriRef = ref("http://localhost:3006/directory/"); + const [result, app] = withSetup(() => methods.useResource(uriRef)); + + expect(result.value.isFetched()).toBe(false); + + await waitForResource(result.value); + + expect(result.value.isFetched()).toBe(true); + expect(result.value.type).toEqual("SolidContainer"); + + uriRef.value = "http://localhost:3006/directory/leaf"; + await nextTick(); + + expect(result.value.isFetched()).toBe(false); + expect(result.value.uri).toEqual("http://localhost:3006/directory/leaf"); + + await waitForResource(result.value); + + expect(result.value.isFetched()).toBe(true); + expect(result.value.type).toEqual("SolidLeaf"); + + app.unmount(); + }); + + it("should handle change in options", async () => { + const uriRef: Ref = ref( + "http://localhost:3006/directory/", + ); + const optionsRef: Ref> = ref({ + suppressInitialRead: true, + }); + const [result, app] = withSetup(() => + methods.useResource(uriRef, optionsRef), + ); + + expect(result.value.isFetched()).toBe(false); + + await new Promise((resolve) => setTimeout(resolve, 500)); + + expect(result.value.isFetched()).toBe(false); + + optionsRef.value = { suppressInitialRead: false }; + + await waitForResource(result.value); + + expect(result.value.isFetched()).toBe(true); + + expect(s.fetchMock).toHaveBeenCalledTimes(1); + + app.unmount(); + }); + }); + + describe("useSubject", () => { + it("should return a linked data object with given subject uri", async () => { + const [result, app] = withSetup(() => { + const useSubjectResult = methods.useSubject( + FoafProfileShapeType, + "http://localhost:3006/directory/person#me", + ); + const useResourceResult = methods.useResource( + "http://localhost:3006/directory/person", + ); + + return [useSubjectResult, useResourceResult] as const; + }); + + const rerenderCount = new RerenderCount(result); + + expect(rerenderCount.count).toEqual(0); + + await waitForResource(result[1].value); + + expect(rerenderCount.count).toEqual(1); + + assert(!result[1].value.isError); + expect(result[1].value.isAbsent()).toBe(false); + + expect(result[0].value.name).toEqual("Name"); + const knows = result[0].value.knows?.map((k) => k["@id"]); + expect(knows).toHaveLength(2); + expect(knows).toContain("https://example.com/profile/card#me"); + expect(knows).toContain("https://example.org/profile/card#i"); + + app.unmount(); + }); + + it("should change the LDO when the subject changes", async () => { + // s.fetchMock.mockClear(); + + const subject = ref("http://localhost:3006/directory/person#me"); + + expect(s.fetchMock).toHaveBeenCalledTimes(0); + + const [result, app] = withSetup(() => { + const useSubjectResult = methods.useSubject( + FoafProfileShapeType, + subject, + ); + + const useResourceResult = methods.useResource( + subject as Ref, + ); + + return [useSubjectResult, useResourceResult] as const; + }); + + const rerenderCount = new RerenderCount(result[0]); + + // inform the tracking proxy we're interested in this and check the result + // removing access to the property will lead to less renders + expect(result[0].value.name).toEqual(undefined); + + expect(rerenderCount.count).toEqual(0); + + await waitForResource(result[1].value); + + expect(result[0].value.name).toEqual("Name"); + expect(rerenderCount.count).toEqual(1); + + subject.value = "http://localhost:3006/directory/person#i"; + + // this should not trigger refetch + await nextTick(); + + expect(result[1].value.isFetched()).toBe(true); + expect(rerenderCount.count).toEqual(2); + expect(result[0].value.name).toEqual("myname"); + + subject.value = "http://localhost:3006/directory/person2#me"; + + // this triggers refetch + await nextTick(); + expect(result[1].value.isFetched()).toBe(false); + expect(result[0].value.name).toEqual(undefined); + expect(rerenderCount.count).toEqual(3); + + await waitForResource(result[1].value); + + expect(result[1].value.isFetched()).toBe(true); + expect(result[1].value.isAbsent()).toBe(false); + expect(result[0].value.name).toEqual("Other Name"); + expect(rerenderCount.count).toEqual(4); + + app.unmount(); + }); + + it.todo( + "should change the LDO when the shape type changes (this will probably ruin types though)", + ); + it.todo("should change the LDO when the options.dataset changes"); + }); + + describe("useMatchSubject", () => { + it("should return a LdSet of matched linked data objects", async () => { + const [result, app] = withSetup(() => { + const useMatchSubjectResult = methods.useMatchSubject( + FoafProfileShapeType, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + "http://xmlns.com/foaf/0.1/Person", + ); + const useResourceResult = methods.useResource( + "http://localhost:3006/directory/person", + ); + + return [useMatchSubjectResult, useResourceResult] as const; + }); + + await waitForResource(result[1].value); + + assert(!result[1].value.isError); + expect(result[1].value.isAbsent()).toBe(false); + + expect(result[0].value).toBeInstanceOf(BasicLdSet); + expect(result[0].value.size).toBe(2); + expect(result[0].value.map((v) => v["@id"])).toContain( + "http://localhost:3006/directory/person#me", + ); + expect(result[0].value.map((v) => v["@id"])).toContain( + "http://localhost:3006/directory/person#i", + ); + + app.unmount(); + }); + it.todo( + "should change the LDO set when the shape type changes (this will probably ruin types though)", + ); + it.todo("should change the LDO set when the options.dataset changes"); + it.todo("should change the LDO set when predicate changes"); + + it("should change the LDO set when object changes", async () => { + const object = ref("http://xmlns.com/foaf/0.1/Person"); + + const [result, app] = withSetup(() => { + const useMatchSubjectResult = methods.useMatchSubject( + FoafProfileShapeType, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + object, + undefined, + ); + const useResourceResult = methods.useResource( + "http://localhost:3006/directory/person", + ); + + return [useMatchSubjectResult, useResourceResult] as const; + }); + + const rerenderCount = new RerenderCount(result); + + expect(result[0].value.size).toBe(0); + + await waitForResource(result[1].value); + + expect(rerenderCount.count).toBe(2); + + assert(!result[1].value.isError); + expect(result[1].value.isAbsent()).toBe(false); + + expect(result[0].value).toBeInstanceOf(BasicLdSet); + expect(result[0].value.size).toBe(2); + expect(result[0].value.map((v) => v["@id"])).toContain( + "http://localhost:3006/directory/person#me", + ); + expect(result[0].value.map((v) => v["@id"])).toContain( + "http://localhost:3006/directory/person#i", + ); + + object.value = "http://xmlns.com/foaf/0.1/PersonalProfileDocument"; + await nextTick(); + expect(result[0].value.size).toBe(1); + + result[0].value.forEach((v) => { + expect(v["@id"]).toBe("http://localhost:3006/directory/person"); + }); + + // TODO fix, it does update in production though + expect(rerenderCount.count).toBe(3); + + app.unmount(); + }); + + it.todo("should change the LDO set when graph changes"); + }); + + describe("useMatchObject", () => { + it("should return a LdSet of matched linked data objects", async () => { + const [result, app] = withSetup(() => { + const useMatchObjectResult = methods.useMatchObject( + FoafProfileShapeType, + "http://localhost:3006/directory/person#me", + "http://xmlns.com/foaf/0.1/knows", + "http://localhost:3006/directory/person", + ); + const useResourceResult = methods.useResource( + "http://localhost:3006/directory/person", + ); + + return [useMatchObjectResult, useResourceResult] as const; + }); + + await waitForResource(result[1].value); + + assert(!result[1].value.isError); + expect(result[1].value.isAbsent()).toBe(false); + + const friends = result[0].value; + expect(friends.size).toBe(2); + expect(friends.map((v) => v["@id"])).toContain( + "https://example.com/profile/card#me", + ); + expect(friends.map((v) => v["@id"])).toContain( + "https://example.org/profile/card#i", + ); + + app.unmount(); + }); + + it.todo("should change the LDO set when the shape type changes"); + it.todo("should change the LDO set when the options.dataset changes"); + it.todo("should change the LDO set when subject changes"); + it.todo("should change the LDO set when predicate changes"); + it.todo("should change the LDO set when graph changes"); + }); + + describe("useSubscribeToResource", () => { + it("should subscribe to resource changes (detect resource changes)", async () => { + // subscribe + const [result, app] = withSetup( + () => + [ + methods.useSubscribeToResource([personResourceUri]), + methods.useSubject(FoafProfileShapeType, personUri), + ] as const, + ); + + void result[1].value.name; + const rerenderCount = new RerenderCount(result[1]); + + // change the resource + const resource = methods.dataset.getResource(personResourceUri); + + await vi.waitFor(() => { + expect(resource.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.isSubscribedToNotifications()).toBe(true); + }); + // check that the change has been detected + rerenderCount.clearCount(); + const changeResult = await s.authFetch(personResourceUri, { + method: "PATCH", + headers: { "content-type": "text/n3" }, + body: ` + @prefix solid: . + @prefix foaf: . + + _:patch a solid:InsertDeletePatch; + solid:inserts { <#me> foaf:name "NEW NAME". }; + solid:deletes { <#me> foaf:name ?name. }; + solid:where { <#me> foaf:name ?name. }.`, + }); + expect(changeResult.ok).toBe(true); + await nextTick(); + // refetch should be going on + await vi.waitFor(() => { + expect(resource.isLoading()).toBe(true); + }); + // and then done + await vi.waitFor(() => expect(resource.isLoading()).toBe(false)); + expect(resource.isError).toBe(false); + // and expect refreshed data + expect(result[1].value.name).toEqual("NEW NAME"); + await nextTick(); + // expect(rerenderCount.count).toEqual(1); + + app.unmount(); + }); + + it("should unsubscribe from resources removed", async () => { + const urlRef = ref([personResourceUri]); + // subscribe + const [, app] = withSetup( + () => + [ + methods.useSubscribeToResource(urlRef), + methods.useSubject(FoafProfileShapeType, personUri), + ] as const, + ); + + const resource = methods.dataset.getResource(personResourceUri); + await vi.waitFor(() => { + expect(resource.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.isSubscribedToNotifications()).toBe(true); + }); + + // remove resource from subscription + urlRef.value = []; + + // and check that it's not subscribed, eventually + await vi.waitFor(() => { + expect(resource.isSubscribedToNotifications()).toBe(false); + }); + + app.unmount(); + }); + + it("should unsubscribe from resources when finished", async () => { + // subscribe + const [, app] = withSetup( + () => + [ + methods.useSubscribeToResource([personResourceUri]), + methods.useSubject(FoafProfileShapeType, personUri), + ] as const, + ); + + const resource = methods.dataset.getResource(personResourceUri); + await vi.waitFor(() => { + expect(resource.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.isSubscribedToNotifications()).toBe(true); + }); + + // unmount + app.unmount(); + + // and check that the resource is not subscribed, eventually + await vi.waitFor(() => { + expect(resource.isSubscribedToNotifications()).toBe(false); + }); + }); + }); + + describe("useLinkQuery", () => { + it("should resolve the query", async () => { + const [result, app] = withSetup(() => + methods.useLinkQuery( + FoafProfileShapeType, + person2ResourceUri, + person2Uri, + { name: true, knows: { name: true, "@id": true } }, + ), + ); + + const person2Resource = methods.dataset.getResource(person2ResourceUri); + const personResource = methods.dataset.getResource(personResourceUri); + + await vi.waitFor(() => { + expect(person2Resource.isFetched()).toBe(true); + expect(personResource.isFetched()).toBe(true); + }); + + // test result + expect(result.value.knows?.map((k) => k.name)).toEqual(["Name"]); + + // wait for resources to be subscribed to notifications + await vi.waitFor(() => { + expect(person2Resource.isSubscribedToNotifications()).toBe(true); + expect(personResource.isSubscribedToNotifications()).toBe(true); + }); + + app.unmount(); + + // wait for the resources to be unsubscribed from notifications after unmount + await vi.waitFor(() => { + expect(person2Resource.isSubscribedToNotifications()).toBe(false); + expect(personResource.isSubscribedToNotifications()).toBe(false); + }); + }); + it.todo("should update the query when a resource changes"); + }); + + describe("useChangeDataset", () => { + it("should accept and commit changes to a default dataset", async () => { + const [results, app] = withSetup(() => { + const changeDataset = methods.useChangeDataset(); + const resource = methods.useResource(personResourceUri); + methods.useSubscribeToResource([personResourceUri]); + + return { changeDataset, resource }; + }); + + expect(results.changeDataset.transactionDataset.value).toBeInstanceOf( + ConnectedLdoTransactionDataset, + ); + + // make sure the resource is fetched before proceeding + await vi.waitFor(() => { + expect(results.resource.value.isFetched()).toBe(true); + }); + // make sure resource will update + await vi.waitFor(() => { + expect(results.resource.value.isSubscribedToNotifications()).toBe(true); + }); + + // change person's name + results.changeDataset.setData((dataset) => { + dataset.deleteMatches(namedNode(personUri), foafName); + dataset.add( + quad( + namedNode(personUri), + foafName, + literal("New Saved Name"), + namedNode(personResourceUri), + ), + ); + }); + + expect( + results.changeDataset.transactionDataset.value.getChanges().added?.size, + ).toEqual(1); + expect( + results.changeDataset.transactionDataset.value.getChanges().removed + ?.size, + ).toEqual(1); + const result = await results.changeDataset.commitData(); + expect(result.isError).toBe(false); + // transaction dataset should be reset at this point + expect( + results.changeDataset.transactionDataset.value.getChanges(), + ).toEqual({}); + + // the resource should reload (thanks to subscription) + await vi.waitFor(() => { + expect(results.resource.value.isLoading()).toBe(true); + }); + await vi.waitFor(() => { + expect(results.resource.value.isLoading()).toBe(false); + }); + + const names = methods.dataset.match(namedNode(personUri), foafName); + + // and the name should be updated + expect(names.size).toBe(1); + expect(names.toArray()[0].object.value).toBe("New Saved Name"); + + app.unmount(); + + // let's just wait for unsubscribing at the end + await vi.waitFor(() => { + expect(results.resource.value.isLoading()).toBe(false); + }); + }); + + it.todo("should handle changes to custom dataset"); + it.todo("should handle error in saving"); + }); + + describe("useChangeSubject", () => { + it("should accept and commit changes to a default dataset", async () => { + const [{ changeDataset, resource, subject, otherSubject }, app] = + withSetup(() => { + const changeDataset = methods.useChangeSubject( + FoafProfileShapeType, + personUri, + ); + const resource = methods.useResource(personResourceUri); + const subject = methods.useSubject(FoafProfileShapeType, personUri); + const otherSubject = methods.useSubject( + FoafProfileShapeType, + otherPersonUri, + ); + return { changeDataset, resource, subject, otherSubject }; + }); + + await vi.waitFor(() => { + expect(resource.value.isFetched()).toBe(true); + }); + + changeDataset.setData(resource.value, (profile) => { + profile.name = "ANOTHER NAME"; + }); + changeDataset.setData( + resource.value, + (profile) => { + profile.name = "OTHER PERSON NAME"; + }, + otherSubject.value, + ); + + const result = await changeDataset.commitData(); + expect(result.isError).toBe(false); + expect(subject.value.name).toEqual("ANOTHER NAME"); + expect(otherSubject.value.name).toEqual("OTHER PERSON NAME"); + + app.unmount(); + }); + + it.todo("should accept and commit changes to a custom dataset"); + it.todo("should handle error in saving"); + }); + + describe("useChangeMatchObject", () => { + it("should accept and commit changes to a default dataset", async () => { + const [{ changeDataset, resource, resource2, objects, subject }, app] = + withSetup(() => { + const changeDataset = methods.useChangeMatchObject( + FoafProfileShapeType, + person2Uri, + foafKnows, + ); + const resource = methods.useResource(personResourceUri); + const resource2 = methods.useResource(person2ResourceUri); + const subject = methods.useSubject(FoafProfileShapeType, person2Uri); + const objects = methods.useMatchObject( + FoafProfileShapeType, + person2Uri, + foafKnows, + ); + methods.useSubscribeToResource([ + personResourceUri, + person2ResourceUri, + ]); + return { changeDataset, resource, resource2, objects, subject }; + }); + + await vi.waitFor(() => { + expect(resource.value.isFetched()).toBe(true); + expect(resource2.value.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(true); + expect(resource2.value.isSubscribedToNotifications()).toBe(true); + }); + + changeDataset.setData(resource.value, (friends) => { + expect(friends.size).toEqual(1); + friends.forEach((person) => { + person.name = "ANOTHER NAME"; + }); + }); + + // we can also edit other subjects and graphs (resources) within the same transaction + changeDataset.setData( + resource2.value, + (person) => { + person.name = "PERSON 2 NAME"; + }, + subject.value, + ); + + const result = await changeDataset.commitData(); + expect(result.isError).toBe(false); + + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(true); + expect(resource2.value.isLoading()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(false); + expect(resource2.value.isLoading()).toBe(false); + }); + + expect(objects.value.size).toBe(1); + expect(objects.value.toArray()[0].name).toEqual("ANOTHER NAME"); + expect(subject.value.name).toEqual("PERSON 2 NAME"); + + app.unmount(); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(false); + expect(resource2.value.isSubscribedToNotifications()).toBe(false); + }); + }); + + it("should accept and commit changes to a custom dataset", async () => { + const otherDataset = createConnectedLdoDataset([ + solidConnectedPlugin, + ]) as IConnectedLdoDataset<[SolidConnectedPlugin]>; + otherDataset.setContext("solid", { fetch: s.fetchMock }); + const useResource = createUseResource(otherDataset); + const useMatchObject = createUseMatchObject(otherDataset); + const useSubscribeToResource = createUseSubscribeToResource(otherDataset); + const [{ changeDataset, resource, resource2, objects }, app] = withSetup( + () => { + const changeDataset = methods.useChangeMatchObject( + FoafProfileShapeType, + person2Uri, + foafKnows, + undefined, + { + dataset: otherDataset as IConnectedLdoDataset< + [SolidConnectedPlugin] + >, + }, + ); + const resource = useResource(personResourceUri); + const resource2 = useResource(person2ResourceUri); + const objects = useMatchObject( + FoafProfileShapeType, + person2Uri, + foafKnows, + ); + useSubscribeToResource([personResourceUri, person2ResourceUri]); + return { changeDataset, resource, resource2, objects }; + }, + ); + + await vi.waitFor(() => { + expect(resource.value.isFetched()).toBe(true); + expect(resource2.value.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(true); + expect(resource2.value.isSubscribedToNotifications()).toBe(true); + }); + + changeDataset.setData(resource.value, (friends) => { + friends.forEach((person) => { + person.name = "ANOTHER NAME"; + }); + }); + + const result = await changeDataset.commitData(); + expect(result.isError).toBe(false); + + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(false); + }); + + expect(objects.value.size).toBe(1); + expect(objects.value.toArray()[0].name).toEqual("ANOTHER NAME"); + + app.unmount(); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(false); + expect(resource2.value.isSubscribedToNotifications()).toBe(false); + }); + }); + + it.todo("should handle error in saving"); + }); + + describe("useChangeMatchSubject", () => { + it("should accept and commit changes to a default dataset", async () => { + const [{ changeDataset, resource, resource2, subjects, subject2 }, app] = + withSetup(() => { + // people in first resource who have friends + const changeDataset = methods.useChangeMatchSubject( + FoafProfileShapeType, + foafKnows, + undefined, + namedNode(personResourceUri), + ); + const resource = methods.useResource(personResourceUri); + const resource2 = methods.useResource(person2ResourceUri); + const subject2 = methods.useSubject(FoafProfileShapeType, person2Uri); + const subjects = methods.useMatchSubject( + FoafProfileShapeType, + foafKnows, + undefined, + namedNode(personResourceUri), + ); + methods.useSubscribeToResource([ + personResourceUri, + person2ResourceUri, + ]); + return { + changeDataset, + resource, + resource2, + subjects, + subject2, + }; + }); + + await vi.waitFor(() => { + expect(resource.value.isFetched()).toBe(true); + expect(resource2.value.isFetched()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(true); + expect(resource2.value.isSubscribedToNotifications()).toBe(true); + }); + + changeDataset.setData(resource.value, (people) => { + // expect(people.size).toEqual(1); // weirdly, this fails! TODO + let count = 0; + people.forEach((person) => { + count++; + person.name = "ANOTHER NAME"; + }); + expect(count).toBe(1); + }); + + // we can also edit other subjects and graphs (resources) within the same transaction + changeDataset.setData( + resource2.value, + (person) => { + person.name = "PERSON 2 NAME"; + }, + subject2.value, + ); + + const result = await changeDataset.commitData(); + expect(result.isError).toBe(false); + + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(true); + expect(resource2.value.isLoading()).toBe(true); + }); + await vi.waitFor(() => { + expect(resource.value.isLoading()).toBe(false); + expect(resource2.value.isLoading()).toBe(false); + }); + + // expect(subjects.value.size).toBe(1); // TODO weirdly, this fails + expect(subjects.value.toArray()).toHaveLength(1); + expect(subjects.value.toArray()[0].name).toEqual("ANOTHER NAME"); + expect(subject2.value.name).toEqual("PERSON 2 NAME"); + + app.unmount(); + await vi.waitFor(() => { + expect(resource.value.isSubscribedToNotifications()).toBe(false); + expect(resource2.value.isSubscribedToNotifications()).toBe(false); + }); + }); + + it.todo("should accept and commit changes to a custom dataset"); + it.todo("should handle error in saving"); + }); +}); diff --git a/packages/vue/test/_ldo/foafProfile.context.ts b/packages/vue/test/_ldo/foafProfile.context.ts new file mode 100644 index 00000000..e7a57dfc --- /dev/null +++ b/packages/vue/test/_ldo/foafProfile.context.ts @@ -0,0 +1,48 @@ +import type { LdoJsonldContext } from "@ldo/ldo"; + +/** + * ============================================================================= + * foafProfileContext: JSONLD Context for foafProfile + * ============================================================================= + */ +export const foafProfileContext: LdoJsonldContext = { + type: { + "@id": "@type", + "@isCollection": true, + }, + Person: { + "@id": "http://xmlns.com/foaf/0.1/Person", + "@context": { + type: { + "@id": "@type", + "@isCollection": true, + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, + }, + }, + name: { + "@id": "http://xmlns.com/foaf/0.1/name", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + img: { + "@id": "http://xmlns.com/foaf/0.1/img", + "@type": "http://www.w3.org/2001/XMLSchema#string", + }, + knows: { + "@id": "http://xmlns.com/foaf/0.1/knows", + "@type": "@id", + "@isCollection": true, + }, +}; diff --git a/packages/vue/test/_ldo/foafProfile.schema.ts b/packages/vue/test/_ldo/foafProfile.schema.ts new file mode 100644 index 00000000..25243cde --- /dev/null +++ b/packages/vue/test/_ldo/foafProfile.schema.ts @@ -0,0 +1,97 @@ +import type { Schema } from "shexj"; + +/** + * ============================================================================= + * foafProfileSchema: ShexJ Schema for foafProfile + * ============================================================================= + */ +export const foafProfileSchema: Schema = { + type: "Schema", + shapes: [ + { + id: "https://example.com/FoafProfile", + type: "ShapeDecl", + shapeExpr: { + type: "Shape", + expression: { + type: "EachOf", + expressions: [ + { + type: "TripleConstraint", + predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + valueExpr: { + type: "NodeConstraint", + values: ["http://xmlns.com/foaf/0.1/Person"], + }, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Defines the node as a Person (from foaf)", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/name", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Define a person's name.", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/img", + valueExpr: { + type: "NodeConstraint", + datatype: "http://www.w3.org/2001/XMLSchema#string", + }, + min: 0, + max: 1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: "Photo link but in string form", + }, + }, + ], + }, + { + type: "TripleConstraint", + predicate: "http://xmlns.com/foaf/0.1/knows", + valueExpr: "https://example.com/FoafProfile", + min: 0, + max: -1, + annotations: [ + { + type: "Annotation", + predicate: "http://www.w3.org/2000/01/rdf-schema#comment", + object: { + value: + "A list of WebIds for all the people this user knows.", + }, + }, + ], + }, + ], + }, + extra: ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"], + }, + }, + ], +}; diff --git a/packages/vue/test/_ldo/foafProfile.shapeTypes.ts b/packages/vue/test/_ldo/foafProfile.shapeTypes.ts new file mode 100644 index 00000000..2c52ee86 --- /dev/null +++ b/packages/vue/test/_ldo/foafProfile.shapeTypes.ts @@ -0,0 +1,19 @@ +import type { ShapeType } from "@ldo/ldo"; +import { foafProfileSchema } from "./foafProfile.schema"; +import { foafProfileContext } from "./foafProfile.context"; +import type { FoafProfile } from "./foafProfile.typings"; + +/** + * ============================================================================= + * LDO ShapeTypes foafProfile + * ============================================================================= + */ + +/** + * FoafProfile ShapeType + */ +export const FoafProfileShapeType: ShapeType = { + schema: foafProfileSchema, + shape: "https://example.com/FoafProfile", + context: foafProfileContext, +}; diff --git a/packages/vue/test/_ldo/foafProfile.typings.ts b/packages/vue/test/_ldo/foafProfile.typings.ts new file mode 100644 index 00000000..16843881 --- /dev/null +++ b/packages/vue/test/_ldo/foafProfile.typings.ts @@ -0,0 +1,33 @@ +import type { LdoJsonldContext, LdSet } from "@ldo/ldo"; + +/** + * ============================================================================= + * Typescript Typings for foafProfile + * ============================================================================= + */ + +/** + * FoafProfile Type + */ +export interface FoafProfile { + "@id"?: string; + "@context"?: LdoJsonldContext; + /** + * Defines the node as a Person (from foaf) + */ + type: LdSet<{ + "@id": "Person"; + }>; + /** + * Define a person's name. + */ + name?: string; + /** + * Photo link but in string form + */ + img?: string; + /** + * A list of WebIds for all the people this user knows. + */ + knows?: LdSet; +} diff --git a/packages/vue/test/test-utils.ts b/packages/vue/test/test-utils.ts new file mode 100644 index 00000000..0ec07079 --- /dev/null +++ b/packages/vue/test/test-utils.ts @@ -0,0 +1,63 @@ +import type { Resource } from "@ldo/connected"; +import { vi } from "vitest"; +import { + createApp, + type MultiWatchSources, + watch, + type App, + type WatchSource, +} from "vue"; + +/** + * https://vuejs.org/guide/scaling-up/testing.html#testing-composables + */ +export function withSetup(composable: () => T): [T, App] { + let result!: T; + const app = createApp({ + setup() { + result = composable(); + // suppress missing template warning + return () => {}; + }, + }); + app.mount(document.createElement("div")); + // return the result and the app instance + // for testing provide/unmount + return [result, app]; +} + +/** + * This counts how many times the reference or references updated. + * Constructor accepts any parameter that fits vue's watch source/sources parameter. + * + * https://vuejs.org/api/reactivity-core.html#watch + */ +type AllowedSources = + | WatchSource + | MultiWatchSources + | Readonly; +export class RerenderCount { + private _count = 0; + + constructor(sources: AllowedSources) { + watch(sources, () => { + this._count++; + }); + } + + public get count() { + return this._count; + } + + public clearCount() { + this._count = 0; + } +} + +export async function waitForResource(resource: Resource) { + await vi.waitFor(() => { + if (!resource.isFetched()) { + throw new Error("not fetched yet"); + } + }); +} diff --git a/packages/vue/tsconfig.esm.json b/packages/vue/tsconfig.esm.json new file mode 100644 index 00000000..77952ca6 --- /dev/null +++ b/packages/vue/tsconfig.esm.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.esm.json", + "compilerOptions": { + "outDir": "./dist/esm", + "rootDir": "./src", + "declarationDir": "./dist/types" + }, + "include": ["src"] +} diff --git a/packages/vue/tsdown.config.ts b/packages/vue/tsdown.config.ts new file mode 100644 index 00000000..7f0dfbd9 --- /dev/null +++ b/packages/vue/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm", "cjs"], + target: "es2021", + unbundle: true, + dts: true, + clean: true, + outDir: "dist", + tsconfig: "tsconfig.esm.json", + skipNodeModulesBundle: true, +}); diff --git a/packages/vue/vitest.config.js b/packages/vue/vitest.config.js new file mode 100644 index 00000000..cc93abb3 --- /dev/null +++ b/packages/vue/vitest.config.js @@ -0,0 +1,29 @@ +import { defineConfig } from "vitest/config"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + plugins: [], + resolve: { + alias: [ + { + find: /^@ldo\/([^/]+)$/, + replacement: path.resolve(__dirname, "../$1/src/index.ts"), + }, + { + find: /^@ldo\/([^/]+)\/(.*)$/, + replacement: path.resolve(__dirname, "../$1/$2"), + }, + ], + }, + test: { + globals: true, + setupFiles: ["../../vitest.jest-compat.ts"], + coverage: { + provider: "istanbul", + }, + environment: "jsdom", + }, +}); diff --git a/tsconfig.base.json b/tsconfig.base.json index ed5af721..abc54da4 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2021", "module": "commonjs", - "lib": ["ES2021", "DOM"], + "lib": ["ES2021", "DOM", "DOM.Iterable"], "strict": true, "esModuleInterop": true, "moduleResolution": "node", diff --git a/vitest.jest-compat.ts b/vitest.jest-compat.ts index 130a1690..4c9922e0 100644 --- a/vitest.jest-compat.ts +++ b/vitest.jest-compat.ts @@ -2,3 +2,24 @@ import { vi } from "vitest"; // Keep legacy Jest test helpers working during migration. globalThis.jest = vi as unknown as Record; + +// vitest and jsdom don't play well together +// browser and Node have different Uint8Array instances, leading to error in Jose library. +// here, we loosen the instanceof Uint8Array check. +if (typeof globalThis !== "undefined") { + // Keep Node's Uint8Array for reference + const NodeUint8Array = globalThis.__node_uint8array__ ?? Uint8Array; + globalThis.__node_uint8array__ ??= NodeUint8Array; + + // overwrite the instanceof check with a more loose check so both instances pass the comparison + Object.defineProperty(NodeUint8Array, Symbol.hasInstance, { + value(instance: unknown) { + if (!instance) return false; + return ( + instance.constructor?.name === "Uint8Array" || + Object.prototype.toString.call(instance) === "[object Uint8Array]" + ); + }, + configurable: true, + }); +}