diff --git a/.mise/tasks/bench b/.mise/tasks/bench new file mode 100755 index 0000000..b2abbce --- /dev/null +++ b/.mise/tasks/bench @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +#MISE description="Benchmark raw backends, direct adapters, and filesystem facade overhead." +set -euo pipefail + +deno ci +deno task bench:memory +deno task bench:deno +deno task bench:deno-kv +deno task bench:node +deno task bench:sqlite +deno task bench:bun diff --git a/.mise/tasks/bench-providers b/.mise/tasks/bench-providers new file mode 100755 index 0000000..d6afc31 --- /dev/null +++ b/.mise/tasks/bench-providers @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +#MISE description="Benchmark official SDK baselines, direct clients, adapters, and facades against local providers." +set -euo pipefail + +COMPOSE="tests/provider/compose.yml" +docker compose -f "$COMPOSE" up -d +cleanup() { + docker compose -f "$COMPOSE" down -v --remove-orphans +} +trap cleanup EXIT + +wait_for_http() { + local name="$1" + local url="$2" + local status="000" + for _ in $(seq 1 60); do + status="$(curl -sS -o /dev/null -w '%{http_code}' "$url" 2>/dev/null || true)" + if [[ "$status" != "000" ]]; then return 0; fi + sleep 1 + done + docker compose -f "$COMPOSE" logs "$name" >&2 || true + return 1 +} + +wait_for_http s3 http://127.0.0.1:8333/ +wait_for_http azure http://127.0.0.1:10000/devstoreaccount1 + +deno ci +deno run -A bench/provider.bench.ts +bun run bench/bun-provider.bench.ts diff --git a/.mise/tasks/bench/files b/.mise/tasks/bench/files deleted file mode 100755 index bfd872a..0000000 --- a/.mise/tasks/bench/files +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -#MISE description="Benchmark native positional writes and record-backed update costs." -set -euo pipefail -deno run -A bench/files.ts diff --git a/.mise/tasks/npm b/.mise/tasks/npm new file mode 100755 index 0000000..10b4274 --- /dev/null +++ b/.mise/tasks/npm @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +#MISE description="Build the npm tarball from deno.json and preserve Drizzle as an optional peer." +set -euo pipefail + +VERSION="${RELEASE_VERSION:-}" +if [[ -z "$VERSION" ]]; then + VERSION="$(node -p "JSON.parse(require('node:fs').readFileSync('deno.json', 'utf8')).version")" +fi + +OUT="${RELEASE_DIR:-.release/npm}" +rm -rf "$OUT" +mkdir -p "$OUT/extract" + +DIRTY=() +if [[ "${PACK_ALLOW_DIRTY:-0}" == "1" ]]; then + DIRTY+=(--allow-dirty) +fi + +deno pack --no-deno-shim --set-version "$VERSION" "${DIRTY[@]}" --output "$OUT/deno.tgz" +tar -xzf "$OUT/deno.tgz" -C "$OUT/extract" + +node --input-type=module - "$OUT/extract/package/package.json" <<'NODE' +import { readFile, writeFile } from 'node:fs/promises'; + +const path = process.argv[2]; +const generated = JSON.parse(await readFile(path, 'utf8')); +const source = JSON.parse(await readFile('package.json', 'utf8')); +const drizzle = generated.dependencies?.['drizzle-orm'] ?? source.peerDependencies['drizzle-orm']; + +if (generated.dependencies) { + delete generated.dependencies['drizzle-orm']; + if (Object.keys(generated.dependencies).length === 0) delete generated.dependencies; +} + +generated.description = source.description; +generated.license = source.license; +generated.repository = source.repository; +generated.homepage = source.homepage; +generated.bugs = source.bugs; +generated.keywords = source.keywords; +generated.sideEffects = false; +generated.engines = source.engines; +generated.peerDependencies = { ...generated.peerDependencies, 'drizzle-orm': drizzle }; +generated.peerDependenciesMeta = { + ...generated.peerDependenciesMeta, + 'drizzle-orm': { optional: true }, +}; + +await writeFile(path, `${JSON.stringify(generated, null, 2)}\n`); +NODE + +rm "$OUT/deno.tgz" +npm pack "$OUT/extract/package" --pack-destination "$OUT" --json > "$OUT/pack.json" +node --input-type=module - "$OUT/pack.json" <<'NODE' +import { readFile } from 'node:fs/promises'; +const [result] = JSON.parse(await readFile(process.argv[2], 'utf8')); +if (!result?.filename) throw new Error('npm pack did not report a tarball filename.'); +console.log(`${process.cwd()}/${process.argv[2].replace(/pack\.json$/, result.filename)}`); +NODE diff --git a/.mise/tasks/quality b/.mise/tasks/quality new file mode 100755 index 0000000..5aa49a6 --- /dev/null +++ b/.mise/tasks/quality @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +#MISE description="Run the complete Deno quality, stress, coverage, and package dry-run gate." +set -euo pipefail + +deno ci +deno task check +deno task lint +deno task doc +deno task fmt:check +deno task test:stress +deno task test:coverage +deno publish --dry-run +deno pack --dry-run --no-deno-shim diff --git a/.mise/tasks/test-providers b/.mise/tasks/test-providers new file mode 100755 index 0000000..ba293a7 --- /dev/null +++ b/.mise/tasks/test-providers @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +#MISE description="Run direct S3 and Azure Blob clients against local provider containers." +set -euo pipefail + +COMPOSE="tests/provider/compose.yml" + +docker compose -f "$COMPOSE" up -d +cleanup() { + docker compose -f "$COMPOSE" down -v --remove-orphans +} +trap cleanup EXIT + +wait_for_http() { + local name="$1" + local url="$2" + local status="000" + for _ in $(seq 1 60); do + status="$(curl -sS -o /dev/null -w '%{http_code}' "$url" 2>/dev/null || true)" + if [[ "$status" != "000" ]]; then + return 0 + fi + sleep 1 + done + echo "$name did not become reachable at $url" >&2 + docker compose -f "$COMPOSE" logs "$name" >&2 || true + return 1 +} + +wait_for_http s3 http://127.0.0.1:8333/ +wait_for_http azure http://127.0.0.1:10000/devstoreaccount1 + +deno ci +deno test \ + --allow-net=127.0.0.1:8333,127.0.0.1:10000 \ + --sanitize-ops \ + --sanitize-resources \ + --trace-leaks \ + tests/provider.test.ts diff --git a/.mise/tasks/verify-npm b/.mise/tasks/verify-npm new file mode 100755 index 0000000..0025d9a --- /dev/null +++ b/.mise/tasks/verify-npm @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +#MISE description="Build the npm tarball and verify it from Node, Deno, and Bun consumers." +set -euo pipefail + +deno ci +PACK_ALLOW_DIRTY="${PACK_ALLOW_DIRTY:-0}" RELEASE_VERSION="${RELEASE_VERSION:-0.0.0-test}" mise run npm +node tests/package/verify.mjs .release/npm/*.tgz diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8358b12 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Okiki Ojo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bench/browser/opfs.spec.ts b/bench/browser/opfs.spec.ts new file mode 100644 index 0000000..285fbe3 --- /dev/null +++ b/bench/browser/opfs.spec.ts @@ -0,0 +1,65 @@ +import { expect, test } from "@playwright/test"; + +/** Fixture page that exposes raw, adapter, and facade browser benchmark operations. */ +const APP_URL = "http://127.0.0.1:4173/tests/browser/fixtures/index.html"; + +/** Opens the benchmark fixture and waits for its callable API. */ +async function ready(page: import("@playwright/test").Page): Promise { + await page.goto(APP_URL); + await page.waitForFunction(() => Boolean((window as unknown as { opfsTest?: { ready?: boolean } }).opfsTest?.ready)); +} + +/** Normalizes one browser benchmark result into comparable overhead ratios. */ +function report( + browser: string, + backend: string, + iterations: number, + bytes: number, + result: { rawMs: number; adapterMs: number; facadeMs: number }, +): Record { + return { + browser, + backend, + iterations, + bytes, + ...result, + adapterOverhead: result.adapterMs / result.rawMs, + facadeOverhead: result.facadeMs / result.rawMs, + facadeOverAdapter: result.facadeMs / result.adapterMs, + }; +} + +test("reports raw native OPFS, direct adapter, and facade overhead", async ({ browserName, page }, testInfo) => { + await ready(page); + const result = await page.evaluate(async () => await window.opfsTest.benchmark(25, 64 * 1024)); + test.skip(result === null, "OPFS is unavailable in this browser context."); + expect(result!.rawMs).toBeGreaterThan(0); + expect(result!.adapterMs).toBeGreaterThan(0); + expect(result!.facadeMs).toBeGreaterThan(0); + const sample = report(browserName, "opfs", 25, 64 * 1024, result!); + console.log(`[opfs benchmark] ${JSON.stringify(sample)}`); + await testInfo.attach("opfs-benchmark.json", { + body: JSON.stringify(sample, null, 2), + contentType: "application/json", + }); +}); + +for (const backend of ["localstorage", "indexeddb", "cache"] as const) { + test(`reports raw ${backend}, direct adapter, and facade overhead`, async ({ browserName, page }, testInfo) => { + await ready(page); + const result = await page.evaluate( + async ({ backend }) => await window.opfsTest.benchmarkAdapter(backend, 20, 16 * 1024), + { backend }, + ); + test.skip(result === null, `${backend} is unavailable in this browser context.`); + expect(result!.rawMs).toBeGreaterThan(0); + expect(result!.adapterMs).toBeGreaterThan(0); + expect(result!.facadeMs).toBeGreaterThan(0); + const sample = report(browserName, backend, 20, 16 * 1024, result!); + console.log(`[opfs benchmark] ${JSON.stringify(sample)}`); + await testInfo.attach(`${backend}-benchmark.json`, { + body: JSON.stringify(sample, null, 2), + contentType: "application/json", + }); + }); +} diff --git a/bench/browser/playwright.config.ts b/bench/browser/playwright.config.ts new file mode 100644 index 0000000..3c046c7 --- /dev/null +++ b/bench/browser/playwright.config.ts @@ -0,0 +1,22 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** Same-origin benchmark server used by every browser project. */ +const baseURL = "http://127.0.0.1:4173"; + +export default defineConfig({ + testDir: ".", + testMatch: "*.spec.ts", + workers: 1, + reporter: "line", + use: { baseURL }, + webServer: { + command: "deno run -A npm:vite@8.2.1 --host 127.0.0.1 --port 4173 --strictPort", + url: baseURL, + reuseExistingServer: true, + }, + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { name: "webkit", use: { ...devices["Desktop Safari"] } }, + ], +}); diff --git a/bench/bun-provider.bench.ts b/bench/bun-provider.bench.ts new file mode 100644 index 0000000..8e3bdea --- /dev/null +++ b/bench/bun-provider.bench.ts @@ -0,0 +1,114 @@ +import { S3Client as BunS3Client } from "bun"; +import { toBytes } from "@std/streams/to-bytes"; +import { bench, run } from "mitata"; + +import { createFileSystem } from "../mod.ts"; +import { createObjectAdapter } from "../src/adapter/object.ts"; +import { createS3Client } from "../src/s3.ts"; + +/** Local S3-compatible fixture shared with the provider test matrix. */ +const S3_ENDPOINT = "http://127.0.0.1:8333"; +const BUCKET = "opfs-test"; +const PREFIX = `bench/bun/${crypto.randomUUID()}`; +/** Small payload keeps client and abstraction overhead visible on loopback. */ +const payload = new Uint8Array(256 * 1024); +payload.fill(17); +/** Multipart payload exercises each streaming scheduler above the five-MiB S3 minimum. */ +const multipart = new Uint8Array(6 * 1024 * 1024); +multipart.fill(19); + +/** Bun's current native Rust-backed S3 client baseline. */ +const bun = new BunS3Client({ + endpoint: S3_ENDPOINT, + bucket: BUCKET, + region: "us-east-1", + accessKeyId: "admin", + secretAccessKey: "secret", + virtualHostedStyle: false, + retry: 0, + partSize: 5 * 1024 * 1024, + queueSize: 4, +}); +/** Project direct SigV4 client with retry and metrics overhead disabled. */ +const s3 = createS3Client({ + endpoint: S3_ENDPOINT, + bucket: BUCKET, + region: "us-east-1", + credentials: { accessKeyId: "admin", secretAccessKey: "secret" }, + request: { retries: 0 }, + metrics: "none", + partSize: 5 * 1024 * 1024, + concurrency: 4, +}); +const adapter = createObjectAdapter(s3, { prefix: `${PREFIX}/adapter` }); +const facade = createFileSystem(createObjectAdapter(s3, { prefix: `${PREFIX}/facade` }), { + coordination: "none", + metrics: "none", +}); +const measured = createFileSystem(createObjectAdapter(s3, { prefix: `${PREFIX}/metrics` }), { + coordination: "none", + metrics: "basic", +}); + +const bunKey = `${PREFIX}/bun.bin`; +const directKey = `${PREFIX}/direct.bin`; +await bun.write(bunKey, payload); +await bun.file(bunKey).stat(); +await s3.put(directKey, payload); +await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); +await facade.writeFile("/bench.bin", payload); +await measured.writeFile("/bench.bin", payload); + +bench("provider/s3 Bun S3Client: 256 KiB replace + stat", async () => { + await bun.write(bunKey, payload); + await bun.file(bunKey).stat(); +}); +bench("provider/s3 project direct client: 256 KiB replace + stat", async () => { + await s3.put(directKey, payload); +}); +bench("provider/s3 project direct adapter: 256 KiB replace + stat", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); +}); +bench("provider/s3 project facade metrics none: 256 KiB replace + stat", async () => { + await facade.writeFile("/bench.bin", payload); +}); +bench("provider/s3 project facade metrics basic: 256 KiB replace + stat", async () => { + await measured.writeFile("/bench.bin", payload); +}); + +bench("provider/s3 Bun S3File: 256 KiB read", async () => { + await bun.file(bunKey).bytes(); +}); +bench("provider/s3 project direct client: 256 KiB read", async () => { + await toBytes(await s3.get(directKey)); +}); +bench("provider/s3 project direct adapter: 256 KiB read", async () => { + await adapter.readFile("/bench.bin"); +}); +bench("provider/s3 project facade metrics none: 256 KiB read", async () => { + await facade.readFile("/bench.bin"); +}); + +bench("provider/s3 Bun NetworkSink: 6 MiB multipart + stat", async () => { + const key = `${PREFIX}/bun-multipart.bin`; + const writer = bun.file(key).writer({ partSize: 5 * 1024 * 1024, queueSize: 4, retry: 0 }); + await writer.write(multipart); + await writer.end(); + await bun.file(key).stat(); +}); +bench("provider/s3 project direct client: 6 MiB multipart + stat", async () => { + const source = new ReadableStream({ + start(controller) { + controller.enqueue(multipart); + controller.close(); + }, + }); + await s3.put(`${PREFIX}/direct-multipart.bin`, source, { size: multipart.byteLength }); +}); + +try { + await run(); +} finally { + await facade.close(); + await measured.close(); +} diff --git a/bench/bun.bench.ts b/bench/bun.bench.ts new file mode 100644 index 0000000..9fe3b34 --- /dev/null +++ b/bench/bun.bench.ts @@ -0,0 +1,94 @@ +import { bench, run } from "mitata"; +import { copyFile, mkdir, mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { createFileSystem } from "../mod.ts"; +import { createBunAdapter } from "../src/adapter/bun.ts"; + +/** Minimal Bun runtime shape used by the benchmark without global Bun typings. */ +interface BunRuntimeType { + file(path: string): Blob & { bytes(): Promise }; + write(path: string, data: Blob | Response | ArrayBufferView | ArrayBuffer | string): Promise; +} + +/** Resolves Bun only when the benchmark actually runs under Bun. */ +function getBun(): BunRuntimeType { + const runtime = Reflect.get(globalThis, "Bun") as BunRuntimeType | undefined; + if (runtime === undefined) throw new TypeError("bun.bench.ts requires the Bun runtime."); + return runtime; +} + +/** Temporary benchmark root that keeps raw, adapter, and facade data isolated. */ +const root = await mkdtemp(join(tmpdir(), "okikio-opfs-bench-")); +/** Host directory used by the direct runtime filesystem baseline. */ +const rawRoot = join(root, "raw"); +/** Host directory used by direct adapter operations. */ +const adapterRoot = join(root, "adapter"); +/** Host directory used by the facade with coordination disabled. */ +const noneRoot = join(root, "none"); +/** Host directory used by the facade with local coordination enabled. */ +const localRoot = join(root, "local"); +await Promise.all([rawRoot, adapterRoot, noneRoot, localRoot].map((path) => mkdir(path, { recursive: true }))); + +/** Fixed-size payload shared by every benchmark path so byte volume stays comparable. */ +const payload = new Uint8Array(64 * 1024); +/** Concrete host path used by the direct filesystem baseline. */ +const rawPath = join(rawRoot, "bench.bin"); +/** Direct runtime adapter measured without the filesystem facade. */ +const adapter = createBunAdapter({ root: adapterRoot }); +/** Filesystem facade measured with coordination disabled. */ +const none = createFileSystem(createBunAdapter({ root: noneRoot }), { coordination: "none", metrics: "none" }); +/** Filesystem facade measured with same-realm local coordination. */ +const local = createFileSystem(createBunAdapter({ root: localRoot }), { coordination: "local", metrics: "none" }); +/** Bun runtime resolved lazily through the ambient global. */ +const bun = getBun(); + +bench("bun/raw file: 64 KiB replace + read", async () => { + await bun.write(rawPath, payload); + await bun.file(rawPath).bytes(); +}); + +bench("bun/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("bun/facade none: 64 KiB replace + read", async () => { + await none.writeFile("/bench.bin", payload); + await none.readFile("/bench.bin"); +}); + +bench("bun/facade local: 64 KiB replace + read", async () => { + await local.writeFile("/bench.bin", payload); + await local.readFile("/bench.bin"); +}); + +await bun.write(join(rawRoot, "source.bin"), payload); +await adapter.writeFile("/source.bin", payload, { mode: "replace" }); +await none.writeFile("/source.bin", payload); + +bench("bun/raw node fs: 64 KiB copyFile", async () => { + await copyFile(join(rawRoot, "source.bin"), join(rawRoot, "copy-node.bin")); +}); + +bench("bun/raw Bun.write: 64 KiB BunFile copy", async () => { + await bun.write(join(rawRoot, "copy-bun.bin"), bun.file(join(rawRoot, "source.bin"))); +}); + +bench("bun/adapter: 64 KiB native copy", async () => { + await adapter.copy!("/source.bin", "/copy.bin", { overwrite: true }); +}); + +bench("bun/facade: 64 KiB native copy", async () => { + if (await none.exists("/copy.bin")) await none.remove("/copy.bin"); + await none.copy("/source.bin", "/copy.bin"); +}); + +try { + await run(); +} finally { + await none.close(); + await local.close(); + await rm(root, { recursive: true, force: true }); +} diff --git a/bench/deno-kv.bench.ts b/bench/deno-kv.bench.ts new file mode 100644 index 0000000..0a3f345 --- /dev/null +++ b/bench/deno-kv.bench.ts @@ -0,0 +1,43 @@ +/// + +import { bench, run } from "mitata"; + +import { createFileSystem } from "../mod.ts"; +import { createDenoKvAdapter } from "../src/adapter/deno-kv.ts"; + +/** Temporary on-disk Deno KV location used only for this benchmark run. */ +const path = await Deno.makeTempFile({ prefix: "okikio-opfs-kv-bench-" }); +await Deno.remove(path); +/** Real Deno KV database shared by raw, adapter, and facade measurements. */ +const database = await Deno.openKv(path); +/** Fixed 64 KiB payload used by every Deno KV path. */ +const payload = new Uint8Array(64 * 1024); +/** Deno KV key used by the direct backend baseline. */ +const rawKey = ["bench", "raw"] as const; +/** Direct Deno KV adapter measured without facade overhead. */ +const adapter = createDenoKvAdapter(database, { prefix: "bench-adapter" }); +/** Filesystem facade backed by the same Deno KV database with coordination disabled. */ +const fileSystem = createFileSystem(createDenoKvAdapter(database, { prefix: "bench-facade" }), { coordination: "none", metrics: "none" }); + +bench("deno-kv/raw: 64 KiB replace + get", async () => { + await database.set(rawKey, payload); + await database.get(rawKey); +}); + +bench("deno-kv/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("deno-kv/facade: 64 KiB replace + read", async () => { + await fileSystem.writeFile("/bench.bin", payload); + await fileSystem.readFile("/bench.bin"); +}); + +try { + await run(); +} finally { + await fileSystem.close(); + database.close(); + await Deno.remove(path).catch(() => undefined); +} diff --git a/bench/deno.bench.ts b/bench/deno.bench.ts new file mode 100644 index 0000000..288e572 --- /dev/null +++ b/bench/deno.bench.ts @@ -0,0 +1,72 @@ +import { bench, run } from "mitata"; + +import { createFileSystem } from "../mod.ts"; +import { createDenoAdapter } from "../src/adapter/deno.ts"; + +/** Temporary benchmark root that keeps raw, adapter, and facade data isolated. */ +const root = await Deno.makeTempDir({ prefix: "okikio-opfs-bench-" }); +/** Host directory used by the direct runtime filesystem baseline. */ +const rawRoot = `${root}/raw`; +/** Host directory used by direct adapter operations. */ +const adapterRoot = `${root}/adapter`; +/** Host directory used by the facade with coordination disabled. */ +const noneRoot = `${root}/none`; +/** Host directory used by the facade with local coordination enabled. */ +const localRoot = `${root}/local`; +for (const path of [rawRoot, adapterRoot, noneRoot, localRoot]) await Deno.mkdir(path, { recursive: true }); + +/** Fixed-size payload shared by every benchmark path so byte volume stays comparable. */ +const payload = new Uint8Array(64 * 1024); +/** Concrete host path used by the direct filesystem baseline. */ +const rawPath = `${rawRoot}/bench.bin`; +/** Direct runtime adapter measured without the filesystem facade. */ +const adapter = createDenoAdapter({ root: adapterRoot }); +/** Filesystem facade measured with coordination disabled. */ +const none = createFileSystem(createDenoAdapter({ root: noneRoot }), { coordination: "none", metrics: "none" }); +/** Filesystem facade measured with same-realm local coordination. */ +const local = createFileSystem(createDenoAdapter({ root: localRoot }), { coordination: "local", metrics: "none" }); + +bench("deno/raw fs: 64 KiB replace + read", async () => { + await Deno.writeFile(rawPath, payload); + await Deno.readFile(rawPath); +}); + +bench("deno/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("deno/facade none: 64 KiB replace + read", async () => { + await none.writeFile("/bench.bin", payload); + await none.readFile("/bench.bin"); +}); + +bench("deno/facade local: 64 KiB replace + read", async () => { + await local.writeFile("/bench.bin", payload); + await local.readFile("/bench.bin"); +}); + +await Deno.writeFile(`${rawRoot}/source.bin`, payload); +await adapter.writeFile("/source.bin", payload, { mode: "replace" }); +await none.writeFile("/source.bin", payload); + +bench("deno/raw fs: 64 KiB native copy", async () => { + await Deno.copyFile(`${rawRoot}/source.bin`, `${rawRoot}/copy.bin`); +}); + +bench("deno/adapter: 64 KiB native copy", async () => { + await adapter.copy!("/source.bin", "/copy.bin", { overwrite: true }); +}); + +bench("deno/facade: 64 KiB native copy", async () => { + if (await none.exists("/copy.bin")) await none.remove("/copy.bin"); + await none.copy("/source.bin", "/copy.bin"); +}); + +try { + await run(); +} finally { + await none.close(); + await local.close(); + await Deno.remove(root, { recursive: true }); +} diff --git a/bench/files.ts b/bench/files.ts deleted file mode 100644 index b60d8d1..0000000 --- a/bench/files.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { bench, do_not_optimize, group, run } from 'mitata'; -import { mkdtemp, rm } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { createFileSystem } from '../mod.ts'; -import { createMemoryAdapter } from '../src/adapter/memory.ts'; -import { createNodeAdapter } from '../src/adapter/node.ts'; - -const chunk = new Uint8Array(64 * 1024); - -/** Writes one native file through a single long-lived positional resource. */ -async function native(bytes: number): Promise { - const root = await mkdtemp(join(tmpdir(), 'opfs-bench-')); - const fileSystem = createFileSystem(createNodeAdapter({ root }), { coordination: 'local' }); - try { - await fileSystem.ensureFile('/output.bin'); - const file = await fileSystem.openWritableFile('/output.bin'); - try { - for (let at = 0; at < bytes; at += chunk.byteLength) await file.write(chunk, { at }); - await file.flush(); - await file.close(); - } catch (error) { - await file.abort(error); - throw error; - } - const stat = await fileSystem.stat('/output.bin'); - return stat.kind === 'file' ? stat.size : 0; - } finally { - await fileSystem.close(); - await rm(root, { recursive: true, force: true }); - } -} - -/** Exercises the intentionally expensive record-update path as a pathological comparison. */ -async function record(bytes: number): Promise { - const fileSystem = createFileSystem(createMemoryAdapter(), { coordination: 'local' }); - try { - for (let at = 0; at < bytes; at += chunk.byteLength) { - await fileSystem.writeFile('/output.bin', chunk, { mode: 'update', at, parents: true }); - } - const stat = await fileSystem.stat('/output.bin'); - return stat.kind === 'file' ? stat.size : 0; - } finally { - await fileSystem.close(); - } -} - -group('@okikio/opfs positional output', () => { - bench('Node positional 16 MiB', async () => do_not_optimize(await native(16 * 1024 * 1024))).gc('once'); - bench('record update 1 MiB pathological', async () => do_not_optimize(await record(1024 * 1024))).gc('once'); -}); - -await run(); diff --git a/bench/memory.bench.ts b/bench/memory.bench.ts new file mode 100644 index 0000000..aedc421 --- /dev/null +++ b/bench/memory.bench.ts @@ -0,0 +1,64 @@ +import { encodeBase64 } from "@std/encoding/base64"; +import { bench, run } from "mitata"; + +import { createFileSystem } from "../mod.ts"; +import { createMemoryAdapter, createMemoryRecordStore } from "../src/adapter/memory.ts"; +import type { RecordType } from "../src/schema.ts"; + +/** Fixed 64 KiB payload shared by all in-memory benchmark paths. */ +const payload = new Uint8Array(64 * 1024); +/** Base64 representation used by the raw record-store baseline. */ +const encoded = encodeBase64(payload); + +/** Raw Map baseline with no adapter or record translation. */ +const raw = new Map(); +/** Direct RecordStore baseline used to isolate record serialization cost. */ +const store = createMemoryRecordStore(); +/** Canonical file record written directly to the RecordStore baseline. */ +const record: RecordType = { + version: 1, + path: "/bench.bin", + parent: "/", + name: "bench.bin", + kind: "file", + data: encoded, + size: payload.byteLength, + lastModified: 0, + mediaType: "application/octet-stream", +}; + +/** Direct memory adapter measured without facade coordination. */ +const adapter = createMemoryAdapter(); +/** Memory-backed filesystem facade measured with coordination disabled. */ +const none = createFileSystem(createMemoryAdapter(), { coordination: "none", metrics: "none" }); +/** Memory-backed filesystem facade measured with local coordination enabled. */ +const local = createFileSystem(createMemoryAdapter(), { coordination: "local", metrics: "none" }); + +bench("memory/raw Map: 64 KiB replace + read", () => { + raw.set("/bench.bin", payload.slice()); + raw.get("/bench.bin")!.slice(); +}); + +bench("memory/RecordStore: 64 KiB set + get", async () => { + await store.set(record); + await store.get(record.path); +}); + +bench("memory/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("memory/facade none: 64 KiB replace + read", async () => { + await none.writeFile("/bench.bin", payload); + await none.readFile("/bench.bin"); +}); + +bench("memory/facade local: 64 KiB replace + read", async () => { + await local.writeFile("/bench.bin", payload); + await local.readFile("/bench.bin"); +}); + +await run(); +await none.close(); +await local.close(); diff --git a/bench/node.bench.ts b/bench/node.bench.ts new file mode 100644 index 0000000..fdd49f2 --- /dev/null +++ b/bench/node.bench.ts @@ -0,0 +1,75 @@ +import { bench, run } from "mitata"; +import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { createFileSystem } from "../mod.ts"; +import { createNodeAdapter } from "../src/adapter/node.ts"; + +/** Temporary benchmark root that keeps raw, adapter, and facade data isolated. */ +const root = await mkdtemp(join(tmpdir(), "okikio-opfs-bench-")); +/** Host directory used by the direct runtime filesystem baseline. */ +const rawRoot = join(root, "raw"); +/** Host directory used by direct adapter operations. */ +const adapterRoot = join(root, "adapter"); +/** Host directory used by the facade with coordination disabled. */ +const noneRoot = join(root, "none"); +/** Host directory used by the facade with local coordination enabled. */ +const localRoot = join(root, "local"); +await Promise.all([rawRoot, adapterRoot, noneRoot, localRoot].map((path) => mkdir(path, { recursive: true }))); + +/** Fixed-size payload shared by every benchmark path so byte volume stays comparable. */ +const payload = new Uint8Array(64 * 1024); +/** Concrete host path used by the direct filesystem baseline. */ +const rawPath = join(rawRoot, "bench.bin"); +/** Direct runtime adapter measured without the filesystem facade. */ +const adapter = createNodeAdapter({ root: adapterRoot }); +/** Filesystem facade measured with coordination disabled. */ +const none = createFileSystem(createNodeAdapter({ root: noneRoot }), { coordination: "none", metrics: "none" }); +/** Filesystem facade measured with same-realm local coordination. */ +const local = createFileSystem(createNodeAdapter({ root: localRoot }), { coordination: "local", metrics: "none" }); + +bench("node/raw fs: 64 KiB replace + read", async () => { + await writeFile(rawPath, payload); + await readFile(rawPath); +}); + +bench("node/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("node/facade none: 64 KiB replace + read", async () => { + await none.writeFile("/bench.bin", payload); + await none.readFile("/bench.bin"); +}); + +bench("node/facade local: 64 KiB replace + read", async () => { + await local.writeFile("/bench.bin", payload); + await local.readFile("/bench.bin"); +}); + +await writeFile(join(rawRoot, "source.bin"), payload); +await adapter.writeFile("/source.bin", payload, { mode: "replace" }); +await none.writeFile("/source.bin", payload); + +bench("node/raw fs: 64 KiB native copy", async () => { + await copyFile(join(rawRoot, "source.bin"), join(rawRoot, "copy.bin")); +}); + +bench("node/adapter: 64 KiB native copy", async () => { + await adapter.copy!("/source.bin", "/copy.bin", { overwrite: true }); +}); + +bench("node/facade: 64 KiB native copy", async () => { + if (await none.exists("/copy.bin")) await none.remove("/copy.bin"); + await none.copy("/source.bin", "/copy.bin"); +}); + +try { + await run(); +} finally { + await none.close(); + await local.close(); + await rm(root, { recursive: true, force: true }); +} diff --git a/bench/provider.bench.ts b/bench/provider.bench.ts new file mode 100644 index 0000000..d8902cf --- /dev/null +++ b/bench/provider.bench.ts @@ -0,0 +1,190 @@ +import { GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client as AwsS3Client } from "@aws-sdk/client-s3"; +import { Upload } from "@aws-sdk/lib-storage"; +import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob"; +import { toBytes } from "@std/streams/to-bytes"; +import { bench, run } from "mitata"; + +import { createFileSystem } from "../mod.ts"; +import { createObjectAdapter } from "../src/adapter/object.ts"; +import { createAzureClient } from "../src/azure.ts"; +import { createS3Client } from "../src/s3.ts"; + +/** Local provider fixture names match tests/provider/compose.yml. */ +const STORAGE_NAME = "opfs-test"; +const S3_ENDPOINT = "http://127.0.0.1:8333"; +const AZURITE_ACCOUNT = "devstoreaccount1"; +const AZURITE_KEY = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; +const AZURE_ENDPOINT = `http://127.0.0.1:10000/${AZURITE_ACCOUNT}`; +/** Small transfer keeps request/setup overhead visible instead of saturating loopback bandwidth. */ +const payload = new Uint8Array(256 * 1024); +payload.fill(7); +/** Multipart payload exercises each client's large-write scheduler separately. */ +const multipart = new Uint8Array(6 * 1024 * 1024); +multipart.fill(11); +const prefix = `bench/${crypto.randomUUID()}`; + +/** Official AWS SDK baseline against the same SeaweedFS endpoint. */ +const aws = new AwsS3Client({ + endpoint: S3_ENDPOINT, + region: "us-east-1", + forcePathStyle: true, + credentials: { accessKeyId: "admin", secretAccessKey: "secret" }, + maxAttempts: 1, +}); +/** Direct project S3 client with retries/metrics disabled for pure path overhead. */ +const s3 = createS3Client({ + endpoint: S3_ENDPOINT, + bucket: STORAGE_NAME, + region: "us-east-1", + credentials: { accessKeyId: "admin", secretAccessKey: "secret" }, + request: { retries: 0 }, + metrics: "none", + partSize: 5 * 1024 * 1024, +}); +const s3Adapter = createObjectAdapter(s3, { prefix: `${prefix}/s3-adapter` }); +const s3Facade = createFileSystem(createObjectAdapter(s3, { prefix: `${prefix}/s3-facade` }), { + coordination: "none", + metrics: "none", +}); +const s3Measured = createFileSystem(createObjectAdapter(s3, { prefix: `${prefix}/s3-metrics` }), { + coordination: "none", + metrics: "basic", +}); + +/** Official Azure SDK baseline against the same Azurite endpoint. */ +const azureCredential = new StorageSharedKeyCredential(AZURITE_ACCOUNT, AZURITE_KEY); +const azureService = new BlobServiceClient(AZURE_ENDPOINT, azureCredential); +const azureContainer = azureService.getContainerClient(STORAGE_NAME); +await azureContainer.createIfNotExists(); +/** Direct project Azure client with retries/metrics disabled for pure path overhead. */ +const azure = createAzureClient({ + endpoint: AZURE_ENDPOINT, + container: STORAGE_NAME, + credential: { kind: "shared-key", account: AZURITE_ACCOUNT, key: AZURITE_KEY }, + request: { retries: 0 }, + metrics: "none", + blockSize: 1024 * 1024, +}); +const azureAdapter = createObjectAdapter(azure, { prefix: `${prefix}/azure-adapter` }); +const azureFacade = createFileSystem(createObjectAdapter(azure, { prefix: `${prefix}/azure-facade` }), { + coordination: "none", + metrics: "none", +}); +const azureMeasured = createFileSystem(createObjectAdapter(azure, { prefix: `${prefix}/azure-metrics` }), { + coordination: "none", + metrics: "basic", +}); + +/** Materializes an AWS SDK GetObject body so all read cases include body consumption. */ +async function readAws(key: string): Promise { + const result = await aws.send(new GetObjectCommand({ Bucket: STORAGE_NAME, Key: key })); + await result.Body?.transformToByteArray(); +} + +/** Opens a fresh Web stream for each multipart attempt. */ +function stream(bytes: Uint8Array): ReadableStream { + return new ReadableStream({ + start(controller) { + controller.enqueue(bytes); + controller.close(); + }, + }); +} + +const awsKey = `${prefix}/aws.bin`; +const s3Key = `${prefix}/s3-client.bin`; +const azureKey = `${prefix}/azure-client.bin`; +const azureOfficial = azureContainer.getBlockBlobClient(`${prefix}/azure-sdk.bin`); +await aws.send(new PutObjectCommand({ Bucket: STORAGE_NAME, Key: awsKey, Body: payload })); +await s3.put(s3Key, payload); +await azureOfficial.uploadData(payload); +await azure.put(azureKey, payload); +await s3Adapter.writeFile("/bench.bin", payload, { mode: "replace" }); +await s3Facade.writeFile("/bench.bin", payload); +await s3Measured.writeFile("/bench.bin", payload); +await azureAdapter.writeFile("/bench.bin", payload, { mode: "replace" }); +await azureFacade.writeFile("/bench.bin", payload); +await azureMeasured.writeFile("/bench.bin", payload); + +bench("provider/s3 AWS SDK: 256 KiB replace + stat", async () => { + await aws.send(new PutObjectCommand({ Bucket: STORAGE_NAME, Key: awsKey, Body: payload })); + await aws.send(new HeadObjectCommand({ Bucket: STORAGE_NAME, Key: awsKey })); +}); +bench("provider/s3 direct client: 256 KiB replace + stat", async () => { + await s3.put(s3Key, payload); +}); +bench("provider/s3 direct adapter: 256 KiB replace + stat", async () => { + await s3Adapter.writeFile("/bench.bin", payload, { mode: "replace" }); +}); +bench("provider/s3 facade metrics none: 256 KiB replace + stat", async () => { + await s3Facade.writeFile("/bench.bin", payload); +}); +bench("provider/s3 facade metrics basic: 256 KiB replace + stat", async () => { + await s3Measured.writeFile("/bench.bin", payload); +}); + +bench("provider/s3 AWS SDK: 256 KiB read", async () => { + await readAws(awsKey); +}); +bench("provider/s3 direct client: 256 KiB read", async () => { + await toBytes(await s3.get(s3Key)); +}); +bench("provider/s3 direct adapter: 256 KiB read", async () => { + await s3Adapter.readFile("/bench.bin"); +}); +bench("provider/s3 facade metrics none: 256 KiB read", async () => { + await s3Facade.readFile("/bench.bin"); +}); + +bench("provider/s3 AWS Upload: 6 MiB multipart + stat", async () => { + await new Upload({ + client: aws, + params: { Bucket: STORAGE_NAME, Key: `${prefix}/aws-multipart.bin`, Body: multipart }, + queueSize: 4, + partSize: 5 * 1024 * 1024, + }).done(); + await aws.send(new HeadObjectCommand({ Bucket: STORAGE_NAME, Key: `${prefix}/aws-multipart.bin` })); +}); +bench("provider/s3 direct client: 6 MiB multipart + stat", async () => { + await s3.put(`${prefix}/s3-multipart.bin`, stream(multipart), { size: multipart.byteLength }); +}); + +bench("provider/azure official SDK: 256 KiB replace + stat", async () => { + await azureOfficial.uploadData(payload); + await azureOfficial.getProperties(); +}); +bench("provider/azure direct client: 256 KiB replace + stat", async () => { + await azure.put(azureKey, payload); +}); +bench("provider/azure direct adapter: 256 KiB replace + stat", async () => { + await azureAdapter.writeFile("/bench.bin", payload, { mode: "replace" }); +}); +bench("provider/azure facade metrics none: 256 KiB replace + stat", async () => { + await azureFacade.writeFile("/bench.bin", payload); +}); +bench("provider/azure facade metrics basic: 256 KiB replace + stat", async () => { + await azureMeasured.writeFile("/bench.bin", payload); +}); + +bench("provider/azure official SDK: 256 KiB read", async () => { + await azureOfficial.downloadToBuffer(); +}); +bench("provider/azure direct client: 256 KiB read", async () => { + await toBytes(await azure.get(azureKey)); +}); +bench("provider/azure direct adapter: 256 KiB read", async () => { + await azureAdapter.readFile("/bench.bin"); +}); +bench("provider/azure facade metrics none: 256 KiB read", async () => { + await azureFacade.readFile("/bench.bin"); +}); + +try { + await run(); +} finally { + await s3Facade.close(); + await s3Measured.close(); + await azureFacade.close(); + await azureMeasured.close(); + aws.destroy(); +} diff --git a/bench/sqlite.bench.ts b/bench/sqlite.bench.ts new file mode 100644 index 0000000..18601c1 --- /dev/null +++ b/bench/sqlite.bench.ts @@ -0,0 +1,69 @@ +import { bench, run } from "mitata"; +import { DatabaseSync } from "node:sqlite"; + +import { createFileSystem } from "../mod.ts"; +import { createSqliteAdapter } from "../src/adapter/sqlite.ts"; + +/** Node SQLite wrapper that matches the direct adapter's small statement contract. */ +class SqliteDatabase { + readonly #database: DatabaseSync; + + constructor(database: DatabaseSync) { + this.#database = database; + } + + prepare(sql: string) { + const statement = this.#database.prepare(sql); + return { + all: async (...params: never[]) => statement.all(...params), + get: async (...params: never[]) => statement.get(...params), + run: async (...params: never[]) => statement.run(...params), + }; + } +} + +/** Fixed 64 KiB payload shared by raw SQLite, adapter, and facade measurements. */ +const payload = new Uint8Array(64 * 1024); +/** Raw in-memory Node SQLite database used as the backend baseline. */ +const raw = new DatabaseSync(":memory:"); +raw.exec("CREATE TABLE raw (path TEXT PRIMARY KEY, data BLOB NOT NULL)"); +/** Prepared raw SQLite replacement statement measured without adapter translation. */ +const rawSet = raw.prepare("INSERT OR REPLACE INTO raw (path, data) VALUES (?, ?)"); +/** Prepared raw SQLite read statement measured without adapter translation. */ +const rawGet = raw.prepare("SELECT data FROM raw WHERE path = ?"); + +/** Dedicated in-memory SQLite database used by the direct adapter measurement. */ +const adapterDatabase = new DatabaseSync(":memory:"); +/** Dedicated in-memory SQLite database used by the filesystem facade measurement. */ +const facadeDatabase = new DatabaseSync(":memory:"); +/** Direct SQLite adapter measured without facade semantics. */ +const adapter = await createSqliteAdapter(new SqliteDatabase(adapterDatabase)); +/** Filesystem facade backed by SQLite with coordination disabled. */ +const fileSystem = createFileSystem(await createSqliteAdapter(new SqliteDatabase(facadeDatabase)), { + coordination: "none", + metrics: "none", +}); + +bench("sqlite/raw BLOB: 64 KiB replace + get", () => { + rawSet.run("/bench.bin", payload); + rawGet.get("/bench.bin"); +}); + +bench("sqlite/adapter: 64 KiB replace + read", async () => { + await adapter.writeFile("/bench.bin", payload, { mode: "replace" }); + await adapter.readFile("/bench.bin"); +}); + +bench("sqlite/facade: 64 KiB replace + read", async () => { + await fileSystem.writeFile("/bench.bin", payload); + await fileSystem.readFile("/bench.bin"); +}); + +try { + await run(); +} finally { + await fileSystem.close(); + raw.close(); + adapterDatabase.close(); + facadeDatabase.close(); +} diff --git a/deno.json b/deno.json index 0b1ee5b..dc9b6c1 100644 --- a/deno.json +++ b/deno.json @@ -7,32 +7,68 @@ "./adapter/opfs": "./src/adapter/opfs.ts", "./adapter/memory": "./src/adapter/memory.ts", "./adapter/deno": "./src/adapter/deno.ts", + "./adapter/deno-kv": "./src/adapter/deno-kv.ts", "./adapter/node": "./src/adapter/node.ts", "./adapter/bun": "./src/adapter/bun.ts", "./adapter/record": "./src/adapter/record.ts", + "./adapter/object": "./src/adapter/object.ts", + "./adapter/s3": "./src/adapter/s3.ts", + "./adapter/azure": "./src/adapter/azure.ts", + "./adapter/localstorage": "./src/adapter/localstorage.ts", + "./adapter/indexeddb": "./src/adapter/indexeddb.ts", + "./adapter/cache": "./src/adapter/cache.ts", + "./adapter/sqlite": "./src/adapter/sqlite.ts", "./adapter/unstorage": "./src/adapter/unstorage.ts", "./adapter/rxdb": "./src/adapter/rxdb.ts", "./adapter/db0": "./src/adapter/db0.ts", "./adapter/drizzle": "./src/adapter/drizzle.ts", + "./driver/kv": "./src/driver/kv.ts", "./driver/unstorage": "./src/driver/unstorage.ts", + "./s3": "./src/s3.ts", + "./azure": "./src/azure.ts", "./iframe": "./src/iframe.ts", "./path": "./src/path.ts", - "./schema": "./src/schema.ts" + "./schema": "./src/schema.ts", + "./bridge": "./src/bridge.ts", + "./bridge/definition": "./src/bridge/definition.ts", + "./bridge/unstorage": "./src/bridge/unstorage.ts", + "./bridge/rxdb": "./src/bridge/rxdb.ts", + "./bridge/db0": "./src/bridge/db0.ts", + "./bridge/drizzle": "./src/bridge/drizzle.ts", + "./bridge/kv": "./src/bridge/kv.ts", + "./capability": "./src/capability.ts", + "./metrics": "./src/metrics.ts", + "./plan": "./src/plan.ts", + "./request": "./src/request.ts" }, "imports": { - "zod": "npm:zod@^4.4.3", + "@std/expect": "jsr:@std/expect@^1.0.20", "drizzle-orm": "npm:drizzle-orm@^0.45.2", - "mitata": "npm:mitata@^1.0.34" + "mitata": "npm:mitata@^1.0.34", + "zod": "npm:zod@^4.4.3", + "@playwright/test": "npm:@playwright/test@^1.62.1", + "vite": "npm:vite@^8.2.1", + "@std/async": "jsr:@std/async@^1.5.0", + "@std/bytes": "jsr:@std/bytes@^1.0.6", + "@std/path": "jsr:@std/path@^1.1.6", + "@std/streams": "jsr:@std/streams@^1.1.2", + "@std/xml": "jsr:@std/xml@^0.2.0", + "@std/encoding": "jsr:@std/encoding@^1.0.11", + "@aws-sdk/client-s3": "npm:@aws-sdk/client-s3@^3.1098.0", + "@aws-sdk/lib-storage": "npm:@aws-sdk/lib-storage@^3.1100.0", + "@azure/storage-blob": "npm:@azure/storage-blob@^12.33.0" + }, + "nodeModulesDir": "auto", + "lock": { + "frozen": true }, "compilerOptions": { - "types": [ - "npm:@types/node" - ], "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "useUnknownInCatchVariables": true, "verbatimModuleSyntax": true, + "erasableSyntaxOnly": true, "lib": [ "dom", "dom.iterable", @@ -40,25 +76,87 @@ "deno.ns" ] }, + "fmt": { + "lineWidth": 120, + "semiColons": true, + "singleQuote": false, + "useTabs": false, + "indentWidth": 2, + "include": [ + "mod.ts", + "src/", + "tests/", + "bench/", + "docs/", + "README.md", + "AGENTS.md", + "deno.json", + "package.json" + ] + }, + "lint": { + "rules": { + "tags": [ + "recommended" + ], + "exclude": [ + "no-slow-types", + "require-await" + ] + }, + "include": [ + "mod.ts", + "src/", + "tests/", + "bench/" + ] + }, "tasks": { - "check": "deno task check:window && deno task check:worker && deno task check:server", - "check:window": "deno check mod.ts src/adapter/definition.ts src/adapter/opfs.ts src/adapter/memory.ts src/adapter/record.ts src/adapter/unstorage.ts src/adapter/rxdb.ts src/adapter/db0.ts src/adapter/drizzle.ts src/driver/unstorage.ts src/iframe.ts src/path.ts src/schema.ts", - "check:worker": "deno check tests/consumer.worker.ts", - "check:server": "deno check src/adapter/deno.ts src/adapter/node.ts src/adapter/bun.ts", - "test": "deno test tests/", - "test:browser": "deno run -A npm:vite tests/browser", - "fmt:code": "deno fmt mod.ts src/ tests/ deno.json package.json", - "fmt:check": "deno fmt --check mod.ts src/ tests/ deno.json package.json", - "lint": "deno lint mod.ts src/ tests/" + "deps:ci": "deno ci", + "check": "deno task check:core && deno task check:browser && deno task check:workers && deno task check:server && deno task check:tests && deno task check:deno-kv && deno task check:providers", + "check:core": "deno check mod.ts src/chunk.ts src/xml.ts src/request.ts src/metrics.ts src/capability.ts src/plan.ts src/bridge.ts src/bridge/definition.ts src/bridge/unstorage.ts src/bridge/rxdb.ts src/bridge/db0.ts src/bridge/drizzle.ts src/bridge/kv.ts src/adapter/definition.ts src/adapter/opfs.ts src/adapter/memory.ts src/adapter/record.ts src/adapter/object.ts src/adapter/unstorage.ts src/adapter/rxdb.ts src/adapter/db0.ts src/adapter/drizzle.ts src/driver/kv.ts src/driver/unstorage.ts src/iframe.ts src/path.ts src/schema.ts src/s3.ts src/azure.ts", + "check:browser": "deno check src/adapter/localstorage.ts src/adapter/indexeddb.ts src/adapter/cache.ts tests/browser/playwright.config.ts tests/browser/opfs.spec.ts tests/browser/worker.spec.ts tests/browser/iframe.spec.ts tests/browser/service-worker.spec.ts tests/browser/adapters.spec.ts tests/browser/fixtures/app.ts bench/browser/playwright.config.ts bench/browser/opfs.spec.ts", + "check:workers": "deno check tests/browser/fixtures/dedicated.ts tests/browser/fixtures/shared.ts tests/browser/fixtures/service.ts", + "check:server": "deno check src/adapter/deno.ts src/adapter/deno-kv.ts src/adapter/node.ts src/adapter/bun.ts src/adapter/sqlite.ts bench/deno.bench.ts bench/node.bench.ts bench/bun.bench.ts bench/sqlite.bench.ts", + "check:tests": "deno check tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts tests/node.test.ts tests/deno.test.ts tests/bun.test.ts", + "check:deno-kv": "deno check --unstable-kv tests/deno-kv.test.ts bench/deno-kv.bench.ts", + "test": "deno task test:portable && deno task test:deno && deno task test:deno-kv", + "test:portable": "deno test --sanitize-ops --sanitize-resources --trace-leaks tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts", + "test:deno": "deno test --allow-read --allow-write --sanitize-ops --sanitize-resources --trace-leaks tests/deno.test.ts", + "test:deno-kv": "deno test --unstable-kv --allow-read --allow-write --sanitize-ops --sanitize-resources --trace-leaks tests/deno-kv.test.ts", + "test:node": "node --test tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts tests/node.test.ts", + "test:bun": "bun test tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts tests/bun.test.ts", + "test:browser": "deno run -A npm:@playwright/test@1.62.1 test --config tests/browser/playwright.config.ts", + "test:browser:install": "deno run -A npm:playwright@1.62.1 install --with-deps chromium firefox webkit", + "test:stress": "deno test --sanitize-ops --sanitize-resources --shuffle=424242 --repeats=5 tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts", + "test:coverage": "deno test --clean --coverage=.coverage --sanitize-ops --sanitize-resources tests/path.test.ts tests/memory.test.ts tests/filesystem.test.ts tests/ecosystems.test.ts tests/chunk.test.ts tests/object.test.ts tests/s3.test.ts tests/azure.test.ts tests/sqlite.test.ts tests/deno-kv-partition.test.ts && deno coverage .coverage --lcov --output=.coverage/lcov.info", + "bench": "deno task bench:memory && deno task bench:deno && deno task bench:deno-kv", + "bench:memory": "deno run -A bench/memory.bench.ts", + "bench:deno": "deno run -A bench/deno.bench.ts", + "bench:node": "node bench/node.bench.ts", + "bench:bun": "bun run bench/bun.bench.ts", + "bench:browser": "deno run -A npm:@playwright/test@1.62.1 test --config bench/browser/playwright.config.ts", + "doc": "deno doc --lint mod.ts", + "fmt": "deno fmt", + "fmt:check": "deno fmt --check", + "lint": "deno lint", + "release:check": "deno task check && deno task lint && deno task doc && deno task fmt:check && deno task test && deno task test:stress && deno publish --dry-run && deno pack --dry-run --no-deno-shim", + "pack:npm": "bash .mise/tasks/npm", + "bench:deno-kv": "deno run --unstable-kv -A bench/deno-kv.bench.ts", + "bench:sqlite": "node bench/sqlite.bench.ts", + "test:providers": "bash .mise/tasks/test-providers", + "check:providers": "deno check tests/provider.test.ts bench/provider.bench.ts", + "bench:providers": "bash .mise/tasks/bench-providers", + "bench:bun-providers": "bun run bench/bun-provider.bench.ts" }, "publish": { "include": [ "mod.ts", "src/", "README.md", + "LICENSE", "docs/", - "deno.json", - "package.json" + "deno.json" ] } } diff --git a/deno.lock b/deno.lock index 78e2ffe..a56c4bf 100644 --- a/deno.lock +++ b/deno.lock @@ -1,28 +1,609 @@ { "version": "5", "specifiers": { - "npm:@deno/shim-deno@~0.19.2": "0.19.2", - "npm:@types/deno@^2.7.0": "2.7.0", - "npm:@types/node@*": "26.2.0", + "jsr:@std/assert@^1.0.19": "1.0.19", + "jsr:@std/async@^1.5.0": "1.5.0", + "jsr:@std/bytes@^1.0.6": "1.0.6", + "jsr:@std/data-structures@^1.1.1": "1.1.2", + "jsr:@std/encoding@^1.0.11": "1.0.11", + "jsr:@std/expect@^1.0.20": "1.0.20", + "jsr:@std/internal@^1.0.12": "1.0.14", + "jsr:@std/internal@^1.0.14": "1.0.14", + "jsr:@std/path@^1.1.6": "1.1.6", + "jsr:@std/streams@^1.1.2": "1.1.2", + "jsr:@std/xml@0.2": "0.2.0", + "npm:@aws-sdk/client-s3@^3.1098.0": "3.1111.0", + "npm:@aws-sdk/lib-storage@^3.1100.0": "3.1111.0_@aws-sdk+client-s3@3.1111.0", + "npm:@azure/storage-blob@^12.33.0": "12.33.0", + "npm:@jsr/std__async@^1.5.0": "1.5.0", + "npm:@jsr/std__bytes@^1.0.6": "1.0.6", + "npm:@jsr/std__encoding@^1.0.11": "1.0.11", + "npm:@jsr/std__expect@^1.0.20": "1.0.20", + "npm:@jsr/std__path@^1.1.6": "1.1.6", + "npm:@jsr/std__streams@^1.1.2": "1.1.2", + "npm:@jsr/std__xml@0.2": "0.2.0", + "npm:@playwright/test@^1.62.1": "1.62.1", "npm:@types/node@^26.2.0": "26.2.0", - "npm:@types/sharedworker@^0.0.229": "0.0.229", "npm:drizzle-orm@~0.45.2": "0.45.2", "npm:mitata@^1.0.34": "1.0.34", + "npm:typescript@^7.0.2": "7.0.2", + "npm:vite@^8.2.1": "8.2.1_@types+node@26.2.0", "npm:zod@^4.4.3": "4.4.3" }, + "jsr": { + "@std/assert@1.0.19": { + "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e", + "dependencies": [ + "jsr:@std/internal@^1.0.12" + ] + }, + "@std/async@1.5.0": { + "integrity": "4c1f22d287a17ad7d8643b1952bc5a4d21e5d8b68dc8c20d8268eecc69096e82", + "dependencies": [ + "jsr:@std/data-structures" + ] + }, + "@std/bytes@1.0.6": { + "integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a" + }, + "@std/data-structures@1.1.2": { + "integrity": "2471377c9e0051f9ec34e57d0ebd0ad133a5a889677446ec6ba9938e4e102918" + }, + "@std/encoding@1.0.11": { + "integrity": "e7cef2f0b3153bccc17431e7c864a1de03a4b6d9647389c43e08aaa775d37385" + }, + "@std/expect@1.0.20": { + "integrity": "ffc4f3c33f732417e3e9acf3d995818c89984313c37d933b9ebbb4571d2887e9", + "dependencies": [ + "jsr:@std/assert", + "jsr:@std/internal@^1.0.14", + "jsr:@std/path" + ] + }, + "@std/internal@1.0.14": { + "integrity": "291516b3d4c35024d6ffbc0a9df5bf4c64116e05b50012cf846710152d2ffdf7" + }, + "@std/path@1.1.6": { + "integrity": "c68485c2a4dfbb5ae3cc74fae4e8c4e5d874cf8a8ed12927917235c758b46cbe", + "dependencies": [ + "jsr:@std/internal@^1.0.14" + ] + }, + "@std/streams@1.1.2": { + "integrity": "0249bf9b78a999f57032ca4d8e7ccaa57579090242640b35ddc948fbb745d3af", + "dependencies": [ + "jsr:@std/bytes" + ] + }, + "@std/xml@0.2.0": { + "integrity": "e0b54ae29592bc1fc688109f78ef5c18680bf820ba8aa0a3990a115e709e58d2" + } + }, "npm": { - "@deno/shim-deno-test@0.5.0": { - "integrity": "sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==" + "@aws-sdk/checksums@3.1000.28": { + "integrity": "sha512-VCpnmyHQ1IH49ni3LXnQj7DPr7rmcJmzYeiCkYdCcfgNtkvOj38cdcL9lapBWoItZWFACJPFJlymqC7/gem3Gw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/client-s3@3.1111.0": { + "integrity": "sha512-VnLT6aSTN8tWl/NsXUysXNZor7wQBp9CRwufo7kt8cwGXvHLZ0S/cV1K9WFcREGboVYSo3NGQ3ZvU7LRidh2aQ==", + "dependencies": [ + "@aws-sdk/checksums", + "@aws-sdk/core", + "@aws-sdk/credential-provider-node", + "@aws-sdk/middleware-sdk-s3", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/core@3.977.8": { + "integrity": "sha512-7+Kcrkvrk9lM/m7jRhHpT4jCdvzGHsuaSRbF8TdzzkY1mRzp/Ogwf9c7H29k4gGhey0BBWhCWr16+t0J61gwmg==", + "dependencies": [ + "@aws-sdk/types", + "@aws-sdk/xml-builder", + "@aws/lambda-invoke-store", + "@smithy/core", + "@smithy/signature-v4", + "@smithy/types", + "bowser", + "tslib" + ] + }, + "@aws-sdk/credential-provider-env@3.972.69": { + "integrity": "sha512-AreCFzcB4kH2HF9031Ot0jSJr3KXvRg6e8uDeub20JEVdZU3Bv0sTq1plc7VsT3KiqutlzH7l0j50UcCWHUioA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-http@3.972.71": { + "integrity": "sha512-A8ObcqVmDMnk4F9NozZ7JwmUu9Q4xyBJkmyq1C5U+wNM9ht9J7+EuuyabsLWXZnOoTqFaJuYBYTKf5CTipkEjA==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-ini@3.973.14": { + "integrity": "sha512-7c+Wti2LsERNWMfm7ySz3/6RPopFW3Nmn7s63Xpcq6R/tRuY5hpvkHA2xVgi5ukJbvok9l0IDtVEvqTtg+X7dw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-login", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/core", + "@smithy/credential-provider-imds", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-login@3.972.76": { + "integrity": "sha512-LVixwOnEJfrrfKHeZjBA8pIMTZjNDq8ak8VpcoWUuCJDrSnBNU8POJksULMgvN089P0MXtQYH2Zs627/MK1K0g==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-node@3.972.80": { + "integrity": "sha512-bE2qh8ww4iClO1jHsBXdOE8FUgzDbdxbyorNjSCoPSkQd51k3jODItuPZfuwcLHZqDXsH+bI4AMHhqtuyR7mSg==", + "dependencies": [ + "@aws-sdk/credential-provider-env", + "@aws-sdk/credential-provider-http", + "@aws-sdk/credential-provider-ini", + "@aws-sdk/credential-provider-process", + "@aws-sdk/credential-provider-sso", + "@aws-sdk/credential-provider-web-identity", + "@aws-sdk/types", + "@smithy/core", + "@smithy/credential-provider-imds", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-process@3.972.69": { + "integrity": "sha512-9kpTNdZTrcqXTfhxM7fgl9Z68ek3Fu5oe3Yf+A/pJGibEqpgZxz2tSY7SinmyCIU2PJ+ygY4FPoBBnLpocMtrQ==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-sso@3.973.13": { + "integrity": "sha512-Oc81qauMPzUoTnAS2YKpNwY6sY/LUyQTEeaf6yP197WMxkEBQfcKLR1MFpD7+pNTubXnfkH6gwpji+Gc7iyD2Q==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/token-providers", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/credential-provider-web-identity@3.972.75": { + "integrity": "sha512-YPN6uoGDgjjjeVFZrcOeCJqmB6zpXoeeNgIjqe+DexJaWqdjVfCCe+VAZwli9Z2h8KhFW8oxkO39emQ1tyz/Mw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/lib-storage@3.1111.0_@aws-sdk+client-s3@3.1111.0": { + "integrity": "sha512-UjLJE0Zxv/m8Xgd3DLBXssZYISgoEPZpJiPWY+MadEdP36oOcjHVsR8ToUq6MMmKdzuBFYB6qGHWS/yKAG4fow==", + "dependencies": [ + "@aws-sdk/client-s3", + "@smithy/core", + "@smithy/types", + "buffer", + "events", + "stream-browserify", + "tslib" + ] + }, + "@aws-sdk/middleware-sdk-s3@3.972.74": { + "integrity": "sha512-2lzoV2z2QO5KJZYGOCnIZ1WVQgzMECvwuzr1xb034a++8QW4U4eGrmC2u4yg1xvNv4TLL/Uv5DLyuAiw0b9z7Q==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/nested-clients@3.997.43": { + "integrity": "sha512-bit+VpqWNyi3wHxFoTsTliNXimCSL2r2OeDTm7ZrG+YsTZ2D7ofDJ6r/t9PVBn80i6/v0X2h9Tgw6QP2MAKfPw==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/signature-v4-multi-region", + "@aws-sdk/types", + "@smithy/core", + "@smithy/fetch-http-handler", + "@smithy/node-http-handler", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/signature-v4-multi-region@3.996.45": { + "integrity": "sha512-bBuyztukzXq6plzFGHAWiQt0QXo+HL8b8lX5cFTzkez/74PtS1c0qPFCIVuHkyoT+miH2qOjAcm1/yoro2ESPA==", + "dependencies": [ + "@aws-sdk/types", + "@smithy/signature-v4", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/token-providers@3.1111.0": { + "integrity": "sha512-JfljgoVtl+s3Qy21n9a7Z48uCQaOXcN74KJ3TEQfPoB293GrXFSt6HSQJF1sTZ8c/5QedEvd3NjJQMO4u9qa5A==", + "dependencies": [ + "@aws-sdk/core", + "@aws-sdk/nested-clients", + "@aws-sdk/types", + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/types@3.974.4": { + "integrity": "sha512-dSFDNG00MEz0/xl5gxL62giLd1iYyJsTxZ1I1DOj6lC+bbgLB4TRsYClJg3b62dhXT1uATzsTNXPnC+33EJV3A==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws-sdk/xml-builder@3.972.39": { + "integrity": "sha512-FTti8DS5MMWXNUWiRwXAJeYS+0GHHiMy0+7XOhcwk63ILHmfS2UFy2z/HNpZCSOJJ3P3dnWY6hfYNW3DF0nXUA==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@aws/lambda-invoke-store@0.3.0": { + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==" + }, + "@azure/abort-controller@2.2.0": { + "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==", + "dependencies": [ + "tslib" + ] + }, + "@azure/core-auth@1.11.0": { + "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-util", + "tslib" + ] + }, + "@azure/core-client@1.11.0": { + "integrity": "sha512-JjQWO6akOck45PH/XBrxzsQGAiKrfFl4m5iggJ0ItMIz5omRufOXWpqCPpdjKN3vKDzlSUvFjaMb7Zwf0gvAdA==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-auth", + "@azure/core-rest-pipeline", + "@azure/core-tracing", + "@azure/core-util", + "@azure/logger", + "tslib" + ] + }, + "@azure/core-http-compat@2.5.0_@azure+core-client@1.11.0_@azure+core-rest-pipeline@1.25.0": { + "integrity": "sha512-BoSmXPx2er1Ai+wKlDvj29jIQespCNBwEmKyZVHO2kEFsWbGjAjwMCGzug3DJM5/QYIV3vej0S1zcU5bq9fa8w==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-client", + "@azure/core-rest-pipeline" + ] + }, + "@azure/core-lro@2.7.2": { + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-util", + "@azure/logger", + "tslib" + ] + }, + "@azure/core-paging@1.7.0": { + "integrity": "sha512-7GEAoIsaoBr6KELNRb8nypowCqvk8dnCHFCYg4XD4lOQGY2GqjQg5IhkRjyBFRO18CGSMq05PaNqSOE9GQro3g==", + "dependencies": [ + "tslib" + ] + }, + "@azure/core-rest-pipeline@1.25.0": { + "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-auth", + "@azure/core-tracing", + "@azure/core-util", + "@azure/logger", + "@typespec/ts-http-runtime", + "tslib" + ] + }, + "@azure/core-tracing@1.4.0": { + "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==", + "dependencies": [ + "tslib" + ] + }, + "@azure/core-util@1.14.0": { + "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==", + "dependencies": [ + "@azure/abort-controller", + "@typespec/ts-http-runtime", + "tslib" + ] + }, + "@azure/core-xml@1.6.0": { + "integrity": "sha512-e7lX/dk//F6Qf7BB6PTY4+p2yuOQtyOeHGyapYHNwqSp2OnYpwQt49A/Nin2XmKBQ69pwagR4k/lQBq8lbHQkA==", + "dependencies": [ + "fast-xml-parser", + "tslib" + ] + }, + "@azure/logger@1.4.0": { + "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==", + "dependencies": [ + "@typespec/ts-http-runtime", + "tslib" + ] }, - "@deno/shim-deno@0.19.2": { - "integrity": "sha512-q3VTHl44ad8T2Tw2SpeAvghdGOjlnLPDNO2cpOxwMrBE/PVas6geWpbpIgrM+czOCH0yejp0yi8OaTuB+NU40Q==", + "@azure/storage-blob@12.33.0": { + "integrity": "sha512-2SX8oP8PyblUcAFZSg39c8Ls+tFjavM6sBeV+qpw33mRzRhI/5hrFJmJ/x0H9xx5l6ECPvgSP8uPxqTeVbHNIA==", "dependencies": [ - "@deno/shim-deno-test", - "which" + "@azure/abort-controller", + "@azure/core-auth", + "@azure/core-client", + "@azure/core-http-compat", + "@azure/core-lro", + "@azure/core-paging", + "@azure/core-rest-pipeline", + "@azure/core-tracing", + "@azure/core-util", + "@azure/core-xml", + "@azure/logger", + "@azure/storage-common", + "events", + "tslib" ] }, - "@types/deno@2.7.0": { - "integrity": "sha512-Y6fWcV8KpYeO3Lik/RiYnUxtr/LWqoeACciU0CA+dr1U/45tGgaX3HWQQAPCNN53raN4Rr4Fht4y6qsUH57fDA==" + "@azure/storage-common@12.5.0_@azure+core-client@1.11.0": { + "integrity": "sha512-bttzuhQiCIwrkzjPDA+AtAR7dg19L/CC6ztcqJ5LfvWpXuys9mHp0UQ0udYnoUvv9SCT9KTR5kqFvFr0e6k0lQ==", + "dependencies": [ + "@azure/abort-controller", + "@azure/core-auth", + "@azure/core-http-compat", + "@azure/core-rest-pipeline", + "@azure/core-tracing", + "@azure/core-util", + "@azure/logger", + "events", + "tslib" + ] + }, + "@jsr/std__assert@1.0.19": { + "integrity": "sha512-pEj6RPkGbqlgRmyKwATp4cUs6+ijxtdrv3bq8v1d2I2CEcMEyPaO8cVKro61wGRDH4cNg8Zx6haztvK/9m7gkA==", + "dependencies": [ + "@jsr/std__internal" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__assert/1.0.19.tgz" + }, + "@jsr/std__async@1.5.0": { + "integrity": "sha512-I2Qekl1oQYM+dpI2RQUCKi1TBuaA7Od3LNzxcqls9s1e2ytiZyREL4403qHJ6UbLNAwbqO2ZhdWAWDUQAajyiA==", + "dependencies": [ + "@jsr/std__data-structures" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__async/1.5.0.tgz" + }, + "@jsr/std__bytes@1.0.6": { + "integrity": "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA==", + "tarball": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz" + }, + "@jsr/std__data-structures@1.1.2": { + "integrity": "sha512-YaNAaUYGr3qBnq8LJ0/FSW6tsnDBMX7hcPuGU7Zi/+aWlAJsZ4OroBNSpRIflizbOvCJCycAnXe9yLjIwDF2KA==", + "dependencies": [ + "@jsr/std__assert" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__data-structures/1.1.2.tgz" + }, + "@jsr/std__encoding@1.0.11": { + "integrity": "sha512-4SmW8WXf4x4FjkxVpxbir06QYwxw/h/2+RU5A66iKXbJC9WAYCICZkH01mcOGj8FgSitF/lSTlT585RZfo8Ukg==", + "tarball": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.11.tgz" + }, + "@jsr/std__expect@1.0.20": { + "integrity": "sha512-AkDNshk9qIKP7OzRcSZ9qb4txPJVOxd2mDWl0XtU19eZecIzcNrxrG9gnz1j5t+U+vm6AEHkjMdwnvIfFQsJ7g==", + "dependencies": [ + "@jsr/std__assert", + "@jsr/std__internal", + "@jsr/std__path" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__expect/1.0.20.tgz" + }, + "@jsr/std__internal@1.0.14": { + "integrity": "sha512-JT8b/t40WcR9q0GDwRZUooY7aXeXnFal8iidE/5TckdAFtvpVAsaJ71/Xgf1SfoChs41s6CZkuCYM8toaNgdvA==", + "tarball": "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.14.tgz" + }, + "@jsr/std__path@1.1.6": { + "integrity": "sha512-H1Cmg6z8jFyIsQbVV+vZB4eOx4k6Qg8lyUM6l7nZysj5MZumg89kb0M1bsPBTGQis3fw6mJFkJELo5+AK6oCFA==", + "dependencies": [ + "@jsr/std__internal" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__path/1.1.6.tgz" + }, + "@jsr/std__streams@1.1.2": { + "integrity": "sha512-TNg+Mr/vE2Wg+JDdZpVj9umEyzcmSAOtcp0ildlyVCjHy0/fJ2BJZgO8PQXA/SwJOchvKLaMafZzfFkm9PO13A==", + "dependencies": [ + "@jsr/std__bytes" + ], + "tarball": "https://npm.jsr.io/~/11/@jsr/std__streams/1.1.2.tgz" + }, + "@jsr/std__xml@0.2.0": { + "integrity": "sha512-uuUkBEiLfxa8P6cu9Sl8MjRNNzf5v96UbYmBMn+YE7+7OCBKoCLctjpSXf8vttWTBEM+Raa4FZ6B09L2CPl/Yg==", + "tarball": "https://npm.jsr.io/~/11/@jsr/std__xml/0.2.0.tgz" + }, + "@nodable/entities@3.0.0": { + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==" + }, + "@oxc-project/types@0.144.0": { + "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==" + }, + "@playwright/test@1.62.1": { + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dependencies": [ + "playwright" + ], + "bin": true + }, + "@rolldown/binding-android-arm64@1.2.4": { + "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@rolldown/binding-darwin-arm64@1.2.4": { + "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@rolldown/binding-darwin-x64@1.2.4": { + "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@rolldown/binding-freebsd-x64@1.2.4": { + "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@rolldown/binding-linux-arm-gnueabihf@1.2.4": { + "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@rolldown/binding-linux-arm64-gnu@1.2.4": { + "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rolldown/binding-linux-arm64-musl@1.2.4": { + "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@rolldown/binding-linux-ppc64-gnu@1.2.4": { + "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@rolldown/binding-linux-s390x-gnu@1.2.4": { + "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@rolldown/binding-linux-x64-gnu@1.2.4": { + "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rolldown/binding-linux-x64-musl@1.2.4": { + "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@rolldown/binding-openharmony-arm64@1.2.4": { + "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@rolldown/binding-win32-arm64-msvc@1.2.4": { + "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@rolldown/binding-win32-x64-msvc@1.2.4": { + "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@rolldown/pluginutils@1.0.1": { + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==" + }, + "@smithy/core@3.33.0": { + "integrity": "sha512-uKbkxgqLyepQDZoq8aRSdUqD1ID//rOqG96ixBhp++O7vBtmwYM6fwldGhr9HJP0iYrdc7GP/AlgzPWEZIrNRg==", + "dependencies": [ + "@smithy/types", + "tslib" + ] + }, + "@smithy/credential-provider-imds@4.5.0": { + "integrity": "sha512-2jsPi+7Zv2hSzD9IXR9D7DTqSn7mv4XalzRm+bESh53jiaUS3NKEUbpQFTJP0HhQy9qzZvluxQ3yS24zdRrqsA==", + "dependencies": [ + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@smithy/fetch-http-handler@5.7.0": { + "integrity": "sha512-W/exA8T0LEzCQtJ02w4IzaEQPIspgarqZprb7W8FwnYiDowgCrjl2fTQ6FvuSSUnJORuepBF81abmBJwqh+0XQ==", + "dependencies": [ + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@smithy/node-http-handler@4.11.0": { + "integrity": "sha512-ssHIZsadPUA3lGdnoByxfnjtb9xPYQLvdfJRLKIwxOoa6tO1suG4sLFSsgd7D/CsvYd8QbBIuKTImuJha5l6aQ==", + "dependencies": [ + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@smithy/signature-v4@5.7.0": { + "integrity": "sha512-hCynhm22wMJ8wTF9crcwu8mxggtUrSLLJgDcGUvYFBqpofxycYJCGKOMYg4xtPPFtgNiDJSYmhsWLTrcU/g59Q==", + "dependencies": [ + "@smithy/core", + "@smithy/types", + "tslib" + ] + }, + "@smithy/types@4.17.0": { + "integrity": "sha512-Aw4joiM0ZdErpo39lCj8phT2lxoiKZV+KZzBxnnQhWVtU2Is/WffQSL04uUWRcXUse9Ln8vXZK6V/FwqRVnQpg==", + "dependencies": [ + "tslib" + ] }, "@types/node@26.2.0": { "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", @@ -30,46 +611,484 @@ "undici-types" ] }, - "@types/sharedworker@0.0.229": { - "integrity": "sha512-0gPmMzELgb8NngUbvfpWgE73h2jY5zpHrUOswFFcqXFpmbTWEzvJtaB1ZYxv8u4HRJStgwoFtXDFefQwL24Sxg==" + "@typescript/typescript-aix-ppc64@7.0.2": { + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@typescript/typescript-darwin-arm64@7.0.2": { + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@typescript/typescript-darwin-x64@7.0.2": { + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@typescript/typescript-freebsd-arm64@7.0.2": { + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@typescript/typescript-freebsd-x64@7.0.2": { + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@typescript/typescript-linux-arm64@7.0.2": { + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@typescript/typescript-linux-arm@7.0.2": { + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@typescript/typescript-linux-loong64@7.0.2": { + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@typescript/typescript-linux-mips64el@7.0.2": { + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@typescript/typescript-linux-ppc64@7.0.2": { + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@typescript/typescript-linux-riscv64@7.0.2": { + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@typescript/typescript-linux-s390x@7.0.2": { + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@typescript/typescript-linux-x64@7.0.2": { + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@typescript/typescript-netbsd-arm64@7.0.2": { + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@typescript/typescript-netbsd-x64@7.0.2": { + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@typescript/typescript-openbsd-arm64@7.0.2": { + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@typescript/typescript-openbsd-x64@7.0.2": { + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@typescript/typescript-sunos-x64@7.0.2": { + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@typescript/typescript-win32-arm64@7.0.2": { + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@typescript/typescript-win32-x64@7.0.2": { + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@typespec/ts-http-runtime@0.3.8": { + "integrity": "sha512-bLMpVcWZNzq6lYOybwFwOAR1IXKcHnhUNqYeHjl1bET/qE3jFPFH+p8Wrh3rU4xwdnifPxmKNESBYnvnmc75aA==", + "dependencies": [ + "http-proxy-agent", + "https-proxy-agent", + "tslib" + ] + }, + "agent-base@7.1.4": { + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" + }, + "anynum@1.0.1": { + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==" + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bowser@2.14.1": { + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==" + }, + "buffer@5.6.0": { + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "debug@4.4.3": { + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": [ + "ms" + ] + }, + "detect-libc@2.1.2": { + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==" }, "drizzle-orm@0.45.2": { "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==" }, - "isexe@3.1.5": { - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==" + "events@3.3.0": { + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-xml-builder@1.3.1": { + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "dependencies": [ + "path-expression-matcher", + "xml-naming" + ] + }, + "fast-xml-parser@5.10.1": { + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", + "dependencies": [ + "@nodable/entities", + "fast-xml-builder", + "is-unsafe", + "path-expression-matcher", + "strnum", + "xml-naming" + ], + "bin": true + }, + "fdir@6.5.0_picomatch@4.0.5": { + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dependencies": [ + "picomatch" + ], + "optionalPeers": [ + "picomatch" + ] + }, + "fsevents@2.3.2": { + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "os": ["darwin"], + "scripts": true + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "http-proxy-agent@7.0.2": { + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": [ + "agent-base", + "debug" + ] + }, + "https-proxy-agent@7.0.6": { + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": [ + "agent-base", + "debug" + ] + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits@2.0.4": { + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-unsafe@2.0.0": { + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==" + }, + "lightningcss-android-arm64@1.33.0": { + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "os": ["android"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-arm64@1.33.0": { + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "lightningcss-darwin-x64@1.33.0": { + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "lightningcss-freebsd-x64@1.33.0": { + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "lightningcss-linux-arm-gnueabihf@1.33.0": { + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "os": ["linux"], + "cpu": ["arm"] + }, + "lightningcss-linux-arm64-gnu@1.33.0": { + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-arm64-musl@1.33.0": { + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "lightningcss-linux-x64-gnu@1.33.0": { + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-linux-x64-musl@1.33.0": { + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "lightningcss-win32-arm64-msvc@1.33.0": { + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "lightningcss-win32-x64-msvc@1.33.0": { + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "os": ["win32"], + "cpu": ["x64"] + }, + "lightningcss@1.33.0": { + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dependencies": [ + "detect-libc" + ], + "optionalDependencies": [ + "lightningcss-android-arm64", + "lightningcss-darwin-arm64", + "lightningcss-darwin-x64", + "lightningcss-freebsd-x64", + "lightningcss-linux-arm-gnueabihf", + "lightningcss-linux-arm64-gnu", + "lightningcss-linux-arm64-musl", + "lightningcss-linux-x64-gnu", + "lightningcss-linux-x64-musl", + "lightningcss-win32-arm64-msvc", + "lightningcss-win32-x64-msvc" + ] }, "mitata@1.0.34": { "integrity": "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==" }, + "ms@2.1.3": { + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "nanoid@3.3.18": { + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "bin": true + }, + "path-expression-matcher@1.6.2": { + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==" + }, + "picocolors@1.1.1": { + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch@4.0.5": { + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==" + }, + "playwright-core@1.62.1": { + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "bin": true + }, + "playwright@1.62.1": { + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "dependencies": [ + "playwright-core" + ], + "optionalDependencies": [ + "fsevents@2.3.2" + ], + "bin": true + }, + "postcss@8.5.26": { + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "readable-stream@3.6.2": { + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": [ + "inherits", + "string_decoder", + "util-deprecate" + ] + }, + "rolldown@1.2.4": { + "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "dependencies": [ + "@oxc-project/types", + "@rolldown/pluginutils" + ], + "optionalDependencies": [ + "@rolldown/binding-android-arm64", + "@rolldown/binding-darwin-arm64", + "@rolldown/binding-darwin-x64", + "@rolldown/binding-freebsd-x64", + "@rolldown/binding-linux-arm-gnueabihf", + "@rolldown/binding-linux-arm64-gnu", + "@rolldown/binding-linux-arm64-musl", + "@rolldown/binding-linux-ppc64-gnu", + "@rolldown/binding-linux-s390x-gnu", + "@rolldown/binding-linux-x64-gnu", + "@rolldown/binding-linux-x64-musl", + "@rolldown/binding-openharmony-arm64", + "@rolldown/binding-win32-arm64-msvc", + "@rolldown/binding-win32-x64-msvc" + ], + "bin": true + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "stream-browserify@3.0.0": { + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "dependencies": [ + "inherits", + "readable-stream" + ] + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer" + ] + }, + "strnum@2.4.2": { + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", + "dependencies": [ + "anynum" + ] + }, + "tinyglobby@0.2.17": { + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dependencies": [ + "fdir", + "picomatch" + ] + }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "typescript@7.0.2": { + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "optionalDependencies": [ + "@typescript/typescript-aix-ppc64", + "@typescript/typescript-darwin-arm64", + "@typescript/typescript-darwin-x64", + "@typescript/typescript-freebsd-arm64", + "@typescript/typescript-freebsd-x64", + "@typescript/typescript-linux-arm", + "@typescript/typescript-linux-arm64", + "@typescript/typescript-linux-loong64", + "@typescript/typescript-linux-mips64el", + "@typescript/typescript-linux-ppc64", + "@typescript/typescript-linux-riscv64", + "@typescript/typescript-linux-s390x", + "@typescript/typescript-linux-x64", + "@typescript/typescript-netbsd-arm64", + "@typescript/typescript-netbsd-x64", + "@typescript/typescript-openbsd-arm64", + "@typescript/typescript-openbsd-x64", + "@typescript/typescript-sunos-x64", + "@typescript/typescript-win32-arm64", + "@typescript/typescript-win32-x64" + ], + "bin": true + }, "undici-types@8.3.0": { "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==" }, - "which@4.0.0": { - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "util-deprecate@1.0.2": { + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "vite@8.2.1_@types+node@26.2.0": { + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dependencies": [ - "isexe" + "@types/node", + "lightningcss", + "picomatch", + "postcss", + "rolldown", + "tinyglobby" + ], + "optionalDependencies": [ + "fsevents@2.3.3" + ], + "optionalPeers": [ + "@types/node" ], "bin": true }, + "xml-naming@0.3.0": { + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==" + }, "zod@4.4.3": { "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==" } }, "workspace": { "dependencies": [ - "npm:@types/node@*", + "jsr:@std/async@^1.5.0", + "jsr:@std/bytes@^1.0.6", + "jsr:@std/encoding@^1.0.11", + "jsr:@std/expect@^1.0.20", + "jsr:@std/path@^1.1.6", + "jsr:@std/streams@^1.1.2", + "jsr:@std/xml@0.2", + "npm:@aws-sdk/client-s3@^3.1098.0", + "npm:@aws-sdk/lib-storage@^3.1100.0", + "npm:@azure/storage-blob@^12.33.0", + "npm:@playwright/test@^1.62.1", "npm:drizzle-orm@~0.45.2", "npm:mitata@^1.0.34", + "npm:vite@^8.2.1", "npm:zod@^4.4.3" ], "packageJson": { "dependencies": [ - "npm:@deno/shim-deno@~0.19.2", - "npm:@types/deno@^2.7.0", + "npm:@aws-sdk/client-s3@^3.1098.0", + "npm:@aws-sdk/lib-storage@^3.1100.0", + "npm:@azure/storage-blob@^12.33.0", + "npm:@jsr/std__async@^1.5.0", + "npm:@jsr/std__bytes@^1.0.6", + "npm:@jsr/std__encoding@^1.0.11", + "npm:@jsr/std__expect@^1.0.20", + "npm:@jsr/std__path@^1.1.6", + "npm:@jsr/std__streams@^1.1.2", + "npm:@jsr/std__xml@0.2", + "npm:@playwright/test@^1.62.1", "npm:@types/node@^26.2.0", - "npm:@types/sharedworker@^0.0.229", + "npm:drizzle-orm@~0.45.2", "npm:mitata@^1.0.34", + "npm:typescript@^7.0.2", + "npm:vite@^8.2.1", "npm:zod@^4.4.3" ] } diff --git a/mise.toml b/mise.toml index 8096836..f2ab5ce 100644 --- a/mise.toml +++ b/mise.toml @@ -3,7 +3,8 @@ monorepo_root = true [tools] node = "26" -deno = "2.9" +deno = "2" +bun = "1.3.14" pnpm = "11.9.0" [settings] diff --git a/package.json b/package.json index 44595b0..cb1bf16 100644 --- a/package.json +++ b/package.json @@ -1,49 +1,139 @@ { "name": "@okikio/opfs", "version": "0.0.1", + "private": true, "type": "module", "sideEffects": false, + "description": "An OPFS-shaped filesystem abstraction for browsers, Deno, Bun, Node, and storage adapters.", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/okikio/opfs.git" + }, + "homepage": "https://github.com/okikio/opfs#readme", + "bugs": { + "url": "https://github.com/okikio/opfs/issues" + }, + "keywords": [ + "opfs", + "filesystem", + "origin-private-file-system", + "deno", + "bun", + "node", + "browser", + "storage", + "object-storage", + "s3", + "azure-blob", + "indexeddb", + "unstorage" + ], + "packageManager": "pnpm@11.9.0", + "engines": { + "node": ">=22.18.0" + }, + "scripts": { + "check": "mise run check", + "test": "mise run test", + "fmt": "mise run fmt", + "bench": "mise run bench" + }, "exports": { ".": "./mod.ts", "./adapter": "./src/adapter/definition.ts", "./adapter/opfs": "./src/adapter/opfs.ts", "./adapter/memory": "./src/adapter/memory.ts", "./adapter/deno": "./src/adapter/deno.ts", + "./adapter/deno-kv": "./src/adapter/deno-kv.ts", "./adapter/node": "./src/adapter/node.ts", "./adapter/bun": "./src/adapter/bun.ts", "./adapter/record": "./src/adapter/record.ts", + "./adapter/object": "./src/adapter/object.ts", + "./adapter/s3": "./src/adapter/s3.ts", + "./adapter/azure": "./src/adapter/azure.ts", + "./adapter/localstorage": "./src/adapter/localstorage.ts", + "./adapter/indexeddb": "./src/adapter/indexeddb.ts", + "./adapter/cache": "./src/adapter/cache.ts", + "./adapter/sqlite": "./src/adapter/sqlite.ts", "./adapter/unstorage": "./src/adapter/unstorage.ts", "./adapter/rxdb": "./src/adapter/rxdb.ts", "./adapter/db0": "./src/adapter/db0.ts", "./adapter/drizzle": "./src/adapter/drizzle.ts", + "./driver/kv": "./src/driver/kv.ts", "./driver/unstorage": "./src/driver/unstorage.ts", + "./s3": "./src/s3.ts", + "./azure": "./src/azure.ts", "./iframe": "./src/iframe.ts", "./path": "./src/path.ts", - "./schema": "./src/schema.ts" + "./schema": "./src/schema.ts", + "./bridge": "./src/bridge.ts", + "./bridge/definition": "./src/bridge/definition.ts", + "./bridge/unstorage": "./src/bridge/unstorage.ts", + "./bridge/rxdb": "./src/bridge/rxdb.ts", + "./bridge/db0": "./src/bridge/db0.ts", + "./bridge/drizzle": "./src/bridge/drizzle.ts", + "./bridge/kv": "./src/bridge/kv.ts", + "./capability": "./src/capability.ts", + "./metrics": "./src/metrics.ts", + "./plan": "./src/plan.ts", + "./request": "./src/request.ts" }, "dependencies": { - "zod": "^4.4.3" + "@std/encoding": "npm:@jsr/std__encoding@^1.0.11", + "@std/xml": "npm:@jsr/std__xml@^0.2.0", + "zod": "^4.4.3", + "@std/async": "npm:@jsr/std__async@^1.5.0", + "@std/bytes": "npm:@jsr/std__bytes@^1.0.6", + "@std/path": "npm:@jsr/std__path@^1.1.6", + "@std/streams": "npm:@jsr/std__streams@^1.1.2" }, "peerDependencies": { - "drizzle-orm": "^0.45.3" + "drizzle-orm": "^0.45.2" }, "peerDependenciesMeta": { "drizzle-orm": { "optional": true } }, - "files": [ - "mod.ts", - "src/", - "README.md", - "docs/", - "deno.json" - ], "devDependencies": { - "@deno/shim-deno": "^0.19.2", - "@types/deno": "^2.7.0", + "@playwright/test": "^1.62.1", + "@std/expect": "npm:@jsr/std__expect@^1.0.20", "@types/node": "^26.2.0", - "@types/sharedworker": "^0.0.229", - "mitata": "^1.0.34" + "drizzle-orm": "^0.45.2", + "mitata": "^1.0.34", + "typescript": "^7.0.2", + "vite": "^8.2.1", + "@aws-sdk/client-s3": "^3.1098.0", + "@aws-sdk/lib-storage": "^3.1100.0", + "@azure/storage-blob": "^12.33.0" + }, + "release": { + "branches": [ + "main" + ], + "tagFormat": "opfs@${version}", + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits" + } + ], + [ + "@semantic-release/github", + { + "successComment": false, + "failComment": false, + "releasedLabels": false + } + ] + ] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 365d1be..d40f1e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,93 +1,326 @@ lockfileVersion: '9.0' - settings: autoInstallPeers: true excludeLinksFromLockfile: false - importers: - .: dependencies: zod: specifier: ^4.4.3 version: 4.4.3 devDependencies: - '@deno/shim-deno': - specifier: ^0.19.2 - version: 0.19.2 - '@types/deno': - specifier: ^2.7.0 - version: 2.7.0 + '@std/expect': + specifier: npm:@jsr/std__expect@^1.0.20 + version: '@jsr/std__expect@1.0.20' '@types/node': specifier: ^26.2.0 version: 26.2.0 - '@types/sharedworker': - specifier: ^0.0.229 - version: 0.0.229 + drizzle-orm: + specifier: ^0.45.2 + version: 0.45.2 mitata: specifier: ^1.0.34 version: 1.0.34 - + typescript: + specifier: ^7.0.2 + version: 7.0.2 packages: - - '@deno/shim-deno-test@0.5.0': - resolution: {integrity: sha512-4nMhecpGlPi0cSzT67L+Tm+GOJqvuk8gqHBziqcUQOarnuIax1z96/gJHCSIz2Z0zhxE6Rzwb3IZXPtFh51j+w==} - - '@deno/shim-deno@0.19.2': - resolution: {integrity: sha512-q3VTHl44ad8T2Tw2SpeAvghdGOjlnLPDNO2cpOxwMrBE/PVas6geWpbpIgrM+czOCH0yejp0yi8OaTuB+NU40Q==} - - '@types/deno@2.7.0': - resolution: {integrity: sha512-Y6fWcV8KpYeO3Lik/RiYnUxtr/LWqoeACciU0CA+dr1U/45tGgaX3HWQQAPCNN53raN4Rr4Fht4y6qsUH57fDA==} - + '@jsr/std__assert@1.0.19': + resolution: + integrity: sha512-pEj6RPkGbqlgRmyKwATp4cUs6+ijxtdrv3bq8v1d2I2CEcMEyPaO8cVKro61wGRDH4cNg8Zx6haztvK/9m7gkA== + tarball: https://npm.jsr.io/~/11/@jsr/std__assert/1.0.19.tgz + '@jsr/std__expect@1.0.20': + resolution: + integrity: sha512-AkDNshk9qIKP7OzRcSZ9qb4txPJVOxd2mDWl0XtU19eZecIzcNrxrG9gnz1j5t+U+vm6AEHkjMdwnvIfFQsJ7g== + tarball: https://npm.jsr.io/~/11/@jsr/std__expect/1.0.20.tgz + '@jsr/std__internal@1.0.14': + resolution: + integrity: sha512-JT8b/t40WcR9q0GDwRZUooY7aXeXnFal8iidE/5TckdAFtvpVAsaJ71/Xgf1SfoChs41s6CZkuCYM8toaNgdvA== + tarball: https://npm.jsr.io/~/11/@jsr/std__internal/1.0.14.tgz + '@jsr/std__path@1.1.6': + resolution: + integrity: sha512-H1Cmg6z8jFyIsQbVV+vZB4eOx4k6Qg8lyUM6l7nZysj5MZumg89kb0M1bsPBTGQis3fw6mJFkJELo5+AK6oCFA== + tarball: https://npm.jsr.io/~/11/@jsr/std__path/1.1.6.tgz '@types/node@26.2.0': - resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} - - '@types/sharedworker@0.0.229': - resolution: {integrity: sha512-0gPmMzELgb8NngUbvfpWgE73h2jY5zpHrUOswFFcqXFpmbTWEzvJtaB1ZYxv8u4HRJStgwoFtXDFefQwL24Sxg==} - - isexe@3.1.5: - resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} - engines: {node: '>=18'} - + resolution: + integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg== + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: + integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ== + engines: + node: '>=16.20.0' + cpu: + - ppc64 + os: + - aix + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: + integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - darwin + '@typescript/typescript-darwin-x64@7.0.2': + resolution: + integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - darwin + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: + integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - freebsd + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: + integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - freebsd + '@typescript/typescript-linux-arm64@7.0.2': + resolution: + integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - linux + '@typescript/typescript-linux-arm@7.0.2': + resolution: + integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ== + engines: + node: '>=16.20.0' + cpu: + - arm + os: + - linux + '@typescript/typescript-linux-loong64@7.0.2': + resolution: + integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ== + engines: + node: '>=16.20.0' + cpu: + - loong64 + os: + - linux + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: + integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA== + engines: + node: '>=16.20.0' + cpu: + - mips64el + os: + - linux + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: + integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA== + engines: + node: '>=16.20.0' + cpu: + - ppc64 + os: + - linux + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: + integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ== + engines: + node: '>=16.20.0' + cpu: + - riscv64 + os: + - linux + '@typescript/typescript-linux-s390x@7.0.2': + resolution: + integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw== + engines: + node: '>=16.20.0' + cpu: + - s390x + os: + - linux + '@typescript/typescript-linux-x64@7.0.2': + resolution: + integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - linux + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: + integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - netbsd + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: + integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - netbsd + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: + integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - openbsd + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: + integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - openbsd + '@typescript/typescript-sunos-x64@7.0.2': + resolution: + integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - sunos + '@typescript/typescript-win32-arm64@7.0.2': + resolution: + integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ== + engines: + node: '>=16.20.0' + cpu: + - arm64 + os: + - win32 + '@typescript/typescript-win32-x64@7.0.2': + resolution: + integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g== + engines: + node: '>=16.20.0' + cpu: + - x64 + os: + - win32 mitata@1.0.34: - resolution: {integrity: sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==} - - undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} + resolution: + integrity: sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA== + typescript@7.0.2: + resolution: + integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA== + engines: + node: '>=16.20.0' hasBin: true - + undici-types@8.3.0: + resolution: + integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - + resolution: + integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== + drizzle-orm@0.45.2: + resolution: + integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q== snapshots: - - '@deno/shim-deno-test@0.5.0': {} - - '@deno/shim-deno@0.19.2': + '@jsr/std__assert@1.0.19': + dependencies: + '@jsr/std__internal': 1.0.14 + '@jsr/std__expect@1.0.20': dependencies: - '@deno/shim-deno-test': 0.5.0 - which: 4.0.0 - - '@types/deno@2.7.0': {} - + '@jsr/std__assert': 1.0.19 + '@jsr/std__internal': 1.0.14 + '@jsr/std__path': 1.1.6 + '@jsr/std__internal@1.0.14': {} + '@jsr/std__path@1.1.6': + dependencies: + '@jsr/std__internal': 1.0.14 '@types/node@26.2.0': dependencies: undici-types: 8.3.0 - - '@types/sharedworker@0.0.229': {} - - isexe@3.1.5: {} - + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + '@typescript/typescript-linux-arm@7.0.2': + optional: true + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + '@typescript/typescript-linux-x64@7.0.2': + optional: true + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + '@typescript/typescript-win32-x64@7.0.2': + optional: true mitata@1.0.34: {} - + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 undici-types@8.3.0: {} - - which@4.0.0: - dependencies: - isexe: 3.1.5 - zod@4.4.3: {} + drizzle-orm@0.45.2: {} diff --git a/tsconfig.json b/tsconfig.json index f68b280..c971702 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,49 @@ { "compilerOptions": { - "target": "esnext", - "module": "esnext", - "moduleResolution": "bundler", - "lib": ["DOM", "DOM.Iterable"], + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": [ + "ESNext", + "DOM", + "DOM.Iterable", + "DOM.AsyncIterable" + ], "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "useUnknownInCatchVariables": true, "verbatimModuleSyntax": true, + "erasableSyntaxOnly": true, "allowImportingTsExtensions": true, "noEmit": true, "skipLibCheck": true, - "types": ["node", "deno"] - } + "types": [ + "node" + ] + }, + "include": [ + "mod.ts", + "src/**/*.ts", + "tests/path.test.ts", + "tests/memory.test.ts", + "tests/filesystem.test.ts", + "tests/ecosystems.test.ts", + "tests/chunk.test.ts", + "tests/object.test.ts", + "tests/s3.test.ts", + "tests/azure.test.ts", + "tests/sqlite.test.ts", + "tests/deno-kv-partition.test.ts", + "tests/node.test.ts" + ], + "exclude": [ + "src/adapter/deno.ts", + "src/adapter/bun.ts", + "tests/browser/**", + "tests/deno.test.ts", + "tests/deno-kv.test.ts", + "tests/bun.test.ts", + "bench/**" + ] }