diff --git a/package.json b/package.json index c52e16d..1ec8e01 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,10 @@ "./vite": { "types": "./dist/vite/index.d.mts", "import": "./dist/vite/index.mjs" + }, + "./testing": { + "types": "./dist/testing/index.d.mts", + "import": "./dist/testing/index.mjs" } }, "scripts": { @@ -46,14 +50,16 @@ "solid-ui": "dev", "typescript": "^6.0.3", "vite": "npm:@voidzero-dev/vite-plus-core@latest", - "vite-plus": "^0.1.20" + "vite-plus": "^0.1.20", + "vitest": "^4.0.0" }, "peerDependencies": { "pane-registry": "^3.1.1 || ^3.1.2-0", "rdflib": "^2.3.6", "solid-logic": "^4.0.7 || ^4.0.8-0", "solid-ui": "^3.1.2 || ^3.1.3-0", - "vite": "^8.0.0" + "vite": "^8.0.0", + "vitest": "^4.0.0" }, "peerDependenciesMeta": { "pane-registry": { @@ -70,6 +76,9 @@ }, "vite": { "optional": true + }, + "vitest": { + "optional": true } }, "packageManager": "pnpm@11.7.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82058ec..85a3198 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -81,6 +81,9 @@ importers: vite-plus: specifier: ^0.1.20 version: 0.1.24(@tsdown/css@0.22.1)(@types/node@24.13.2)(@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3))(typescript@6.0.3) + vitest: + specifier: ^4.0.0 + version: 4.1.9(@types/node@24.13.2)(@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)) packages: @@ -1316,6 +1319,35 @@ packages: '@uvdsl/solid-oidc-client-browser@0.2.3': resolution: {integrity: sha512-WzVlxv46EUSoqm7ovsWJRZq8KEI/CdpA9O1fXoiP8bihs2cNxPnet3YcqvIYWYMsTrf0zsR031l5s/BzQ9MEgA==} + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + '@voidzero-dev/vite-plus-core@0.1.24': resolution: {integrity: sha512-iXPGBABnQnrDMx89H6MOCGcTZp+QW+3rY4YMVKdE6ydchSvPk2O3MI2vgaRVfOtWJ2IjnxSnf1n2yjP67ZBRFQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1616,6 +1648,10 @@ packages: resolution: {integrity: sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==} engines: {node: '>=0.10.0'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -1716,6 +1752,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.2.0: + resolution: {integrity: sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==} + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1744,6 +1783,10 @@ packages: exec-limiter@3.2.14: resolution: {integrity: sha512-ZQjJmAnXD+1kQ6ejMZAS5Vxdt7LLMz0Eq7mEu6+7NhlauykuyLihhUkpp4S784QKsmJQIpuuERhQ8Tav8bF3zQ==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + exsolve@1.1.0: resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} @@ -2346,6 +2389,9 @@ packages: setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} @@ -2386,6 +2432,9 @@ packages: spdx-license-ids@3.0.23: resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} @@ -2429,6 +2478,10 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + tmp@0.0.28: resolution: {integrity: sha512-c2mmfiBmND6SOVxzogm1oda0OJ1HZVIk/5n26N59dDTh80MUeavpiCls4PGAdkX1PFkKokLpcf7prSjCeXLsJg==} engines: {node: '>=0.4.0'} @@ -2596,6 +2649,47 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} @@ -2615,6 +2709,11 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + ws@8.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} @@ -3838,6 +3937,47 @@ snapshots: dependencies: jose: 5.10.0 + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: '@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)' + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.9': {} + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + '@voidzero-dev/vite-plus-core@0.1.24(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)': dependencies: '@oxc-project/runtime': 0.133.0 @@ -4019,6 +4159,8 @@ snapshots: capture-stack-trace@1.0.2: {} + chai@6.2.2: {} + compare-versions@6.1.1: {} composed-offset-position@0.0.6(@floating-ui/utils@0.2.11): @@ -4094,6 +4236,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.2.0: {} + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -4115,6 +4259,8 @@ snapshots: limit-it: 3.2.11 typpy: 2.4.0 + expect-type@1.4.0: {} + exsolve@1.1.0: {} fdir@6.5.0(picomatch@4.0.4): @@ -4734,6 +4880,8 @@ snapshots: setimmediate@1.0.5: {} + siginfo@2.0.0: {} + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 @@ -4794,6 +4942,8 @@ snapshots: spdx-license-ids@3.0.23: {} + stackback@0.0.2: {} + std-env@4.1.0: {} string_decoder@1.1.1: @@ -4825,6 +4975,8 @@ snapshots: tinypool@2.1.0: {} + tinyrainbow@3.1.0: {} + tmp@0.0.28: dependencies: os-tmpdir: 1.0.2 @@ -5001,6 +5153,33 @@ snapshots: - vite - yaml + vitest@4.1.9(@types/node@24.13.2)(@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.2.0 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: '@voidzero-dev/vite-plus-core@0.2.1(@tsdown/css@0.22.1)(@types/node@24.13.2)(typescript@6.0.3)' + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.13.2 + transitivePeerDependencies: + - msw + vscode-uri@3.1.0: {} w-json@1.3.10: {} @@ -5016,6 +5195,11 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + ws@8.21.0: {} yallist@3.1.1: {} diff --git a/src/pane-sandbox.ts b/src/pane-sandbox.ts index b04fea3..d7ca455 100644 --- a/src/pane-sandbox.ts +++ b/src/pane-sandbox.ts @@ -1,6 +1,6 @@ import type { DataBrowserContext, PaneDefinition } from 'pane-registry'; import { sym } from 'rdflib'; -import type { NamedNode } from 'rdflib'; +import type { Fetcher, NamedNode } from 'rdflib'; import { authn, solidLogicSingleton, store } from 'solid-logic'; import type { PaneSandboxPluginOptions } from './vite/plugins/pane-sandbox'; @@ -8,6 +8,7 @@ import type { PaneSandboxPluginOptions } from './vite/plugins/pane-sandbox'; import 'solid-ui/theme.css'; import 'solid-ui/components/provider'; import 'solid-ui/components/account'; +import 'solid-ui/components/button'; import './styles/legacy-global.css'; import './styles/legacy-utilities.css'; @@ -58,29 +59,65 @@ function configureFetcher(context: DataBrowserContext) { return fetcher; } -export default async function (pane: PaneDefinition, options: PaneSandboxPluginOptions) { - const subject = options.subject; - const context = createSandboxContext(pane); - const fetcher = configureFetcher(context); +function readSubjectFromUrl(): string | null { + return new URL(window.location.href).searchParams.get('subject'); +} - await authn.checkUser(); +function writeSubjectToUrl(subject: string) { + const url = new URL(window.location.href); + url.searchParams.set('subject', subject); + window.history.replaceState(null, '', url); +} + +async function renderPane( + fetcher: Fetcher, + subject: string, + pane: PaneDefinition, + context: DataBrowserContext, +) { try { - await fetcher?.load(subject); + await fetcher.load(subject); } catch (error) { console.error(error); - console.warn('Initial load failed, rendering anyway...'); + console.warn(`Failed loading subject ${subject}, rendering anyway...`); } - const main = document.querySelector('main'); + const main = document.querySelector('main') as HTMLElement; + const paneSubject = document.getElementById('pane-subject') as HTMLAnchorElement; - if (!main) { - alert('Sandbox main element not found!'); + paneSubject.textContent = subject; + paneSubject.href = subject; - return; - } + main.replaceChildren(pane.render(sym(subject), context)); +} + +export default async function (pane: PaneDefinition, options: PaneSandboxPluginOptions) { + let subject = readSubjectFromUrl() ?? options.subject; + const context = createSandboxContext(pane); + const fetcher = configureFetcher(context); + const header = document.querySelector('header') as HTMLElement; + const changeSubject = document.getElementById('pane-subject-change') as HTMLElement; + const loading = document.getElementById('loading') as HTMLElement; + const paneName = document.getElementById('pane-name') as HTMLElement; + + await authn.checkUser(); + await renderPane(fetcher, subject, pane, context); + + header.classList.remove('-translate-y-full'); + loading.style.opacity = '0'; + paneName.textContent = pane.name; + changeSubject.addEventListener('click', async () => { + const newSubject = prompt('Enter new subject:', subject); + + if (!newSubject) { + return; + } - const rendered = pane.render(sym(subject), context); + subject = newSubject; + writeSubjectToUrl(subject); + await renderPane(fetcher, subject, pane, context); + }); - main.replaceChildren(rendered); + setTimeout(() => loading.remove(), 300); } diff --git a/src/templates/sandbox.html b/src/templates/sandbox.html index 7dbe59a..0cf9ffe 100644 --- a/src/templates/sandbox.html +++ b/src/templates/sandbox.html @@ -8,11 +8,77 @@