diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e6fa8dc --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,42 @@ +name: Release + +on: + release: + types: [published] + +permissions: + id-token: write + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + name: install pnpm + with: + run_install: false + + - name: install node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: install dependencies + run: pnpm install --frozen-lockfile + + - name: lint + run: pnpm /lint:/ + + - name: run tests + run: pnpm test + + - name: build + run: pnpm build + + - name: publish + run: pnpm publish --no-git-checks --access public diff --git a/LICENSE b/LICENSE index b483539..44afd09 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Ori Livni +Copyright (c) Ori Livni Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/index.ts b/lib/index.ts index c9f3d5e..19d7f6f 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,401 +1,7 @@ -/** - * Used to access to the real `Map` contains all data of storage instance - */ -const STORAGE_SYMBOL = Symbol("BrowserStorage storage"); - -//#region Helpers -/** - * Return the value that closest to how browser convert to string values in `localStorage` - */ -function getString(value: any) { - return "" + value; -} - -function getStorage(instance: BrowserStorage) { - return (instance as any)[STORAGE_SYMBOL] as StorageMap; -} -//#endregion - -//#region StorageMap -/** - * Wrapper to `Map` with needed modifications to work more as `localStorage` is - */ -class StorageMap { - #map = new Map(); - - //#region Listeners - #clearListeners = new Set<() => void>(); - #propertyChangeListeners = new Set< - (key: string, value: string | null) => void - >(); - - onPropertyChange( - listener: (key: string, value: string | null) => void, - ): () => void { - this.#propertyChangeListeners.add(listener); - return () => void this.#propertyChangeListeners.delete(listener); - } - - onClearChange(listener: () => void): () => void { - this.#clearListeners.add(listener); - return () => void this.#clearListeners.delete(listener); - } - //#endregion - - //#region Map needed functionality - has(key: PropertyKey) { - return this.#map.has(getString(key)); - } - - get(key: PropertyKey) { - return this.#map.get(getString(key)); - } - - set(key: PropertyKey, value: string) { - const k = getString(key); - const v = getString(value); - this.#map.set(k, v); - - if (this.#propertyChangeListeners.size !== 0) { - for (const cb of this.#propertyChangeListeners) { - cb(k, v); - } - } - - return this; - } - - clear() { - this.#map.clear(); - - if (this.#clearListeners.size !== 0) { - for (const cb of this.#clearListeners) cb(); - } - } - - delete(key: PropertyKey) { - const k = getString(key); - const returnValue = this.#map.delete(k); - - if (this.#propertyChangeListeners.size !== 0) { - for (const cb of this.#propertyChangeListeners) { - cb(k, null); - } - } - - return returnValue; - } - - get size() { - return this.#map.size; - } - - *keys() { - yield* this.#map.keys(); - } - //#endregion -} -//#endregion - -//#region Proxy creator -/** - * @todo `defineProperty` support. (Never actually see support to such as edge case) - */ -function createProxy(instance: BrowserStorage) { - const storage = new StorageMap(); - - //#region Proxy wrapper - const proxy = new Proxy(instance, { - has: (target, key) => { - if (Reflect.has(target, key)) { - return true; - } - - return typeof key === "string" ? storage.has(key) : false; - }, - - get: (target, key, receiver) => { - if (key === STORAGE_SYMBOL) return storage; - - if (Reflect.has(target.constructor.prototype, key)) { - return Reflect.get(target.constructor.prototype, key, receiver); - } - - if (Reflect.has(target, key)) { - return Reflect.get(target, key, receiver); - } else { - return typeof key !== "symbol" ? storage.get(key) : undefined; - } - }, - - ownKeys: (target) => { - const keys = Reflect.ownKeys(target); - - for (const key of storage.keys()) { - if (!Reflect.has(target.constructor.prototype, key)) { - keys.push(key); - } - } - - return keys; - }, - - getOwnPropertyDescriptor: (target, property) => { - if (Reflect.has(target.constructor.prototype, property)) { - return undefined; - } - - if (Reflect.has(target, property)) { - const value = Reflect.getOwnPropertyDescriptor(target, property); - if (value !== undefined) return value; - } - - if (typeof property === "string" && storage.has(property)) { - return { - value: storage.get(property), - writable: true, - enumerable: true, - configurable: true, - }; - } - - return undefined; - }, - - set: (target, property, value, receiver) => { - if (typeof property === "symbol") { - return Reflect.set(target, property, value, receiver); - } - - storage.set(property, value); - return true; - }, - - /** - * `configurable`, `enumerable`, `writable` are ignored. - * Getters and setters aren't allowed - */ - defineProperty: (target, property, attributes) => { - if (Reflect.has(target, property)) { - return Reflect.defineProperty(target, property, attributes); - } - - if (attributes.get !== undefined || attributes.set !== undefined) { - throw new TypeError("Accessor properties are not allowed"); - } - - if (typeof property === "string") { - storage.set(property, attributes.value); - return true; - } - - if (typeof property === "symbol") { - return Reflect.defineProperty(target, property, attributes); - } - - return false; - }, - - // NOTE: Not sure if there is any case it returns `false` - deleteProperty: (target, property) => { - const result = Reflect.deleteProperty(target, property); - - if (typeof property !== "symbol") { - storage.delete(property); - return true; - } - - return result; - }, - }); - //#endregion - - return proxy; -} -//#endregion - -export class BrowserStorage implements Storage { - [name: string]: any; - - constructor() { - // The magic behind `localStorage` and `sessionStorage` special behavior - // NOTE: In JS if your return an object from `constructor` it become the actual - // object returned from `new` expression - return createProxy(this); - } - - get length(): number { - return getStorage(this).size; - } - - clear(): void { - getStorage(this).clear(); - } - - getItem(key: string): string | null { - if (arguments.length === 0) { - throw new TypeError( - `Storage.getItem: At least 1 argument required, but only ${arguments.length} passed`, - ); - } - - const value = getStorage(this).get(key); - return value == null ? null : value; - } - - key(index: number): string | null { - if (arguments.length === 0) { - throw new TypeError( - `Storage.key: At least 1 argument required, but only ${arguments.length} passed`, - ); - } - - let count = 0; - - const i = index % 0x100000000; - - for (const key of getStorage(this).keys()) { - if (count === i) { - return key; - } - count++; - } - - return null; - } - - removeItem(key: string): void { - if (arguments.length === 0) { - throw new TypeError( - `Storage.removeItem: At least 1 argument required, but only ${arguments.length} passed`, - ); - } - - getStorage(this).delete(key); - } - - /** - * @todo Support `QuotaExceededError` throwing for settings "size limit" - */ - setItem(key: string, value: string): void { - if (arguments.length < 2) { - throw new TypeError( - `Storage.setItem: At least 2 argument required, but only ${arguments.length} passed`, - ); - } - - getStorage(this).set(key, value); - } -} - -//#region Public helpers -/** - * Used mainly for tests or if you want to push many items with overcome some bureaucracy - * @param storage - */ -export function getStoragePrivateStorageMap( - storage: BrowserStorage, -): StorageMap { - return getStorage(storage); -} - -export function subscribePropertyChange( - storage: BrowserStorage, - listener: (key: string, value: string | null) => void, -): () => void { - const s = getStoragePrivateStorageMap(storage); - return s.onPropertyChange(listener); -} - -export function subscribeClear( - storage: BrowserStorage, - listener: () => void, -): () => void { - const s = getStoragePrivateStorageMap(storage); - return s.onClearChange(listener); -} - -/** - * Set given key and emit `storage` event on `window`. - * Works only on browser context. - * `storageArea` can only be `null` or `Storage` instance that cannot be sub-class currently. - * So we patch `storageArea` with `Object.defineProperty`. - * @param storage - * @param key - * @param value (`null` for deleting properties) - */ -export function setWithEmitStorage( - storage: BrowserStorage, - key: string, - newValue: string, -): void { - const oldValue = storage.getItem(key); - storage.setItem(key, newValue); - - // Works only on browser context - if (typeof window === "undefined" || typeof StorageEvent === "undefined") { - return; - } - - // Gets normalized value first - const newValue_ = storage.getItem(key); - - // Emits only on change - if (oldValue !== newValue_) { - const event = new StorageEvent("storage", { - storageArea: null, - key, - newValue: newValue_, - oldValue, - bubbles: false, - cancelable: false, - }); - - Object.defineProperty(event, "storageArea", { - get: () => storage, - }); - - window.dispatchEvent(event); - } -} - -/** - * Remove given key and emit `storage` event on `window`. - * Works only on browser context. - * `storageArea` can only be `null` or `Storage` instance that cannot be sub-class currently. - * So we patch `storageArea` with `Object.defineProperty`. - * @param storage - * @param key - * @param value (`null` for deleting properties) - */ -export function removeWithEmitStorage( - storage: BrowserStorage, - key: string, -): void { - const oldValue = storage.getItem(key); - storage.removeItem(key); - - // Works only on browser context - if (typeof window === "undefined" || typeof StorageEvent === "undefined") { - return; - } - - // Emits only on change - if (oldValue !== null) { - const event = new StorageEvent("storage", { - storageArea: null, - key, - newValue: null, - oldValue, - bubbles: false, - cancelable: false, - }); - - Object.defineProperty(event, "storageArea", { - get: () => storage, - }); - - window.dispatchEvent(event); - } -} -//#endregion +export { + BrowserStorage, + removeWithEmitStorage, + setWithEmitStorage, + subscribeClear, + subscribePropertyChange, +} from "./storage"; diff --git a/lib/private/create-proxy.ts b/lib/private/create-proxy.ts new file mode 100644 index 0000000..5db5ad6 --- /dev/null +++ b/lib/private/create-proxy.ts @@ -0,0 +1,113 @@ +import type { BrowserStorage } from "../storage"; +import { STORAGE_SYMBOL, StorageMap } from "./storage-map"; + +export function createProxy(instance: BrowserStorage) { + const storage = new StorageMap(); + + //#region Proxy wrapper + const proxy = new Proxy(instance, { + has: (target, key) => { + if (Reflect.has(target, key)) { + return true; + } + + return typeof key === "string" ? storage.has(key) : false; + }, + + get: (target, key, receiver) => { + if (key === STORAGE_SYMBOL) return storage; + + if (Reflect.has(target.constructor.prototype, key)) { + return Reflect.get(target.constructor.prototype, key, receiver); + } + + if (Reflect.has(target, key)) { + return Reflect.get(target, key, receiver); + } else { + return typeof key !== "symbol" ? storage.get(key) : undefined; + } + }, + + ownKeys: (target) => { + const keys = Reflect.ownKeys(target); + + for (const key of storage.keys()) { + if (!Reflect.has(target.constructor.prototype, key)) { + keys.push(key); + } + } + + return keys; + }, + + getOwnPropertyDescriptor: (target, property) => { + if (Reflect.has(target.constructor.prototype, property)) { + return undefined; + } + + if (Reflect.has(target, property)) { + const value = Reflect.getOwnPropertyDescriptor(target, property); + if (value !== undefined) return value; + } + + if (typeof property === "string" && storage.has(property)) { + return { + value: storage.get(property), + writable: true, + enumerable: true, + configurable: true, + }; + } + + return undefined; + }, + + set: (target, property, value, receiver) => { + if (typeof property === "symbol") { + return Reflect.set(target, property, value, receiver); + } + + storage.set(property, value); + return true; + }, + + /** + * `configurable`, `enumerable`, `writable` are ignored. + * Getters and setters aren't allowed + */ + defineProperty: (target, property, attributes) => { + if (Reflect.has(target, property)) { + return Reflect.defineProperty(target, property, attributes); + } + + if (attributes.get !== undefined || attributes.set !== undefined) { + throw new TypeError("Accessor properties are not allowed"); + } + + if (typeof property === "string") { + storage.set(property, attributes.value); + return true; + } + + if (typeof property === "symbol") { + return Reflect.defineProperty(target, property, attributes); + } + + return false; + }, + + deleteProperty: (target, property) => { + const result = Reflect.deleteProperty(target, property); + + if (typeof property !== "symbol") { + storage.delete(property); + return true; + } + + return result; + }, + }); + //#endregion + + return proxy; +} diff --git a/lib/private/storage-map.ts b/lib/private/storage-map.ts new file mode 100644 index 0000000..b2de8e1 --- /dev/null +++ b/lib/private/storage-map.ts @@ -0,0 +1,103 @@ +import type { BrowserStorage } from "../storage"; + +export const STORAGE_SYMBOL = Symbol("BrowserStorage storage"); + +export function getStorage(instance: BrowserStorage) { + return (instance as any)[STORAGE_SYMBOL] as StorageMap; +} + +/** + * Used mainly for tests or if you want to push many items with overcome some bureaucracy + * @param storage + */ +export function getStoragePrivateStorageMap( + storage: BrowserStorage, +): StorageMap { + return getStorage(storage); +} + +/** + * Return the value that closest to how browser convert to string values in `localStorage` + */ +function getString(value: any) { + return "" + value; +} + +/** + * Wrapper to `Map` with needed modifications to work more as `localStorage` is + */ +export class StorageMap { + #map = new Map(); + + //#region Listeners + #clearListeners = new Set<() => void>(); + #propertyChangeListeners = new Set< + (key: string, value: string | null) => void + >(); + + onPropertyChange( + listener: (key: string, value: string | null) => void, + ): () => void { + this.#propertyChangeListeners.add(listener); + return () => void this.#propertyChangeListeners.delete(listener); + } + + onClearChange(listener: () => void): () => void { + this.#clearListeners.add(listener); + return () => void this.#clearListeners.delete(listener); + } + //#endregion + + //#region Map needed functionality + has(key: PropertyKey) { + return this.#map.has(getString(key)); + } + + get(key: PropertyKey) { + return this.#map.get(getString(key)); + } + + set(key: PropertyKey, value: string) { + const k = getString(key); + const v = getString(value); + this.#map.set(k, v); + + if (this.#propertyChangeListeners.size !== 0) { + for (const cb of this.#propertyChangeListeners) { + cb(k, v); + } + } + + return this; + } + + clear() { + this.#map.clear(); + + if (this.#clearListeners.size !== 0) { + for (const cb of this.#clearListeners) cb(); + } + } + + delete(key: PropertyKey) { + const k = getString(key); + const returnValue = this.#map.delete(k); + + if (this.#propertyChangeListeners.size !== 0) { + for (const cb of this.#propertyChangeListeners) { + cb(k, null); + } + } + + return returnValue; + } + + get size() { + return this.#map.size; + } + + *keys() { + yield* this.#map.keys(); + } + //#endregion +} diff --git a/lib/index.test.ts b/lib/storage.test.ts similarity index 98% rename from lib/index.test.ts rename to lib/storage.test.ts index d7e6118..9e843d7 100644 --- a/lib/index.test.ts +++ b/lib/storage.test.ts @@ -4,16 +4,16 @@ import { subscribeClear, subscribePropertyChange, BrowserStorage, - getStoragePrivateStorageMap, setWithEmitStorage, removeWithEmitStorage, -} from "./index"; +} from "./storage"; import { withPage } from "../test-utils/with-page"; +import { getStoragePrivateStorageMap } from "./private/storage-map"; /** * Used for tests with puppeteer */ -declare var browserStorage: typeof import("./index"); +declare var browserStorage: typeof import("./storage"); //#endregion //#region Integration diff --git a/lib/storage.ts b/lib/storage.ts new file mode 100644 index 0000000..6054190 --- /dev/null +++ b/lib/storage.ts @@ -0,0 +1,179 @@ +import { getStorage, getStoragePrivateStorageMap } from "./private/storage-map"; +import { createProxy } from "./private/create-proxy"; + +export class BrowserStorage implements Storage { + [name: string]: any; + + constructor() { + // The magic behind `localStorage` and `sessionStorage` special behavior + // NOTE: In JS if your return an object from `constructor` it become the actual + // object returned from `new` expression + return createProxy(this); + } + + get length(): number { + return getStorage(this).size; + } + + clear(): void { + getStorage(this).clear(); + } + + getItem(key: string): string | null { + if (arguments.length === 0) { + throw new TypeError( + `Storage.getItem: At least 1 argument required, but only ${arguments.length} passed`, + ); + } + + const value = getStorage(this).get(key); + return value == null ? null : value; + } + + key(index: number): string | null { + if (arguments.length === 0) { + throw new TypeError( + `Storage.key: At least 1 argument required, but only ${arguments.length} passed`, + ); + } + + let count = 0; + + const i = index % 0x100000000; + + for (const key of getStorage(this).keys()) { + if (count === i) { + return key; + } + count++; + } + + return null; + } + + removeItem(key: string): void { + if (arguments.length === 0) { + throw new TypeError( + `Storage.removeItem: At least 1 argument required, but only ${arguments.length} passed`, + ); + } + + getStorage(this).delete(key); + } + + /** + * @todo Support `QuotaExceededError` throwing for settings "size limit" + */ + setItem(key: string, value: string): void { + if (arguments.length < 2) { + throw new TypeError( + `Storage.setItem: At least 2 argument required, but only ${arguments.length} passed`, + ); + } + + getStorage(this).set(key, value); + } +} + +//#region Public helpers + +export function subscribePropertyChange( + storage: BrowserStorage, + listener: (key: string, value: string | null) => void, +): () => void { + const s = getStoragePrivateStorageMap(storage); + return s.onPropertyChange(listener); +} + +export function subscribeClear( + storage: BrowserStorage, + listener: () => void, +): () => void { + const s = getStoragePrivateStorageMap(storage); + return s.onClearChange(listener); +} + +/** + * Set given key and emit `storage` event on `window`. + * Works only on browser context. + * `storageArea` can only be `null` or `Storage` instance that cannot be sub-class currently. + * So we patch `storageArea` with `Object.defineProperty`. + * @param storage + * @param key + * @param value (`null` for deleting properties) + */ +export function setWithEmitStorage( + storage: BrowserStorage, + key: string, + newValue: string, +): void { + const oldValue = storage.getItem(key); + storage.setItem(key, newValue); + + // Works only on browser context + if (typeof window === "undefined" || typeof StorageEvent === "undefined") { + return; + } + + // Gets normalized value first + const newValue_ = storage.getItem(key); + + // Emits only on change + if (oldValue !== newValue_) { + const event = new StorageEvent("storage", { + storageArea: null, + key, + newValue: newValue_, + oldValue, + bubbles: false, + cancelable: false, + }); + + Object.defineProperty(event, "storageArea", { + get: () => storage, + }); + + window.dispatchEvent(event); + } +} + +/** + * Remove given key and emit `storage` event on `window`. + * Works only on browser context. + * `storageArea` can only be `null` or `Storage` instance that cannot be sub-class currently. + * So we patch `storageArea` with `Object.defineProperty`. + * @param storage + * @param key + * @param value (`null` for deleting properties) + */ +export function removeWithEmitStorage( + storage: BrowserStorage, + key: string, +): void { + const oldValue = storage.getItem(key); + storage.removeItem(key); + + // Works only on browser context + if (typeof window === "undefined" || typeof StorageEvent === "undefined") { + return; + } + + // Emits only on change + if (oldValue !== null) { + const event = new StorageEvent("storage", { + storageArea: null, + key, + newValue: null, + oldValue, + bubbles: false, + cancelable: false, + }); + + Object.defineProperty(event, "storageArea", { + get: () => storage, + }); + + window.dispatchEvent(event); + } +} +//#endregion diff --git a/lib/wpt-tests/defineProperty.window.test.ts b/lib/wpt-tests/defineProperty.window.test.ts index 7b3aae6..809c894 100644 --- a/lib/wpt-tests/defineProperty.window.test.ts +++ b/lib/wpt-tests/defineProperty.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { describe, expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion describe.sequential.each([9, "x"])("%j", function (key) { diff --git a/lib/wpt-tests/missing_arguments.window.test.ts b/lib/wpt-tests/missing_arguments.window.test.ts index f616c5b..11df3d9 100644 --- a/lib/wpt-tests/missing_arguments.window.test.ts +++ b/lib/wpt-tests/missing_arguments.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test.each<(storage: Storage) => unknown>([ diff --git a/lib/wpt-tests/set.window.test.ts b/lib/wpt-tests/set.window.test.ts index f79f097..4cb0a13 100644 --- a/lib/wpt-tests/set.window.test.ts +++ b/lib/wpt-tests/set.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { describe, expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion describe.sequential.each([9, "x"])("Testing key: %j", function (key) { diff --git a/lib/wpt-tests/storage_builtins.window.test.ts b/lib/wpt-tests/storage_builtins.window.test.ts index bce35bb..b9abaa7 100644 --- a/lib/wpt-tests/storage_builtins.window.test.ts +++ b/lib/wpt-tests/storage_builtins.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("Builtins in", function () { diff --git a/lib/wpt-tests/storage_clear.window.test.ts b/lib/wpt-tests/storage_clear.window.test.ts index 20bab3e..904f8d5 100644 --- a/lib/wpt-tests/storage_clear.window.test.ts +++ b/lib/wpt-tests/storage_clear.window.test.ts @@ -18,7 +18,7 @@ */ import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; test("Clear in", function () { const storage = new BrowserStorage(); diff --git a/lib/wpt-tests/storage_enumerate.window.test.ts b/lib/wpt-tests/storage_enumerate.window.test.ts index 29fd116..a737aa6 100644 --- a/lib/wpt-tests/storage_enumerate.window.test.ts +++ b/lib/wpt-tests/storage_enumerate.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("enumerate a Storage object and get only the keys as a result and the built-in properties of the Storage object should be ignored", function () { diff --git a/lib/wpt-tests/storage_functions_not_overwritten.window.test.ts b/lib/wpt-tests/storage_functions_not_overwritten.window.test.ts index a8820ba..89f258e 100644 --- a/lib/wpt-tests/storage_functions_not_overwritten.window.test.ts +++ b/lib/wpt-tests/storage_functions_not_overwritten.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("should be not rendered unusable by setting a key with the same name as a storage function such that the function is hidden", () => { diff --git a/lib/wpt-tests/storage_getitem.window.test.ts b/lib/wpt-tests/storage_getitem.window.test.ts index d753f34..f414c6d 100644 --- a/lib/wpt-tests/storage_getitem.window.test.ts +++ b/lib/wpt-tests/storage_getitem.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { describe, expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion describe("Get value by getItem(key) and named access", () => { diff --git a/lib/wpt-tests/storage_in.window.test.ts b/lib/wpt-tests/storage_in.window.test.ts index a11de6c..35f71d9 100644 --- a/lib/wpt-tests/storage_in.window.test.ts +++ b/lib/wpt-tests/storage_in.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("The in operator in storage: property access", function () { diff --git a/lib/wpt-tests/storage_indexing.window.test.ts b/lib/wpt-tests/storage_indexing.window.test.ts index dd9d234..fba9284 100644 --- a/lib/wpt-tests/storage_indexing.window.test.ts +++ b/lib/wpt-tests/storage_indexing.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { describe, expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion describe("Indexed getter", function () { diff --git a/lib/wpt-tests/storage_key.window.test.ts b/lib/wpt-tests/storage_key.window.test.ts index fa9d5cf..cc348fd 100644 --- a/lib/wpt-tests/storage_key.window.test.ts +++ b/lib/wpt-tests/storage_key.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { describe, expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion describe(".key", function () { diff --git a/lib/wpt-tests/storage_key_empty_string.test.ts b/lib/wpt-tests/storage_key_empty_string.test.ts index 0b6274e..dae6c61 100644 --- a/lib/wpt-tests/storage_key_empty_string.test.ts +++ b/lib/wpt-tests/storage_key_empty_string.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test(".key with empty string", function () { diff --git a/lib/wpt-tests/storage_length.window.test.ts b/lib/wpt-tests/storage_length.window.test.ts index 519dd71..5c041f7 100644 --- a/lib/wpt-tests/storage_length.window.test.ts +++ b/lib/wpt-tests/storage_length.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("storage.length (method access)", () => { diff --git a/lib/wpt-tests/storage_removeitem.window.test.ts b/lib/wpt-tests/storage_removeitem.window.test.ts index 0ed0cca..a4884e6 100644 --- a/lib/wpt-tests/storage_removeitem.window.test.ts +++ b/lib/wpt-tests/storage_removeitem.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("storage.removeItem()", () => { diff --git a/lib/wpt-tests/storage_set_value_enumerate.window.test.ts b/lib/wpt-tests/storage_set_value_enumerate.window.test.ts index 9c3f70f..6b7f6d2 100644 --- a/lib/wpt-tests/storage_set_value_enumerate.window.test.ts +++ b/lib/wpt-tests/storage_set_value_enumerate.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion const store_list = [ diff --git a/lib/wpt-tests/storage_setitem.window.test.ts b/lib/wpt-tests/storage_setitem.window.test.ts index 3336de3..b315561 100644 --- a/lib/wpt-tests/storage_setitem.window.test.ts +++ b/lib/wpt-tests/storage_setitem.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion var test_error = { name: "test" }; diff --git a/lib/wpt-tests/storage_string_conversion.window.test.ts b/lib/wpt-tests/storage_string_conversion.window.test.ts index 4bcdae7..084fbc2 100644 --- a/lib/wpt-tests/storage_string_conversion.window.test.ts +++ b/lib/wpt-tests/storage_string_conversion.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("storage only stores strings", () => { diff --git a/lib/wpt-tests/storage_supported_property_names.window.test.ts b/lib/wpt-tests/storage_supported_property_names.window.test.ts index b8f4c10..f0a64da 100644 --- a/lib/wpt-tests/storage_supported_property_names.window.test.ts +++ b/lib/wpt-tests/storage_supported_property_names.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("Object.getOwnPropertyNames on storage Storage", () => { diff --git a/lib/wpt-tests/symbol-props.window.test.ts b/lib/wpt-tests/symbol-props.window.test.ts index 506dbea..e944f29 100644 --- a/lib/wpt-tests/symbol-props.window.test.ts +++ b/lib/wpt-tests/symbol-props.window.test.ts @@ -19,7 +19,7 @@ //#region Imports import { expect, test } from "vitest"; -import { BrowserStorage } from "../index"; +import { BrowserStorage } from "../storage"; //#endregion test("storage: plain set + get (loose)", () => { diff --git a/package.json b/package.json index aab22af..5569331 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@orisomething/browser-storage", - "version": "0.1.1", + "version": "1.0.0", "license": "MIT", "author": "Ori Livni", "repository": { @@ -11,10 +11,10 @@ "node": ">= 18" }, "scripts": { - "build": "rolldown -c rolldown.config.ts", + "build": "rm -rf dist && rolldown -c rolldown.config.ts", "test": "vitest", "lint:eslint": "eslint . --max-warnings 0", - "lint:prettier": " pnpm prettier --check .", + "lint:prettier": "pnpm prettier --check .", "lint:tsc": "tsc -p ." }, "type": "module", diff --git a/rolldown.config.ts b/rolldown.config.ts index 9a51a6c..73a1633 100644 --- a/rolldown.config.ts +++ b/rolldown.config.ts @@ -2,7 +2,17 @@ import { defineConfig } from "rolldown"; import dts from "vite-plugin-dts"; export default defineConfig({ - input: "src/index.ts", + input: "lib/index.ts", + + platform: "neutral", + + treeshake: { + annotations: true, + commonjs: false, + moduleSideEffects: false, + unknownGlobalSideEffects: false, + }, + output: { dir: "dist", format: "esm",