diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 35181e08e..000000000
--- a/.eslintrc
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "root": true,
- "parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint", "no-only-tests", "eslint-comments"],
- "ignorePatterns": ["packages/*/dist/**", "packages/*/dev/**", "**/*/__snapshots__/**"],
- "parserOptions": {
- "project": "./tsconfig.json",
- "tsconfigRootDir": ".",
- "sourceType": "module"
- },
- "rules": {
- /*
- forgot to remove/implement
- */
- "no-console": "warn",
- "no-debugger": "warn",
- "prefer-const": "warn",
- "@typescript-eslint/no-unused-vars": [
- "warn",
- {
- "argsIgnorePattern": "^_",
- "varsIgnorePattern": "^_",
- "caughtErrorsIgnorePattern": "^_"
- }
- ],
- "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn",
- "@typescript-eslint/no-unnecessary-condition": "warn",
- "@typescript-eslint/no-unnecessary-qualifier": "warn",
- "@typescript-eslint/no-unnecessary-type-arguments": "warn",
- "@typescript-eslint/no-unnecessary-type-assertion": "warn",
- "@typescript-eslint/no-unnecessary-type-constraint": "warn",
- "@typescript-eslint/no-useless-empty-export": "warn",
- "eslint-comments/no-unused-disable": "warn",
- /*
- tests
- */
- "no-only-tests/no-only-tests": "warn"
- }
-}
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index 8a2412f06..e0e90bcb8 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -9,21 +9,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2.2.4
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js environment
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Setup prettier cache
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: node_modules/.cache/prettier
key: prettier-${{ github.sha }}
@@ -34,7 +34,7 @@ jobs:
run: pnpm run format
- name: Add, Commit and Push
- uses: stefanzweifel/git-auto-commit-action@v4
+ uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "Format"
@@ -42,7 +42,7 @@ jobs:
run: pnpm run update-readme
- name: Add, Commit and Push
- uses: stefanzweifel/git-auto-commit-action@v4
+ uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "Update Readme"
file_pattern: README.md
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2432dd13f..d108569bb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -10,27 +10,19 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2.2.4
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js environment
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 20
cache: pnpm
- name: Install Dependencies
run: pnpm i
- - name: Cache turbo build setup
- uses: actions/cache@v3
- with:
- path: node_modules/.cache/turbo
- key: turbo-${{ github.sha }}
- restore-keys: |
- turbo-
-
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index bf2758fd1..61a1acc82 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -11,27 +11,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- - uses: pnpm/action-setup@v2.2.4
+ - uses: pnpm/action-setup@v4
- name: Setup Node.js environment
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: 18
+ node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install
- - name: Cache turbo build setup
- uses: actions/cache@v3
- with:
- path: node_modules/.cache/turbo
- key: turbo-${{ github.sha }}
- restore-keys: |
- turbo-
-
- name: Build all packages
run: pnpm build
diff --git a/.gitignore b/.gitignore
index 06b9ef505..6f383ce95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@ node_modules/
jspm_packages/
dist/
+
# TypeScript v1 declaration files
typings/
@@ -153,6 +154,7 @@ dist/
# vscode settings
.vscode/settings.json
+.vscode/launch.json
# Temporary folders
tmp/
@@ -162,9 +164,6 @@ temp/
tsup.config.bundled*.mjs
-# turbo
-.turbo
-
# vite
vite.config.ts.js
vitest.config.ts.timestamp-*.mjs
@@ -178,3 +177,6 @@ _temp_*
# Local Netlify folder
.netlify
+
+# IntelliJ IDEA
+.idea
diff --git a/.nvmrc b/.nvmrc
deleted file mode 100644
index 6d80269a4..000000000
--- a/.nvmrc
+++ /dev/null
@@ -1 +0,0 @@
-18.16.0
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..a4e493edc
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,22 @@
+
+node_modules/
+dist/
+assets
+
+# TypeScript cache
+*.tsbuildinfo
+
+.vscode
+
+# Temporary folders
+tmp/
+temp/
+_temp_*
+
+_generated
+
+.netlify
+.vinxi
+.idea
+
+tsconfig.json
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 739e1de04..d8bc7c92c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,7 +34,7 @@ All our primitives are meant to be consistent and sustain a level of quality. We
9. Be focused on composition vs. isolation of logic
10. Community voice and needs guide road map and planning
11. Strong TypeScript support
-12. Support for both CJS and ESM
+12. ESM (no more CJS support)
13. Solid performance!
## Basic and Compound Primitives
diff --git a/README.md b/README.md
index 047fd02ee..1443e256a 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,8 @@
# Solid Primitives
-[](https://pnpm.io/)
-[](https://turborepo.org/)
-[](https://dash.deno.com/playground/combined-npm-downloads)
+[](https://pnpm.io/)
+[](https://dash.deno.com/playground/combined-npm-downloads)
A project that strives to develop high-quality, community contributed Solid primitives. All utilities are well tested and continuously maintained. Every contribution to the repository is checked for quality and maintained to the highest degree of excellence. The ultimate goal is to extend Solid's primary and secondary primitives with a set of tertiary primitives.
@@ -51,12 +50,13 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|[geolocation](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createGeolocation](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#creategeolocation) [createGeolocationWatcher](https://github.com/solidjs-community/solid-primitives/tree/main/packages/geolocation#creategeolocationwatcher)|[](https://bundlephobia.com/package/@solid-primitives/geolocation)|[](https://www.npmjs.com/package/@solid-primitives/geolocation)|
|[mutation-observer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutation-observer#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createMutationObserver](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutation-observer#createmutationobserver)|[](https://bundlephobia.com/package/@solid-primitives/mutation-observer)|[](https://www.npmjs.com/package/@solid-primitives/mutation-observer)|
|[permission](https://github.com/solidjs-community/solid-primitives/tree/main/packages/permission#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createPermission](https://github.com/solidjs-community/solid-primitives/tree/main/packages/permission#createpermission)|[](https://bundlephobia.com/package/@solid-primitives/permission)|[](https://www.npmjs.com/package/@solid-primitives/permission)|
-|[storage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createstorage) [createCookieStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createcookiestorage) [createAsyncStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createasyncstorage) [createStorageSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createstoragesignal) [createLocalStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createlocalstorage) [createSessionStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#createsessionstorage) [cookieStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#cookiestorage) [makePersisted](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#makepersisted)|[](https://bundlephobia.com/package/@solid-primitives/storage)|[](https://www.npmjs.com/package/@solid-primitives/storage)|
+|[storage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[makePersisted](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#makepersisted) [cookieStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#cookiestorage) [tauriStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#tauristorage) [multiplexStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#multiplexstorage) [storageSync](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#storagesync) [messageSync](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#messagesync) [wsSync](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#wssync) [multiplexSync](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#multiplexsync) [addClearMethod](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#addclearmethod) [addWithOptionsMethod](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#addwithoptionsmethod) [makeObjectStorage](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage#makeobjectstorage)|[](https://bundlephobia.com/package/@solid-primitives/storage)|[](https://www.npmjs.com/package/@solid-primitives/storage)|
|[timer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[makeTimer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#maketimer) [createTimer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#createtimer) [createTimeoutLoop](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#createtimeoutloop) [createPolled](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#createpolled) [createIntervalCounter](https://github.com/solidjs-community/solid-primitives/tree/main/packages/timer#createintervalcounter)|[](https://bundlephobia.com/package/@solid-primitives/timer)|[](https://www.npmjs.com/package/@solid-primitives/timer)|
|[upload](https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createFileUploader](https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload#createfileuploader) [createDropzone](https://github.com/solidjs-community/solid-primitives/tree/main/packages/upload#createdropzone)|[](https://bundlephobia.com/package/@solid-primitives/upload)|[](https://www.npmjs.com/package/@solid-primitives/upload)|
|[workers](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createWorker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createworker) [createWorkerPool](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createworkerpool) [createSignaledWorker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/workers#createsignaledworker)|[](https://bundlephobia.com/package/@solid-primitives/workers)|[](https://www.npmjs.com/package/@solid-primitives/workers)|
|
*Network*
|
|[connectivity](https://github.com/solidjs-community/solid-primitives/tree/main/packages/connectivity#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createConnectivitySignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/connectivity#createconnectivitysignal)|[](https://bundlephobia.com/package/@solid-primitives/connectivity)|[](https://www.npmjs.com/package/@solid-primitives/connectivity)|
+|[cookies](https://github.com/solidjs-community/solid-primitives/tree/main/packages/cookies#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createServerCookie](https://github.com/solidjs-community/solid-primitives/tree/main/packages/cookies#createservercookie) [createUserTheme](https://github.com/solidjs-community/solid-primitives/tree/main/packages/cookies#createusertheme) [getCookiesString](https://github.com/solidjs-community/solid-primitives/tree/main/packages/cookies#getcookiesstring)|[](https://bundlephobia.com/package/@solid-primitives/cookies)|[](https://www.npmjs.com/package/@solid-primitives/cookies)|
|[fetch](https://github.com/solidjs-community/solid-primitives/tree/main/packages/fetch#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createFetch](https://github.com/solidjs-community/solid-primitives/tree/main/packages/fetch#createfetch)|[](https://bundlephobia.com/package/@solid-primitives/fetch)|[](https://www.npmjs.com/package/@solid-primitives/fetch)|
|[graphql](https://github.com/solidjs-community/solid-primitives/tree/main/packages/graphql#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createGraphQLClient](https://github.com/solidjs-community/solid-primitives/tree/main/packages/graphql#creategraphqlclient)|[](https://bundlephobia.com/package/@solid-primitives/graphql)|[](https://www.npmjs.com/package/@solid-primitives/graphql)|
|[stream](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createStream](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#createstream) [createAmplitudeStream](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#createamplitudestream) [createMediaPermissionRequest](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#createmediapermissionrequest) [createAmplitudeFromStream](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#createamplitudefromstream) [createScreen](https://github.com/solidjs-community/solid-primitives/tree/main/packages/stream#createscreen)|[](https://bundlephobia.com/package/@solid-primitives/stream)|[](https://www.npmjs.com/package/@solid-primitives/stream)|
@@ -64,7 +64,8 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|
|
@@ -90,7 +91,7 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|[map](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[ReactiveMap](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#reactivemap) [ReactiveWeakMap](https://github.com/solidjs-community/solid-primitives/tree/main/packages/map#reactiveweakmap)|[](https://bundlephobia.com/package/@solid-primitives/map)|[](https://www.npmjs.com/package/@solid-primitives/map)|
|[memo](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createLatest](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createlatest) [createLatestMany](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createlatestmany) [createWritableMemo](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createwritablememo) [createLazyMemo](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createlazymemo) [createPureReaction](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createpurereaction) [createMemoCache](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#creatememocache) [createReducer](https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createreducer)|[](https://bundlephobia.com/package/@solid-primitives/memo)|[](https://www.npmjs.com/package/@solid-primitives/memo)|
|[mutable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createMutable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable#createmutable) [modifyMutable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/mutable#modifymutable)|[](https://bundlephobia.com/package/@solid-primitives/mutable)|[](https://www.npmjs.com/package/@solid-primitives/mutable)|
-|[resource](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createAggregated](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#createaggregated) [createDeepSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#createdeepsignal) [makeAbortable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makeabortable) [makeCache](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makecache) [makeRetrying](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makeretrying)|[](https://bundlephobia.com/package/@solid-primitives/resource)|[](https://www.npmjs.com/package/@solid-primitives/resource)|
+|[resource](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createAggregated](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#createaggregated) [createDeepSignal](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#createdeepsignal) [makeAbortable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makeabortable) [createAbortable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#createabortable) [makeCache](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makecache) [makeRetrying](https://github.com/solidjs-community/solid-primitives/tree/main/packages/resource#makeretrying)|[](https://bundlephobia.com/package/@solid-primitives/resource)|[](https://www.npmjs.com/package/@solid-primitives/resource)|
|[rootless](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createSubRoot](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createsubroot) [createCallback](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createcallback) [createDisposable](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createdisposable) [createSharedRoot](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createsharedroot) [createRootPool](https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createrootpool)|[](https://bundlephobia.com/package/@solid-primitives/rootless)|[](https://www.npmjs.com/package/@solid-primitives/rootless)|
|[set](https://github.com/solidjs-community/solid-primitives/tree/main/packages/set#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[ReactiveSet](https://github.com/solidjs-community/solid-primitives/tree/main/packages/set#reactiveset) [ReactiveWeakSet](https://github.com/solidjs-community/solid-primitives/tree/main/packages/set#reactiveweakset)|[](https://bundlephobia.com/package/@solid-primitives/set)|[](https://www.npmjs.com/package/@solid-primitives/set)|
|[signal-builders](https://github.com/solidjs-community/solid-primitives/tree/main/packages/signal-builders#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[List of builders](https://github.com/solidjs-community/solid-primitives/tree/main/packages/signal-builders#list-of-builders)|[](https://bundlephobia.com/package/@solid-primitives/signal-builders)|[](https://www.npmjs.com/package/@solid-primitives/signal-builders)|
@@ -101,12 +102,12 @@ The goal of Solid Primitives is to wrap client and server side functionality to
|[marker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/marker#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createMarker](https://github.com/solidjs-community/solid-primitives/tree/main/packages/marker#createmarker)|[](https://bundlephobia.com/package/@solid-primitives/marker)|[](https://www.npmjs.com/package/@solid-primitives/marker)|
|[masonry](https://github.com/solidjs-community/solid-primitives/tree/main/packages/masonry#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createMasonry](https://github.com/solidjs-community/solid-primitives/tree/main/packages/masonry#createmasonry)|[](https://bundlephobia.com/package/@solid-primitives/masonry)|[](https://www.npmjs.com/package/@solid-primitives/masonry)|
|[pagination](https://github.com/solidjs-community/solid-primitives/tree/main/packages/pagination#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createPagination](https://github.com/solidjs-community/solid-primitives/tree/main/packages/pagination#createpagination) [createInfiniteScroll](https://github.com/solidjs-community/solid-primitives/tree/main/packages/pagination#createinfinitescroll)|[](https://bundlephobia.com/package/@solid-primitives/pagination)|[](https://www.npmjs.com/package/@solid-primitives/pagination)|
+|[virtual](https://github.com/solidjs-community/solid-primitives/tree/main/packages/virtual#readme)|[](https://github.com/solidjs-community/solid-primitives/blob/main/CONTRIBUTING.md#contribution-process)|[createVirutalList](https://github.com/solidjs-community/solid-primitives/tree/main/packages/virtual#createvirutallist) [VirtualList](https://github.com/solidjs-community/solid-primitives/tree/main/packages/virtual#virtuallist)|[](https://bundlephobia.com/package/@solid-primitives/virtual)|[](https://www.npmjs.com/package/@solid-primitives/virtual)|
|
diff --git a/packages/audio/package.json b/packages/audio/package.json
index 308590745..5d7dc46ea 100644
--- a/packages/audio/package.json
+++ b/packages/audio/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/audio",
- "version": "1.3.17",
+ "version": "1.4.0",
"description": "Primitives to manage audio and single sounds.",
"author": "David Di Biase ",
"license": "MIT",
@@ -25,18 +25,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
@@ -54,7 +50,6 @@
"primitives"
],
"devDependencies": {
- "solid-heroicons": "^3.2.4",
"solid-js": "^1.8.7"
},
"dependencies": {
diff --git a/packages/audio/src/index.ts b/packages/audio/src/index.ts
index 6bf07fb0f..e50759289 100644
--- a/packages/audio/src/index.ts
+++ b/packages/audio/src/index.ts
@@ -27,16 +27,18 @@ export type AudioEventHandlers = {
// Helper for producing the audio source
const unwrapSource = (src: AudioSource) => {
- let player: HTMLAudioElement;
if (src instanceof HTMLAudioElement) {
- player = src;
- } else {
- player = new Audio();
- player[typeof src === "string" ? "src" : "srcObject"] = src as string & MediaSource;
+ return src;
}
+ const player = new Audio();
+ setAudioSrc(player, src);
return player;
};
+function setAudioSrc(el: HTMLAudioElement, src: AudioSource) {
+ el[typeof src === "string" ? "src" : "srcObject"] = src as string & MediaSource;
+}
+
/**
* Generates a basic audio instance with limited functionality.
*
@@ -51,20 +53,21 @@ export const makeAudio = (
if (isServer) {
return {} as HTMLAudioElement;
}
+
const player = unwrapSource(src);
- const listeners = (enabled: boolean) => {
- Object.entries(handlers).forEach(([evt, handler]) =>
- player[enabled ? "addEventListener" : "removeEventListener"](
- evt,
- handler as EventListenerOrEventListenerObject,
- ),
- );
- };
- onMount(() => listeners(true));
+
+ onMount(() => {
+ for (const [name, handler] of Object.entries(handlers)) {
+ player.addEventListener(name, handler as any);
+ }
+ });
onCleanup(() => {
player.pause();
- listeners(false);
+ for (const [name, handler] of Object.entries(handlers)) {
+ player.removeEventListener(name, handler as any);
+ }
});
+
return player;
};
@@ -82,7 +85,7 @@ export const makeAudio = (
*
* @example
* ```ts
- * const { start, seek } = makeAudioPlayer('./example1.mp3);
+ * const { start, seek } = makeAudioPlayer('./example1.mp3');
* ```
*/
export const makeAudioPlayer = (
@@ -105,13 +108,16 @@ export const makeAudioPlayer = (
};
}
const player = makeAudio(src, handlers);
- const play = () => player.play();
- const pause = () => player.pause();
- const seek = (time: number) =>
+ return {
+ player,
+ play: () => player.play(),
+ pause: () => player.pause(),
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- player.fastSeek ? player.fastSeek(time) : (player.currentTime = time);
- const setVolume = (volume: number) => (player.volume = volume);
- return { play, pause, seek, setVolume, player };
+ seek: player.fastSeek
+ ? (time: number) => player.fastSeek(time)
+ : (time: number) => (player.currentTime = time),
+ setVolume: (volume: number) => (player.volume = volume),
+ };
};
/**
@@ -177,19 +183,23 @@ export const createAudio = (
},
];
}
+
const player = unwrapSource(access(src));
+
const [store, setStore] = createStaticStore({
state: AudioState.LOADING,
player,
currentTime: 0,
- get duration() {
- return this.player.duration;
- },
- get volume() {
- return this.player.volume;
- },
+ duration: 0,
+ volume: 0,
});
- const { play, pause, setVolume, seek } = makeAudioPlayer(store.player, {
+
+ const {
+ play,
+ pause,
+ setVolume: _setVolume,
+ seek,
+ } = makeAudioPlayer(store.player, {
loadeddata: () => {
setStore({
state: AudioState.READY,
@@ -208,20 +218,27 @@ export const createAudio = (
playing: () => setStore("state", AudioState.PLAYING),
pause: () => setStore("state", AudioState.PAUSED),
error: () => setStore("state", AudioState.ERROR),
+ ended: () => setStore("state", AudioState.COMPLETE),
});
+
+ const setVolume = (volume: number) => {
+ setStore("volume", volume);
+ _setVolume(volume);
+ };
+
// Bind reactive properties as needed
if (src instanceof Function) {
createEffect(() => {
- const newSrc = access(src);
+ const newSrc = src();
if (newSrc instanceof HTMLAudioElement) {
- setStore("player", () => newSrc);
+ setStore("player", newSrc);
} else {
- store.player[typeof newSrc === "string" ? "src" : "srcObject"] = newSrc as string &
- MediaSource;
+ setAudioSrc(store.player, newSrc);
}
seek(0);
});
}
+
if (playing) {
createEffect(() => (playing() ? play() : pause()));
}
@@ -229,5 +246,6 @@ export const createAudio = (
createEffect(() => setVolume(volume()));
setVolume(volume());
}
+
return [store, { seek, play, pause, setVolume }];
};
diff --git a/packages/audio/test/index.test.ts b/packages/audio/test/index.test.ts
index 8d59685b3..c8e4fede9 100644
--- a/packages/audio/test/index.test.ts
+++ b/packages/audio/test/index.test.ts
@@ -1,7 +1,7 @@
import "./setup";
import { createRoot, createSignal } from "solid-js";
import { describe, expect, it } from "vitest";
-import { makeAudio, makeAudioPlayer, createAudio } from "../src/index.js";
+import { makeAudio, makeAudioPlayer, createAudio, AudioState } from "../src/index.js";
const testPath =
"https://github.com/solidjs-community/solid-primitives/blob/audio/packages/audio/dev/sample1.mp3?raw=true";
@@ -47,7 +47,7 @@ describe("makeAudioPlayer", () => {
}));
});
-describe("createAudioPlayer", () => {
+describe("createAudio", () => {
it("test srcObject value path", () =>
createRoot(dispose => {
const media = {} as MediaSource;
@@ -72,4 +72,14 @@ describe("createAudioPlayer", () => {
expect(audio.player.volume).toBe(0.5);
dispose();
}));
+
+ it("should set the COMPLETE state when audio ends", () => {
+ const [[audio], dispose] = createRoot(dispose => [createAudio({} as MediaSource), dispose]);
+ expect(audio.state).toBe(AudioState.LOADING);
+
+ audio.player.dispatchEvent(new Event("ended"));
+ expect(audio.state).toBe(AudioState.COMPLETE);
+
+ dispose();
+ });
});
diff --git a/packages/audio/tsconfig.json b/packages/audio/tsconfig.json
index 4082f16a5..acef5da3e 100644
--- a/packages/audio/tsconfig.json
+++ b/packages/audio/tsconfig.json
@@ -1,3 +1,19 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../static-store"
+ },
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/autofocus/CHANGELOG.md b/packages/autofocus/CHANGELOG.md
index b603b3732..d265f195e 100644
--- a/packages/autofocus/CHANGELOG.md
+++ b/packages/autofocus/CHANGELOG.md
@@ -1,5 +1,16 @@
# @solid-primitives/autofocus
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/utils@6.3.0
+
## 0.0.111
### Patch Changes
diff --git a/packages/autofocus/README.md b/packages/autofocus/README.md
index e7375ac65..b213b7f51 100644
--- a/packages/autofocus/README.md
+++ b/packages/autofocus/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/autofocus
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/autofocus)
[](https://www.npmjs.com/package/@solid-primitives/autofocus)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
@@ -14,7 +13,7 @@ Primitives for autofocusing HTML elements.
The native autofocus attribute only works on page load, which makes it incompatible with SolidJS. These primitives run on render, allowing autofocus on initial render as well as dynamically added components.
- [`autofocus`](#autofocus) - Directive to autofocus an element on render.
-- [`createAutofocus`](#createAutofocus) - Reactive primitive to autofocus an element on render.
+- [`createAutofocus`](#createautofocus) - Reactive primitive to autofocus an element on render.
## Installation
diff --git a/packages/autofocus/package.json b/packages/autofocus/package.json
index 29b61829b..208724d0e 100644
--- a/packages/autofocus/package.json
+++ b/packages/autofocus/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/autofocus",
- "version": "0.0.111",
+ "version": "0.1.0",
"description": "Primitives for autofocusing HTML elements",
"author": "jer3m01 ",
"contributors": [],
@@ -34,18 +34,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/autofocus/tsconfig.json b/packages/autofocus/tsconfig.json
index 4082f16a5..dc1970e16 100644
--- a/packages/autofocus/tsconfig.json
+++ b/packages/autofocus/tsconfig.json
@@ -1,3 +1,16 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/bounds/CHANGELOG.md b/packages/bounds/CHANGELOG.md
index b781394b2..32e48c904 100644
--- a/packages/bounds/CHANGELOG.md
+++ b/packages/bounds/CHANGELOG.md
@@ -1,5 +1,34 @@
# @solid-primitives/bounds
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/resize-observer@2.1.0
+ - @solid-primitives/event-listener@2.4.0
+ - @solid-primitives/static-store@0.1.0
+ - @solid-primitives/utils@6.3.0
+
+## 0.0.123
+
+### Patch Changes
+
+- Updated dependencies [56d9511]
+ - @solid-primitives/static-store@0.0.9
+ - @solid-primitives/resize-observer@2.0.27
+
+## 0.0.122
+
+### Patch Changes
+
+- Updated dependencies [a7338e7]
+ - @solid-primitives/resize-observer@2.0.26
+
## 0.0.121
### Patch Changes
diff --git a/packages/bounds/README.md b/packages/bounds/README.md
index 1e878a75d..37113eef5 100644
--- a/packages/bounds/README.md
+++ b/packages/bounds/README.md
@@ -4,14 +4,13 @@
# @solid-primitives/bounds
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/bounds)
[](https://www.npmjs.com/package/@solid-primitives/bounds)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Primitives for tracking HTML element size and position on screen as it changes.
-- [`createElementBounds`](#createElementBounds) - Creates a reactive store-like object of current element bounds — position on the screen, and size dimensions.
+- [`createElementBounds`](#createelementbounds) - Creates a reactive store-like object of current element bounds — position on the screen, and size dimensions.
## Installation
diff --git a/packages/bounds/package.json b/packages/bounds/package.json
index c142bef8f..07cf5e321 100644
--- a/packages/bounds/package.json
+++ b/packages/bounds/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/bounds",
- "version": "0.0.121",
+ "version": "0.1.0",
"description": "Primitives for tracking HTML element size and position on screen as it changes.",
"author": "Damian Tarnawski ",
"contributors": [],
@@ -34,18 +34,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/bounds/tsconfig.json b/packages/bounds/tsconfig.json
index 0f52db9f5..961c7e0e6 100644
--- a/packages/bounds/tsconfig.json
+++ b/packages/bounds/tsconfig.json
@@ -1,5 +1,25 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev", "./demo"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../event-listener"
+ },
+ {
+ "path": "../resize-observer"
+ },
+ {
+ "path": "../static-store"
+ },
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/broadcast-channel/CHANGELOG.md b/packages/broadcast-channel/CHANGELOG.md
index 127da8ebe..b5ce2137e 100644
--- a/packages/broadcast-channel/CHANGELOG.md
+++ b/packages/broadcast-channel/CHANGELOG.md
@@ -1,5 +1,11 @@
# @solid-primitives/broadcast-channel
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
## 0.0.105
### Patch Changes
diff --git a/packages/broadcast-channel/README.md b/packages/broadcast-channel/README.md
index 6f1c0061a..fb3e2a208 100644
--- a/packages/broadcast-channel/README.md
+++ b/packages/broadcast-channel/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/broadcast-channel
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/broadcast-channel)
[](https://www.npmjs.com/package/@solid-primitives/broadcast-channel)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
@@ -23,8 +22,8 @@ pnpm add @solid-primitives/broadcast-channel
## Available primitives
-- [`makeBroadcastChannel`](#makeBroadcastChannel)
-- [`createBroadcastChannel`](#createBroadcastChannel)
+- [`makeBroadcastChannel`](#makebroadcastchannel)
+- [`createBroadcastChannel`](#createbroadcastchannel)
### `makeBroadcastChannel`
@@ -97,7 +96,7 @@ const App = () => {
### `createBroadcastChannel`
-Provedes the same functionality as [`makeBroadcastChannel`](#makeBroadcastChannel) but instead of returning `onMessage` function, it returns a `message` signal accessor that updates when postMessage is fired from other contexts.
+Provedes the same functionality as [`makeBroadcastChannel`](#makebroadcastchannel) but instead of returning `onMessage` function, it returns a `message` signal accessor that updates when postMessage is fired from other contexts.
```ts
const { postMessage } = createBroadcastChannel("test_channel");
diff --git a/packages/broadcast-channel/dev/index.tsx b/packages/broadcast-channel/dev/index.tsx
index 6882ae44b..4ff11d0c6 100644
--- a/packages/broadcast-channel/dev/index.tsx
+++ b/packages/broadcast-channel/dev/index.tsx
@@ -50,7 +50,7 @@ const Content = (props: { page: TPage; channelName: string }) => {
BroadcastChannel name: {props.channelName}
}>
+ {(key, value, index) => (
+
+ {key}: {value()}
+
+ )}
+
+```
+
+## ``
+
+Creates a list of elements by mapping Set values. Similar to Solid's `` and ``, but here, render function takes two arguments, the value and the index argument as a signal.
+
+### How to use it
+
+```tsx
+import { SetValues } from "@solid-primitives/keyed";
+
+const [set, setSet] = createSignal(new Set());
+
+No items
}>
+ {value =>
{value}
}
+;
+```
+
+### Index argument
+
+Second argument of the map function is an index signal.
+
+`SetValues` is using [`Set#values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values) so the index and resulting JSX will follow the insertion order.
+
+```tsx
+No items
+ )}
+
+ ),
+ container,
+ );
+
+ const oldMapped: ChildNode[] = new Array(container.childNodes.length);
+ container.childNodes.forEach((n, i) => {
+ const v = Array.from(startingMap.values())[i]!;
+ expect(n.textContent).toEqual(`${i}. ${v}`);
+ oldMapped[i] = n;
+ });
+
+ const nextMap = new Set(["0", "3"]);
+ set(nextMap);
+
+ const newMapped: ChildNode[] = new Array(container.childNodes.length);
+ container.childNodes.forEach((n, i) => {
+ const v = Array.from(nextMap.values())[i]!;
+ expect(n.textContent).toEqual(`${i}. ${v}`);
+ newMapped[i] = n;
+ });
+
+ expect(oldMapped[0]).toBe(newMapped[0]);
+ expect(oldMapped[3]).toBe(newMapped[1]);
+ expect(newMapped.includes(oldMapped[1]!)).toEqual(false);
+ expect(newMapped.includes(oldMapped[2]!)).toEqual(false);
+
+ unmount();
+ document.body.removeChild(container);
+ });
+});
diff --git a/packages/keyed/tsconfig.json b/packages/keyed/tsconfig.json
index 0f52db9f5..38c71ce71 100644
--- a/packages/keyed/tsconfig.json
+++ b/packages/keyed/tsconfig.json
@@ -1,5 +1,12 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev", "./demo"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/lifecycle/CHANGELOG.md b/packages/lifecycle/CHANGELOG.md
index 9d69df692..a0fbea12e 100644
--- a/packages/lifecycle/CHANGELOG.md
+++ b/packages/lifecycle/CHANGELOG.md
@@ -1,5 +1,11 @@
# @solid-primitives/lifecycle
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
## 0.0.102
### Patch Changes
diff --git a/packages/lifecycle/README.md b/packages/lifecycle/README.md
index 7707f6e93..85b295297 100644
--- a/packages/lifecycle/README.md
+++ b/packages/lifecycle/README.md
@@ -4,16 +4,15 @@
# @solid-primitives/lifecycle
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/lifecycle)
[](https://www.npmjs.com/package/@solid-primitives/lifecycle)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Package providing extra layer of lifecycle primitives for Solid.
-- [`createIsMounted`](#createIsMounted) - Returns a boolean signal indicating whether the component is mounted or not.
-- [`isHydrated`](#isHydrated) - A signal with the same behavior as [`isHydrating`](#isHydrating) but this one focused only on client-side updates.
-- [`onElementConnect`](#onElementConnect) - Calls the given callback when the target element is connected to the DOM.
+- [`createIsMounted`](#createismounted) - Returns a boolean signal indicating whether the component is mounted or not.
+- [`isHydrated`](#ishydrated) - A signal with the same behavior as [`isHydrating`](#ishydrating) but this one focused only on client-side updates.
+- [`onElementConnect`](#onelementconnect) - Calls the given callback when the target element is connected to the DOM.
## Installation
diff --git a/packages/lifecycle/package.json b/packages/lifecycle/package.json
index 1f5140a95..37a2c7da1 100644
--- a/packages/lifecycle/package.json
+++ b/packages/lifecycle/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/lifecycle",
- "version": "0.0.102",
+ "version": "0.1.0",
"description": "Package providing extra layer of lifecycle primitives for Solid.",
"author": "Damian Tarnawski ",
"contributors": [],
@@ -35,18 +35,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
diff --git a/packages/lifecycle/tsconfig.json b/packages/lifecycle/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/lifecycle/tsconfig.json
+++ b/packages/lifecycle/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/list/CHANGELOG.md b/packages/list/CHANGELOG.md
new file mode 100644
index 000000000..44490691a
--- /dev/null
+++ b/packages/list/CHANGELOG.md
@@ -0,0 +1,17 @@
+# @solid-primitives/list
+
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+## 0.0.101
+
+### Patch Changes
+
+- 1ea2a09: Use isDev const from solid-js/web instead if a string
+
+## 0.0.100
+
+Initial release of the package.
diff --git a/packages/list/LICENSE b/packages/list/LICENSE
new file mode 100644
index 000000000..38b41d975
--- /dev/null
+++ b/packages/list/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Solid Primitives Working Group
+
+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.
\ No newline at end of file
diff --git a/packages/list/README.md b/packages/list/README.md
new file mode 100644
index 000000000..3030abe4d
--- /dev/null
+++ b/packages/list/README.md
@@ -0,0 +1,65 @@
+
+
+
+
+# @solid-primitives/list
+
+[](https://bundlephobia.com/package/@solid-primitives/list)
+[](https://www.npmjs.com/package/@solid-primitives/list)
+[](https://github.com/solidjs-community/solid-primitives#contribution-process)
+
+Package providing additional way to manage arrays. Alternative to `` and `` that has reactive item value and reactive index. Focuses on avoiding recreation of mapped elements.
+
+[`List`](#list) - Component that provides reactive item value and reactive index.
+[`listArray`](#listarray) - Underlying helper for `` unkeyed control flow, similar to `mapArray` and `indexArray`.
+
+## Installation
+
+```bash
+npm install @solid-primitives/list
+# or
+yarn add @solid-primitives/list
+# or
+pnpm add @solid-primitives/list
+```
+
+## List
+
+Example:
+
+```tsx
+function Component() {
+ const [signal, setSignal] = createSignal([1, 2, 3]);
+
+ return (
+
+ {(value, index) => {
+ return (
+
+ {" "}
+ {index()}: {value()}{" "}
+
+ );
+ }}
+
+ );
+}
+```
+
+Component similar to `` and ``, but provides reactive item value and reactive index.
+
+Every element is keyed by item reference and index, but item reference is prioritized. That means whenever element changes it's position in array, it's `index` signal will be updated and if element value is changed, it's `value` signal will be updated.
+
+## listArray
+
+Underlying helper for `` unkeyed control flow. Returns array with elements mapped using provided mapping function.
+
+Mapping function may use provided reactive value and reactive index, but signals for each of them are created only if they are used. Mapping function is ran only when original array has more elements than before. Elements are disposed only if original array has less elements than before.
+
+## Demo
+
+You can see the list in action in the following sandbox: https://primitives.solidjs.community/playground/list/
+
+## Changelog
+
+See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/list/dev/index.tsx b/packages/list/dev/index.tsx
new file mode 100644
index 000000000..b53c5f6b2
--- /dev/null
+++ b/packages/list/dev/index.tsx
@@ -0,0 +1,229 @@
+import {
+ type Component,
+ createSignal,
+ createEffect,
+ onCleanup,
+ untrack,
+ onMount,
+ batch,
+} from "solid-js";
+import { List } from "../src/index.js";
+
+const App: Component = () => {
+ let currentValue = 0;
+ const nextValue = () => `${currentValue++}`;
+
+ const [signal, setSignal] = createSignal<(string | number)[]>(
+ Array.from({ length: 5 }).map(() => nextValue()),
+ );
+ const [changes, setChanges] = createSignal("");
+ const addChange = (description: string) => {
+ setChanges(old => old + "\n" + description);
+ console.log(description);
+ };
+
+ createEffect(() => {
+ batch(() => {
+ setChanges("");
+ addChange("-------------------");
+ });
+ signal();
+ });
+
+ let customArr: HTMLInputElement;
+
+ return (
+
- >
- );
-};
-export default App;
diff --git a/packages/refs/package.json b/packages/refs/package.json
index e1377bcc3..89b20a220 100644
--- a/packages/refs/package.json
+++ b/packages/refs/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/refs",
- "version": "1.0.8",
+ "version": "1.1.0",
"description": "Library of primitives, components and directives for SolidJS that help managing references to JSX elements.",
"author": "Damian Tarnawski @thetarnav ",
"license": "MIT",
@@ -27,18 +27,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
@@ -59,7 +55,6 @@
"@solid-primitives/utils": "workspace:^"
},
"devDependencies": {
- "solid-app-router": "^0.4.2",
"solid-js": "^1.8.7",
"solid-transition-group": "^0.2.3"
},
diff --git a/packages/refs/tsconfig.json b/packages/refs/tsconfig.json
index 4082f16a5..dc1970e16 100644
--- a/packages/refs/tsconfig.json
+++ b/packages/refs/tsconfig.json
@@ -1,3 +1,16 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/resize-observer/CHANGELOG.md b/packages/resize-observer/CHANGELOG.md
index 05ade420f..7c39b4a2f 100644
--- a/packages/resize-observer/CHANGELOG.md
+++ b/packages/resize-observer/CHANGELOG.md
@@ -1,5 +1,32 @@
# @solid-primitives/resize-observer
+## 2.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/event-listener@2.4.0
+ - @solid-primitives/static-store@0.1.0
+ - @solid-primitives/rootless@1.5.0
+ - @solid-primitives/utils@6.3.0
+
+## 2.0.27
+
+### Patch Changes
+
+- Updated dependencies [56d9511]
+ - @solid-primitives/static-store@0.0.9
+
+## 2.0.26
+
+### Patch Changes
+
+- a7338e7: Use generics in `createResizeObserver`
+
## 2.0.25
### Patch Changes
diff --git a/packages/resize-observer/README.md b/packages/resize-observer/README.md
index 4171e42d2..7eb188020 100644
--- a/packages/resize-observer/README.md
+++ b/packages/resize-observer/README.md
@@ -4,17 +4,16 @@
# @solid-primitives/resize-observer
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/resize-observer)
[](https://www.npmjs.com/package/@solid-primitives/resize-observer)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Reactive primitives for observing resizing of HTML elements.
-- [`makeResizeObserver`](#makeResizeObserver) — Instantiate a new ResizeObserver that automatically get's disposed on cleanup.
-- [`createResizeObserver`](#createResizeObserver) — Create resize observer instance, listening for changes to size of reactive element targets array.
-- [`createWindowSize`](#createWindowSize) — Creates a reactive store-like object of current width and height dimensions of the browser window.
-- [`createElementSize`](#createElementSize) — Creates a reactive store-like object of current width and height dimensions of html element.
+- [`makeResizeObserver`](#makeresizeobserver) — Instantiate a new ResizeObserver that automatically get's disposed on cleanup.
+- [`createResizeObserver`](#createresizeobserver) — Create resize observer instance, listening for changes to size of reactive element targets array.
+- [`createWindowSize`](#createwindowsize) — Creates a reactive store-like object of current width and height dimensions of the browser window.
+- [`createElementSize`](#createelementsize) — Creates a reactive store-like object of current width and height dimensions of html element.
## Installation
diff --git a/packages/resize-observer/package.json b/packages/resize-observer/package.json
index 1a2e4025c..8d4a84d07 100644
--- a/packages/resize-observer/package.json
+++ b/packages/resize-observer/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/resize-observer",
- "version": "2.0.25",
+ "version": "2.1.0",
"description": "Reactive primitives for observing resizing of HTML elements.",
"author": "Moshe Udimar",
"contributors": [
@@ -28,18 +28,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"keywords": [
diff --git a/packages/resize-observer/src/index.ts b/packages/resize-observer/src/index.ts
index 6f0458f3c..d41437622 100644
--- a/packages/resize-observer/src/index.ts
+++ b/packages/resize-observer/src/index.ts
@@ -13,10 +13,15 @@ import {
import { Accessor, createEffect, onCleanup, sharedConfig } from "solid-js";
import { isServer } from "solid-js/web";
-export type ResizeHandler = (
+type ResizeObserverEntryGeneric = ResizeObserverEntry & { readonly target: T };
+type ResizeObserverCallbackGeneric = (
+ entries: ResizeObserverEntryGeneric[],
+ observer: ResizeObserver,
+) => void;
+export type ResizeHandler = (
rect: DOMRectReadOnly,
- element: Element,
- entry: ResizeObserverEntry,
+ element: T,
+ entry: ResizeObserverEntryGeneric,
) => void;
export type Size = { width: number; height: number };
@@ -30,7 +35,7 @@ export type NullableSize = { width: number; height: number } | { width: null; he
* @returns `observe` and `unobserve` functions
*/
export function makeResizeObserver(
- callback: ResizeObserverCallback,
+ callback: ResizeObserverCallbackGeneric,
options?: ResizeObserverOptions,
): {
observe: (ref: T) => void;
@@ -39,7 +44,7 @@ export function makeResizeObserver(
if (isServer) {
return { observe: noop, unobserve: noop };
}
- const observer = new ResizeObserver(callback);
+ const observer = new ResizeObserver(callback as ResizeObserverCallback);
onCleanup(observer.disconnect.bind(observer));
return {
observe: ref => observer.observe(ref, options),
@@ -62,15 +67,15 @@ export function makeResizeObserver(
*
* ```
*/
-export function createResizeObserver(
- targets: MaybeAccessor>,
- onResize: ResizeHandler,
+export function createResizeObserver(
+ targets: MaybeAccessor>,
+ onResize: ResizeHandler,
options?: ResizeObserverOptions,
): void {
if (isServer) return;
- const previousMap = new WeakMap(),
- { observe, unobserve } = makeResizeObserver(entries => {
+ const previousMap = new WeakMap(),
+ { observe, unobserve } = makeResizeObserver(entries => {
for (const entry of entries) {
const { contentRect, target } = entry,
width = Math.round(contentRect.width),
@@ -83,7 +88,7 @@ export function createResizeObserver(
}
}, options);
- createEffect((prev: Element[]) => {
+ createEffect((prev: T[]) => {
const refs = filterNonNullable(asArray(access(targets)));
handleDiffArray(refs, prev, observe, unobserve);
return refs;
diff --git a/packages/resize-observer/tsconfig.json b/packages/resize-observer/tsconfig.json
index b5b011cbb..536eac32d 100644
--- a/packages/resize-observer/tsconfig.json
+++ b/packages/resize-observer/tsconfig.json
@@ -1,5 +1,25 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../event-listener"
+ },
+ {
+ "path": "../rootless"
+ },
+ {
+ "path": "../static-store"
+ },
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/resource/CHANGELOG.md b/packages/resource/CHANGELOG.md
index 51dbd8606..34bd1f88c 100644
--- a/packages/resource/CHANGELOG.md
+++ b/packages/resource/CHANGELOG.md
@@ -1,5 +1,29 @@
# @solid-primitives/resource
+## 0.4.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+## 0.3.1
+
+### Patch Changes
+
+- 89117a6: storage: expose init promise/value, resource: docs clarification
+
+## 0.3.0
+
+### Minor Changes
+
+- d7d0d70: resource: new primitive createAbortable (makeAbortable + cleanup)
+
+## 0.2.0
+
+### Minor Changes
+
+- 313f7d5: abort reason, filter abort error
+
## 0.1.2
### Patch Changes
diff --git a/packages/resource/README.md b/packages/resource/README.md
index 6e2d02f05..1fe53c8cf 100644
--- a/packages/resource/README.md
+++ b/packages/resource/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/resource
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/resource)
[](https://www.npmjs.com/package/@solid-primitives/resource)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
@@ -12,8 +11,9 @@
A collection of composable primitives to augment [`createResource`](https://www.solidjs.com/docs/latest/api#createresource)
- [`createAggregated`](#createaggregated) - wraps the resource to aggregate data instead of overwriting it
-- [`createDeepSignal`](#createdeepsignal) - provides a fine-grained signal for the [resource storage option](https://www.solidjs.com/docs/latest/api#createresource:~:text=Resources%20can%20be%20set%20with%20custom%20defined%20storage)
-- [`makeAbortable`](#makeabortable) - wraps the fetcher to be abortable and auto-abort on re-fetch or timeout
+- [`createDeepSignal`](#createdeepsignal) - provides a fine-grained signal for the [resource storage option](https://docs.solidjs.com/reference/basic-reactivity/create-resource#options)
+- [`makeAbortable`](#makeabortable) - sets up an AbortSignal with auto-abort on re-fetch or timeout
+- [`createAbortable`](#createabortable) - like `makeAbortable`, but with automatic abort on cleanup
- [`makeCache`](#makecache) - wraps the fetcher to cache the responses for a certain amount of time
- [`makeRetrying`](#makeretrying) - wraps the fetcher to retry requests after a delay
@@ -37,7 +37,7 @@ const [signal, abort] = makeAbortable({ timeout: 10000 });
const fetcher = (url: string) => fetch(url, { signal: signal() }).then(r => r.json());
-// cached fetcher will only be called if `url` source changes, or gets invalidated
+// cached fetcher will not be called if something for the same URL is still in cache
const [cachedFetcher, invalidate] = makeCache(fetcher, { storage: localStorage });
// works with createResource, or any wrapping API with the same interface
@@ -68,7 +68,7 @@ Objects and Arrays are re-created on each operation, but the values will be left
### createDeepSignal
-Usually resources in Solid.js are immutable. Every time the resource updates, every subscriber of it is updated. Starting with Solid.js 1.5, `createResource` allows to receive a function returning something akin to a signal in [`options.storage`](https://www.solidjs.com/docs/latest/api#createresource:~:text=Resources%20can%20be%20set%20with%20custom%20defined%20storage). This allows to provide the underlying storage for the resource in order to change its reactivity. This allows to add fine-grained reactivity to resources so that you can subscribe to nested properties and only trigger updates as they actually occur:
+Usually resources in Solid.js are immutable. Every time the resource updates, every subscriber of it is updated. Starting with Solid.js 1.5, `createResource` allows to receive a function returning something akin to a signal in [`options.storage`](https://docs.solidjs.com/reference/basic-reactivity/create-resource#options). This allows to provide the underlying storage for the resource in order to change its reactivity. This allows to add fine-grained reactivity to resources so that you can subscribe to nested properties and only trigger updates as they actually occur:
```ts
// this adds fine-grained reactivity to the contents of data():
@@ -91,7 +91,11 @@ Orchestrates AbortController creation and aborting of abortable fetchers, either
```ts
// definition
-const [signal: AbortSignal, abort: () => void] = makeAbortable({
+const [
+ signal: AbortSignal,
+ abort: () => void,
+ filterErrors: (err: E) => E instanceof AbortError ? void : E
+] = makeAbortable({
timeout?: 10000,
noAutoAbort?: true,
});
@@ -99,12 +103,17 @@ const [signal: AbortSignal, abort: () => void] = makeAbortable({
// usage
const fetcher = (url: string) => fetch(
url, { signal: signal() }
-).then(r => r.json());
+).then(r => r.json(), filterErrors);
```
-- The signal function always returns an unaborted signal; if `noAutoAbort` is not set to true, calling it will also abort a previous signal, if present
-- The abort callback will always abort the current signal
+- The `signal` function always returns a signal that is not yet aborted; if `noAutoAbort` is not set to true, calling it will also abort a previous signal, if present
+- The `abort` callback will always abort the current signal
- If `timeout` is set, the signal will be aborted after that many Milliseconds
+- The `filterErrors` function can be used to filter out abort errors
+
+### createAbortable
+
+This function does exactly the same as `makeAbortable`, but also automatically aborts on cleanup. Only use within a reactive scope.
### makeCache
@@ -240,7 +249,7 @@ const addTodo = todo => {
#### Scroll Restoration
-This is already covered in [solid-router](https://github.com/solidjs/solid-router).
+This is already covered in [@solidjs/router](https://github.com/solidjs/solid-router).
#### Polling
@@ -248,7 +257,7 @@ Just use an interval with refetch; ideally, also use [`makeAbortable`](#makeabor
## Demo
-You may view a working example of createFileSystem/makeVirtualFileSystem/makeWebAccessFileSystem here:
+You may view a working example of our resource primitives here:
https://primitives.solidjs.community/playground/resource/
## Changelog
diff --git a/packages/resource/package.json b/packages/resource/package.json
index 5fe3f8205..45a4785d1 100644
--- a/packages/resource/package.json
+++ b/packages/resource/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/resource",
- "version": "0.1.2",
+ "version": "0.4.0",
"description": "A template primitive example.",
"author": "Alex ",
"contributors": [],
@@ -20,6 +20,7 @@
"createAggregated",
"createDeepSignal",
"makeAbortable",
+ "createAbortable",
"makeCache",
"makeRetrying"
],
@@ -35,18 +36,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/resource/src/index.ts b/packages/resource/src/index.ts
index 7b76a64b5..b5da335e5 100644
--- a/packages/resource/src/index.ts
+++ b/packages/resource/src/index.ts
@@ -5,19 +5,22 @@ import {
type ResourceFetcher,
type ResourceFetcherInfo,
type Signal,
+ onCleanup,
} from "solid-js";
import { createStore, reconcile, unwrap } from "solid-js/store";
export type AbortableOptions = {
- noAutoAbort?: true;
+ noAutoAbort?: boolean;
timeout?: number;
};
/**
* **Creates and handles an AbortSignal**
* ```ts
- * const [signal, abort] = makeAbortable({ timeout: 10000 });
- * const fetcher = (url) => fetch(url, { signal: signal() });
+ * const [signal, abort, filterAbortError] =
+ * makeAbortable({ timeout: 10000 });
+ * const fetcher = (url) => fetch(url, { signal: signal() })
+ * .catch(filterAbortError); // filters abort errors
* ```
* Returns an accessor for the signal and the abort callback.
*
@@ -26,25 +29,59 @@ export type AbortableOptions = {
* - `noAutoAbort`: can be set to true to make a new source not automatically abort a previous request
*/
export function makeAbortable(
- options?: AbortableOptions,
-): [signal: () => AbortSignal, abort: () => void] {
+ options: AbortableOptions = {},
+): [
+ signal: () => AbortSignal,
+ abort: (reason?: string) => void,
+ filterAbortError: (err: any) => void,
+] {
let controller: AbortController | undefined;
let timeout: NodeJS.Timeout | number | undefined;
- const abort = () => {
+ const abort = (reason?: string) => {
timeout && clearTimeout(timeout);
- controller?.abort();
+ controller?.abort(reason);
};
const signal = () => {
- if (!options?.noAutoAbort && controller?.signal.aborted === false) {
- abort();
+ if (!options.noAutoAbort && controller?.signal.aborted === false) {
+ abort("retry");
}
controller = new AbortController();
- if (options?.timeout) {
- timeout = setTimeout(abort, options.timeout);
+ if (options.timeout) {
+ timeout = setTimeout(() => abort("timeout"), options.timeout);
}
return controller.signal;
};
- return [signal, abort];
+ return [
+ signal,
+ abort,
+ err => {
+ if (err.name === "AbortError") {
+ return undefined;
+ }
+ throw err;
+ },
+ ];
+}
+
+/**
+ * **Creates and handles an AbortSignal with automated cleanup**
+ * ```ts
+ * const [signal, abort, filterAbortError] =
+ * createAbortable();
+ * const fetcher = (url) => fetch(url, { signal: signal() })
+ * .catch(filterAbortError); // filters abort errors
+ * ```
+ * Returns an accessor for the signal and the abort callback.
+ *
+ * Options are optional and include:
+ * - `timeout`: time in Milliseconds after which the fetcher aborts automatically
+ * - `noAutoAbort`: can be set to true to make a new source not automatically abort a previous request
+ */
+
+export function createAbortable(options?: AbortableOptions) {
+ const [signal, abort, filterAbortError] = makeAbortable(options);
+ onCleanup(abort);
+ return [signal, abort, filterAbortError];
}
const mapEntries = (entries: [key: string, value: any][]) =>
@@ -115,7 +152,7 @@ export function makeCache(
const save = () => {
try {
options?.storage?.setItem(key, serialize(localCache));
- } catch (e) {
+ } catch (_) {
/**/
}
};
@@ -126,7 +163,7 @@ export function makeCache(
if (options?.storage) {
try {
Object.assign(localCache, deserialize(options.storage.getItem(key) || "{}"));
- } catch (e) {
+ } catch (_) {
/**/
}
}
diff --git a/packages/resource/test/index.test.ts b/packages/resource/test/index.test.ts
index b4e5e2532..6849b7eee 100644
--- a/packages/resource/test/index.test.ts
+++ b/packages/resource/test/index.test.ts
@@ -1,6 +1,5 @@
-import { createEffect, createResource, createSignal, on } from "solid-js";
+import { catchError, createEffect, createResource, createRoot, createSignal, on } from "solid-js";
import { describe, test, expect, vi, afterAll, beforeEach, beforeAll } from "vitest";
-import { testEffect } from "@solidjs/testing-library";
import {
makeAbortable,
createAggregated,
@@ -10,6 +9,33 @@ import {
createDeepSignal,
} from "../src/index.js";
+export function testEffect(
+ fn: (done: (result: T) => void) => void,
+): Promise {
+ let done: (result: T) => void;
+ let fail: (error: any) => void;
+ let promise = new Promise((resolve, reject) => {
+ done = resolve;
+ fail = reject;
+ });
+ createRoot(dispose => {
+ catchError(() => {
+ fn(result => {
+ done(result);
+ dispose();
+ });
+ }, fail);
+ });
+ return promise;
+}
+
+class AbortError extends Error {
+ constructor(msg: string) {
+ super(msg);
+ }
+ name = "AbortError";
+}
+
beforeAll(() => {
vi.useFakeTimers();
});
@@ -34,6 +60,18 @@ describe("makeAbortable", () => {
abort();
expect(signal2.aborted, "signal should be aborted when calling abort()").toBeTruthy();
});
+ test("filters (only) abort errors", async () => {
+ const [_signal, _abort, filterAbortError] = makeAbortable();
+ await Promise.reject(new AbortError("test"))
+ .catch(filterAbortError)
+ .then(resolution => expect(resolution).toBeUndefined())
+ .catch(err => expect.fail(err.message || "failed with error"));
+ const noAbortError = new Error("not an AbortError");
+ await Promise.reject(noAbortError)
+ .catch(filterAbortError)
+ .then(() => expect.fail("filtered error that was not an AbortError"))
+ .catch(err => expect(err).toBe(noAbortError));
+ });
});
describe("makeAggregated", () => {
diff --git a/packages/resource/tsconfig.json b/packages/resource/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/resource/tsconfig.json
+++ b/packages/resource/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/rootless/CHANGELOG.md b/packages/rootless/CHANGELOG.md
index 99163b7d4..6df15ba59 100644
--- a/packages/rootless/CHANGELOG.md
+++ b/packages/rootless/CHANGELOG.md
@@ -1,5 +1,16 @@
# @solid-primitives/rootless
+## 1.5.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/utils@6.3.0
+
## 1.4.5
### Patch Changes
diff --git a/packages/rootless/README.md b/packages/rootless/README.md
index 596b70edd..c6b6f1231 100644
--- a/packages/rootless/README.md
+++ b/packages/rootless/README.md
@@ -4,18 +4,17 @@
# @solid-primitives/rootless
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/rootless)
[](https://www.npmjs.com/package/@solid-primitives/rootless)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
A collection of helpers that aim to simplify using reactive primitives outside of reactive roots, and managing disposal of reactive roots.
-- [`createSubRoot`](#createSubRoot) - Creates a reactive **sub root**, that will be automatically disposed when it's owner does.
-- [`createCallback`](#createCallback) - A wrapper for creating callbacks with `runWithOwner`.
-- [`createDisposable`](#createDisposable) - For disposing computations early – before the root cleanup.
-- [`createSingletonRoot`](#createSingletonRoot) - Share "global primitives" across multiple reactive scopes.
-- [`createRootPool`](#createRootPool) - Creates a pool of reactive roots, that can be reused.
+- [`createSubRoot`](#createsubroot) - Creates a reactive **sub root**, that will be automatically disposed when it's owner does.
+- [`createCallback`](#createcallback) - A wrapper for creating callbacks with `runWithOwner`.
+- [`createDisposable`](#createdisposable) - For disposing computations early – before the root cleanup.
+- [`createSingletonRoot`](#createsingletonroot) - Share "global primitives" across multiple reactive scopes.
+- [`createRootPool`](#createrootpool) - Creates a pool of reactive roots, that can be reused.
## Installation
@@ -98,7 +97,7 @@ For disposing computations early – before the root cleanup.
### How to use it
-Executes provided function in a [`createSubRoot`](#createSubRoot) _(auto-disposing root)_, and returns a dispose function, to dispose computations used inside before automatic cleanup.
+Executes provided function in a [`createSubRoot`](#createsubroot) _(auto-disposing root)_, and returns a dispose function, to dispose computations used inside before automatic cleanup.
```ts
const dispose = createDisposable(dispose => {
diff --git a/packages/rootless/package.json b/packages/rootless/package.json
index f9216e6d3..774ac4700 100644
--- a/packages/rootless/package.json
+++ b/packages/rootless/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/rootless",
- "version": "1.4.5",
+ "version": "1.5.0",
"description": "A collection of helpers that aim to simplify using reactive primitives outside of reactive roots, and managing disposal of reactive roots.",
"author": "Damian Tarnawski @thetarnav ",
"license": "MIT",
@@ -27,18 +27,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
diff --git a/packages/rootless/tsconfig.json b/packages/rootless/tsconfig.json
index 4082f16a5..dc1970e16 100644
--- a/packages/rootless/tsconfig.json
+++ b/packages/rootless/tsconfig.json
@@ -1,3 +1,16 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/scheduled/CHANGELOG.md b/packages/scheduled/CHANGELOG.md
index 90e256e95..00749b67c 100644
--- a/packages/scheduled/CHANGELOG.md
+++ b/packages/scheduled/CHANGELOG.md
@@ -1,5 +1,17 @@
# @solid-primitives/scheduled
+## 1.5.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+## 1.4.4
+
+### Patch Changes
+
+- 57a3078: Add check for typeof window, fallback to throttle if undefined
+
## 1.4.3
### Patch Changes
diff --git a/packages/scheduled/README.md b/packages/scheduled/README.md
index f77b32088..9b4138c2e 100644
--- a/packages/scheduled/README.md
+++ b/packages/scheduled/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/scheduled
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/scheduled)
[](https://www.npmjs.com/package/@solid-primitives/scheduled)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
@@ -13,7 +12,7 @@ Primitives for creating scheduled — throttled or debounced — callbacks.
- [`debounce`](#debounce) - Creates a callback that is **debounced** and cancellable.
- [`throttle`](#throttle) - Creates a callback that is **throttled** and cancellable.
-- [`scheduleIdle`](#scheduleIdle) - Creates a callback throttled using `window.requestIdleCallback()`.
+- [`scheduleIdle`](#scheduleidle) - Creates a callback throttled using `window.requestIdleCallback()`.
- [`leading`](#leading) - Creates a scheduled and cancellable callback that will be called on **leading** edge.
- [`createScheduled`](#createscheduled) - Creates a signal used for scheduling execution of solid computations by tracking.
- [Scheduling explanation](#scheduling-explanation)
diff --git a/packages/scheduled/package.json b/packages/scheduled/package.json
index b8f53ac0e..e613c12af 100644
--- a/packages/scheduled/package.json
+++ b/packages/scheduled/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/scheduled",
- "version": "1.4.3",
+ "version": "1.5.0",
"description": "Primitives for creating scheduled — throttled or debounced — callbacks.",
"contributors": [
"David Di Biase ",
@@ -35,18 +35,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/scheduled/src/index.ts b/packages/scheduled/src/index.ts
index 65e676437..083a884fa 100644
--- a/packages/scheduled/src/index.ts
+++ b/packages/scheduled/src/index.ts
@@ -112,7 +112,7 @@ export const throttle: ScheduleCallback = (callback, wait) => {
export const scheduleIdle: ScheduleCallback = isServer
? () => Object.assign(() => void 0, { clear: () => void 0 })
: // requestIdleCallback is not supported in Safari
- (window.requestIdleCallback as typeof window.requestIdleCallback | undefined)
+ typeof requestIdleCallback !== "undefined"
? (callback, maxWait) => {
let isDeferred = false,
id: ReturnType,
diff --git a/packages/scheduled/tsconfig.json b/packages/scheduled/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/scheduled/tsconfig.json
+++ b/packages/scheduled/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/script-loader/CHANGELOG.md b/packages/script-loader/CHANGELOG.md
index d1f0fec52..192e9b93b 100644
--- a/packages/script-loader/CHANGELOG.md
+++ b/packages/script-loader/CHANGELOG.md
@@ -1,5 +1,17 @@
# @solid-primitives/script-loader
+## 2.3.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+## 2.2.0
+
+### Minor Changes
+
+- 7e47dbd: fix events in firefox
+
## 2.1.2
### Patch Changes
diff --git a/packages/script-loader/README.md b/packages/script-loader/README.md
index ba567a83b..de0a3f40b 100644
--- a/packages/script-loader/README.md
+++ b/packages/script-loader/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/script-loader
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/script-loader)
[](https://www.npmjs.com/package/@solid-primitives/script-loader)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
diff --git a/packages/script-loader/package.json b/packages/script-loader/package.json
index 403aca8d1..578e3f063 100644
--- a/packages/script-loader/package.json
+++ b/packages/script-loader/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/script-loader",
- "version": "2.1.2",
+ "version": "2.3.0",
"description": "Primitive to load scripts dynamically",
"author": "Alex Lohr ",
"contributors": [
@@ -26,18 +26,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/script-loader/src/index.ts b/packages/script-loader/src/index.ts
index 58f91ad15..de7017b4b 100644
--- a/packages/script-loader/src/index.ts
+++ b/packages/script-loader/src/index.ts
@@ -1,4 +1,11 @@
-import { Accessor, createRenderEffect, onCleanup, splitProps, type ComponentProps } from "solid-js";
+import {
+ Accessor,
+ createRenderEffect,
+ onCleanup,
+ splitProps,
+ type ComponentProps,
+ type JSX,
+} from "solid-js";
import { spread, isServer } from "solid-js/web";
export type ScriptProps = Omit, "src" | "textContent"> & {
@@ -32,9 +39,33 @@ export function createScriptLoader(props: ScriptProps): HTMLScriptElement | unde
return undefined;
}
const script = document.createElement("script");
- const [local, scriptProps] = splitProps(props, OMITTED_PROPS);
+ const eventKeys: string[] = Object.keys(props).filter(p => p.startsWith("on"));
+ const [local, events, scriptProps] = splitProps(
+ props,
+ OMITTED_PROPS,
+ eventKeys as readonly (keyof typeof props)[],
+ );
setTimeout(() => spread(script, scriptProps, false, true));
createRenderEffect(() => {
+ Object.entries(events).forEach(
+ ([name, handler]: [string, JSX.EventHandlerUnion]) =>
+ script.addEventListener(
+ /^on:?(.*)/.test(name)
+ ? name.startsWith("on:")
+ ? RegExp.$1
+ : RegExp.$1.toLowerCase()
+ : name,
+ (ev: Event) => {
+ Object.defineProperties(ev, {
+ target: { value: script, enumerable: true },
+ currentTarget: { value: script, enumerable: true },
+ });
+ Array.isArray(handler)
+ ? handler[0](handler[1], ev)
+ : typeof handler === "function" && handler.call(null, Object.assign(ev));
+ },
+ ),
+ );
const src = typeof local.src === "string" ? local.src : local.src();
const prop = /^(https?:|\w[\.\w-_%]+|)\//.test(src) ? "src" : "textContent";
if (script[prop] !== src) {
diff --git a/packages/script-loader/tsconfig.json b/packages/script-loader/tsconfig.json
index b5b011cbb..38c71ce71 100644
--- a/packages/script-loader/tsconfig.json
+++ b/packages/script-loader/tsconfig.json
@@ -1,5 +1,12 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/scroll/CHANGELOG.md b/packages/scroll/CHANGELOG.md
index 6b4850e43..ef57acdcc 100644
--- a/packages/scroll/CHANGELOG.md
+++ b/packages/scroll/CHANGELOG.md
@@ -1,5 +1,25 @@
# @solid-primitives/scroll
+## 2.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/event-listener@2.4.0
+ - @solid-primitives/static-store@0.1.0
+ - @solid-primitives/rootless@1.5.0
+
+## 2.0.24
+
+### Patch Changes
+
+- Updated dependencies [56d9511]
+ - @solid-primitives/static-store@0.0.9
+
## 2.0.23
### Patch Changes
diff --git a/packages/scroll/README.md b/packages/scroll/README.md
index 500d5eb67..ed2ed67ac 100644
--- a/packages/scroll/README.md
+++ b/packages/scroll/README.md
@@ -4,15 +4,14 @@
# @solid-primitives/scroll
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/scroll)
[](https://www.npmjs.com/package/@solid-primitives/scroll)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Reactive primitives to react to element/window scrolling.
-- [`createScrollPosition`](#createScrollPosition) - Reactive primitive providing a store-like object with current scroll position of specified target.
-- [`useWindowScrollPosition`](#useWindowScrollPosition) - Returns a reactive object with current window scroll position.
+- [`createScrollPosition`](#createscrollposition) - Reactive primitive providing a store-like object with current scroll position of specified target.
+- [`useWindowScrollPosition`](#usewindowscrollposition) - Returns a reactive object with current window scroll position.
## Installation
diff --git a/packages/scroll/package.json b/packages/scroll/package.json
index 6ccc4f62b..1b7736a0a 100644
--- a/packages/scroll/package.json
+++ b/packages/scroll/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/scroll",
- "version": "2.0.23",
+ "version": "2.1.0",
"description": "Reactive primitives to react to element/window scrolling.",
"author": "David Di Biase ",
"contributors": [
@@ -27,18 +27,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/scroll/tsconfig.json b/packages/scroll/tsconfig.json
index b5b011cbb..f696f1546 100644
--- a/packages/scroll/tsconfig.json
+++ b/packages/scroll/tsconfig.json
@@ -1,5 +1,22 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../event-listener"
+ },
+ {
+ "path": "../rootless"
+ },
+ {
+ "path": "../static-store"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/selection/CHANGELOG.md b/packages/selection/CHANGELOG.md
index db4cc555e..9a14ba8a8 100644
--- a/packages/selection/CHANGELOG.md
+++ b/packages/selection/CHANGELOG.md
@@ -1,5 +1,11 @@
# @solid-primitives/selection
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
## 0.0.8
### Patch Changes
diff --git a/packages/selection/README.md b/packages/selection/README.md
index bfa5072a0..4ed73db82 100644
--- a/packages/selection/README.md
+++ b/packages/selection/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/selection
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/selection)
[](https://www.npmjs.com/package/@solid-primitives/selection)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
diff --git a/packages/selection/package.json b/packages/selection/package.json
index e3918c474..d131910ac 100644
--- a/packages/selection/package.json
+++ b/packages/selection/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/selection",
- "version": "0.0.8",
+ "version": "0.1.0",
"description": "selection primitive.",
"author": "Alex Lohr ",
"contributors": [],
@@ -23,18 +23,14 @@
},
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"files": [
diff --git a/packages/selection/tsconfig.json b/packages/selection/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/selection/tsconfig.json
+++ b/packages/selection/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/set/CHANGELOG.md b/packages/set/CHANGELOG.md
index 1f077471a..d9cc9c0bc 100644
--- a/packages/set/CHANGELOG.md
+++ b/packages/set/CHANGELOG.md
@@ -1,5 +1,37 @@
# @solid-primitives/set
+## 0.6.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/trigger@1.2.0
+
+## 0.5.0
+
+### Minor Changes
+
+- aa596ec: Fixes for ReactiveSet (#688):
+ - Iterators and `.forEach()` no longer track specific keys.
+ - Added support for `thisArg` as per `forEach` spec
+ - `super.clear()` now called before dirtying signals
+ - Uses new `dirtyAll` form trigger package
+
+### Patch Changes
+
+- Updated dependencies [32fcb81]
+ - @solid-primitives/trigger@1.1.0
+
+## 0.4.12
+
+### Patch Changes
+
+- 1ea2a09: Use new Map/SetIterator types
+
## 0.4.11
### Patch Changes
diff --git a/packages/set/README.md b/packages/set/README.md
index ee944de24..5108b3bb3 100644
--- a/packages/set/README.md
+++ b/packages/set/README.md
@@ -4,15 +4,14 @@
# @solid-primitives/set
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/set)
[](https://www.npmjs.com/package/@solid-primitives/set)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
The Javascript built-in `Set` & `WeakSet` data structures as a reactive signals.
-- **[`ReactiveSet`](#ReactiveSet)** - A reactive version of a Javascript built-in `Set` class.
-- **[`ReactiveWeakSet`](#ReactiveWeakSet)** - A reactive version of a Javascript built-in `WeakSet` class.
+- **[`ReactiveSet`](#reactiveset)** - A reactive version of a Javascript built-in `Set` class.
+- **[`ReactiveWeakSet`](#reactiveweakset)** - A reactive version of a Javascript built-in `WeakSet` class.
## Installation
diff --git a/packages/set/package.json b/packages/set/package.json
index cf6587494..0e6e8239b 100644
--- a/packages/set/package.json
+++ b/packages/set/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/set",
- "version": "0.4.11",
+ "version": "0.6.0",
"description": "The Set & WeakSet data structures as a reactive signals.",
"author": "Damian Tarnawski @thetarnav ",
"license": "MIT",
@@ -31,18 +31,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/set/src/index.ts b/packages/set/src/index.ts
index e22b8ce79..26d81653c 100644
--- a/packages/set/src/index.ts
+++ b/packages/set/src/index.ts
@@ -20,72 +20,79 @@ export class ReactiveSet extends Set {
constructor(values?: Iterable | null) {
super();
- if (values) for (const v of values) super.add(v);
+ if (values) for (const value of values) super.add(value);
+ }
+
+ [Symbol.iterator](): SetIterator {
+ return this.values();
}
- // reads
get size(): number {
this.#triggers.track($KEYS);
return super.size;
}
- has(v: T): boolean {
- this.#triggers.track(v);
- return super.has(v);
- }
- *keys(): IterableIterator {
- for (const key of super.keys()) {
- this.#triggers.track(key);
- yield key;
- }
- this.#triggers.track($KEYS);
+ has(value: T): boolean {
+ this.#triggers.track(value);
+ return super.has(value);
}
- values(): IterableIterator {
- return this.keys();
+
+ keys(): SetIterator {
+ return this.values();
}
- *entries(): IterableIterator<[T, T]> {
- for (const key of super.keys()) {
- this.#triggers.track(key);
- yield [key, key];
- }
+
+ *values(): SetIterator {
this.#triggers.track($KEYS);
+
+ for (const value of super.values()) {
+ yield value;
+ }
}
- [Symbol.iterator](): IterableIterator {
- return this.values();
+ *entries(): SetIterator<[T, T]> {
+ this.#triggers.track($KEYS);
+
+ for (const entry of super.entries()) {
+ yield entry;
+ }
}
- forEach(callbackfn: (value: T, value2: T, set: this) => void) {
+
+ forEach(callbackfn: (value1: T, value2: T, set: Set) => void, thisArg?: any): void {
this.#triggers.track($KEYS);
- super.forEach(callbackfn as any);
+ super.forEach(callbackfn, thisArg);
}
- // writes
- add(v: T): this {
- if (!super.has(v)) {
- super.add(v);
+ add(value: T): this {
+ if (!super.has(value)) {
+ super.add(value);
batch(() => {
- this.#triggers.dirty(v);
+ this.#triggers.dirty(value);
this.#triggers.dirty($KEYS);
});
}
+
return this;
}
- delete(v: T): boolean {
- const r = super.delete(v);
- if (r) {
+
+ delete(value: T): boolean {
+ const result = super.delete(value);
+
+ if (result) {
batch(() => {
- this.#triggers.dirty(v);
+ this.#triggers.dirty(value);
this.#triggers.dirty($KEYS);
});
}
- return r;
+
+ return result;
}
+
clear(): void {
if (super.size) {
+ super.clear();
+
batch(() => {
- for (const v of super.keys()) this.#triggers.dirty(v);
- super.clear();
- this.#triggers.dirty($KEYS);
+ this.#triggers.dirtyAll();
});
}
}
@@ -106,24 +113,28 @@ export class ReactiveWeakSet extends WeakSet {
constructor(values?: Iterable | null) {
super();
- if (values) for (const v of values) super.add(v);
+ if (values) for (const value of values) super.add(value);
}
- has(v: T): boolean {
- this.#triggers.track(v);
- return super.has(v);
+ has(value: T): boolean {
+ this.#triggers.track(value);
+ return super.has(value);
}
- add(v: T): this {
- if (!super.has(v)) {
- super.add(v);
- this.#triggers.dirty(v);
+
+ add(value: T): this {
+ if (!super.has(value)) {
+ super.add(value);
+ this.#triggers.dirty(value);
}
return this;
}
- delete(v: T): boolean {
- const deleted = super.delete(v);
- deleted && this.#triggers.dirty(v);
- return deleted;
+
+ delete(value: T): boolean {
+ const result = super.delete(value);
+
+ result && this.#triggers.dirty(value);
+
+ return result;
}
}
diff --git a/packages/set/test/index.test.ts b/packages/set/test/index.test.ts
index 8d97062e5..412fba986 100644
--- a/packages/set/test/index.test.ts
+++ b/packages/set/test/index.test.ts
@@ -100,11 +100,8 @@ describe("ReactiveSet", () => {
const dispose = createRoot(dispose => {
createEffect(() => {
const run: number[] = [];
- let i = 0;
for (const key of fn(set)) {
run.push(key);
- if (i === 2) break; // don't iterate over all keys
- i += 1;
}
captured.push(run);
});
@@ -112,21 +109,30 @@ describe("ReactiveSet", () => {
});
expect(captured).toHaveLength(1);
- expect(captured[0]).toEqual([1, 2, 3]);
+ expect(captured[0]).toEqual([1, 2, 3, 4]);
set.delete(4);
- expect(captured, "deleted unseen key").toHaveLength(1);
+ expect(captured).toHaveLength(2);
+ expect(captured[1]).toEqual([1, 2, 3]);
set.delete(1);
- expect(captured, "deleted seen").toHaveLength(2);
- expect(captured[1]).toEqual([2, 3]);
+ expect(captured).toHaveLength(3);
+ expect(captured[2]).toEqual([2, 3]);
set.add(4);
- expect(captured, "added key in reach").toHaveLength(3);
- expect(captured[2]).toEqual([2, 3, 4]);
+ expect(captured).toHaveLength(4);
+ expect(captured[3]).toEqual([2, 3, 4]);
set.add(5);
- expect(captured, "added key out of reach").toHaveLength(3);
+ expect(captured).toHaveLength(5);
+ expect(captured[4]).toEqual([2, 3, 4, 5]);
+
+ set.add(5);
+ expect(captured).toHaveLength(5);
+
+ set.clear();
+ expect(captured).toHaveLength(6);
+ expect(captured[5]).toEqual([]);
dispose();
});
diff --git a/packages/set/tsconfig.json b/packages/set/tsconfig.json
index b5b011cbb..5554300f1 100644
--- a/packages/set/tsconfig.json
+++ b/packages/set/tsconfig.json
@@ -1,5 +1,16 @@
{
"extends": "../../tsconfig.json",
- "include": ["./src", "./test", "./dev"],
- "exclude": ["node_modules", "./dist"]
-}
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../trigger"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/share/CHANGELOG.md b/packages/share/CHANGELOG.md
index 59126c105..6e37953eb 100644
--- a/packages/share/CHANGELOG.md
+++ b/packages/share/CHANGELOG.md
@@ -1,5 +1,11 @@
# @solid-primitives/share
+## 2.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
## 2.0.6
### Patch Changes
diff --git a/packages/share/README.md b/packages/share/README.md
index 75574a25e..dab8a6aa0 100644
--- a/packages/share/README.md
+++ b/packages/share/README.md
@@ -4,16 +4,15 @@
# @solid-primitives/share
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/share)
[](https://www.npmjs.com/package/@solid-primitives/share)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Primitives for supporting sharing of resources on social media and beyond.
-- [`createSocialShare`](#createSocialShare) - A primitive for sharing on social media and beyond.
-- [`makeWebShare`](#makeWebShare) - Generates a simple non-reactive WebShare primitive for sharing.
-- [`createWebShare`](#createWebShare) - Creates a reactive status about web share.
+- [`createSocialShare`](#createsocialshare) - A primitive for sharing on social media and beyond.
+- [`makeWebShare`](#makewebshare) - Generates a simple non-reactive WebShare primitive for sharing.
+- [`createWebShare`](#createwebshare) - Creates a reactive status about web share.
## Installation
diff --git a/packages/share/package.json b/packages/share/package.json
index b5dbe3f40..ee0483b33 100644
--- a/packages/share/package.json
+++ b/packages/share/package.json
@@ -1,8 +1,8 @@
{
"name": "@solid-primitives/share",
- "version": "2.0.6",
+ "version": "2.1.0",
"description": "Primitives to help with sharing content on social media and beyond.",
- "author": "David Di Biase ",
"contributors": [
"Omer Ma",
"Tom Pichaud "
@@ -34,18 +34,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/share/tsconfig.json b/packages/share/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/share/tsconfig.json
+++ b/packages/share/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/signal-builders/CHANGELOG.md b/packages/signal-builders/CHANGELOG.md
index 1cef1d29b..896946d84 100644
--- a/packages/signal-builders/CHANGELOG.md
+++ b/packages/signal-builders/CHANGELOG.md
@@ -1,5 +1,16 @@
# @solid-primitives/signal-builders
+## 0.2.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/utils@6.3.0
+
## 0.1.17
### Patch Changes
diff --git a/packages/signal-builders/README.md b/packages/signal-builders/README.md
index f785fa095..baccc5c17 100644
--- a/packages/signal-builders/README.md
+++ b/packages/signal-builders/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/signal-builders
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/signal-builders)
[](https://www.npmjs.com/package/@solid-primitives/signal-builders)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
diff --git a/packages/signal-builders/package.json b/packages/signal-builders/package.json
index 1742159d1..9fb308950 100644
--- a/packages/signal-builders/package.json
+++ b/packages/signal-builders/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/signal-builders",
- "version": "0.1.17",
+ "version": "0.2.0",
"description": "A collection of chainable and composable reactive signal calculations, aka Signal Builders.",
"author": "Your Name ",
"license": "MIT",
@@ -28,18 +28,14 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"browser": {},
"types": "./dist/index.d.ts",
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"scripts": {
diff --git a/packages/signal-builders/tsconfig.json b/packages/signal-builders/tsconfig.json
index 4082f16a5..dc1970e16 100644
--- a/packages/signal-builders/tsconfig.json
+++ b/packages/signal-builders/tsconfig.json
@@ -1,3 +1,16 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/spring/CHANGELOG.md b/packages/spring/CHANGELOG.md
new file mode 100644
index 000000000..5dfa2ff8f
--- /dev/null
+++ b/packages/spring/CHANGELOG.md
@@ -0,0 +1,7 @@
+# @solid-primitives/spring
+
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
diff --git a/packages/spring/LICENSE b/packages/spring/LICENSE
new file mode 100644
index 000000000..38b41d975
--- /dev/null
+++ b/packages/spring/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Solid Primitives Working Group
+
+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.
\ No newline at end of file
diff --git a/packages/spring/README.md b/packages/spring/README.md
new file mode 100644
index 000000000..6d90e1e8c
--- /dev/null
+++ b/packages/spring/README.md
@@ -0,0 +1,62 @@
+
+
+
+
+# @solid-primitives/spring
+
+[](https://bundlephobia.com/package/@solid-primitives/spring)
+[](https://www.npmjs.com/package/@solid-primitives/spring)
+[](https://github.com/solidjs-community/solid-primitives#contribution-process)
+
+A small SolidJS hook to interpolate signal changes with spring physics. Inspired by & directly forked from [`svelte-motion/spring`](https://svelte.dev/docs/svelte-motion#spring) as such, has a very familiar API design.
+
+With this primitive, you can easily animate values that can be interpolated like `number`, `date`, and collections (arrays or nested objects) of those datatypes.
+
+- `createSpring` - Provides a getter and setter for the spring primitive.
+- `createDerivedSpring` - Provides only a getter for the spring primitive deriving from an accessor parameter. Similar to the [@solid-primitives/tween](https://github.com/solidjs-community/solid-primitives/tree/main/packages/tween) API.
+
+The following physics options are available:
+
+- `stiffness` (number, default `0.15`) — a value between 0 and 1 where higher means a 'tighter' spring
+- `damping` (number, default `0.8`) — a value between 0 and 1 where lower means a 'springier' spring
+- `precision` (number, default `0.01`) — determines the threshold at which the spring is considered to have 'settled', where lower means more precise
+
+## Installation
+
+```bash
+npm install @solid-primitives/spring
+# or
+yarn add @solid-primitives/spring
+# or
+pnpm add @solid-primitives/spring
+```
+
+## How to use it
+
+```ts
+// Basic Example
+const [progress, setProgress] = createSpring(0);
+
+// Example with options (less sudden movement)
+const [radialProgress, setRadialProgress] = createSpring(0, { stiffness: 0.05 });
+
+// Example with collections (e.g. Object or Array).
+const [xy, setXY] = createSpring(
+ { x: 50, y: 50 },
+ { stiffness: 0.08, damping: 0.2, precision: 0.01 },
+);
+
+// Example deriving from an existing signal.
+const [myNumber, myNumber] = createSignal(20);
+const springedValue = createDerivedSpring(myNumber, { stiffness: 0.03 });
+```
+
+## Demo
+
+- **[Playground](https://primitives.solidjs.community/playground/spring)** - [source code](https://github.com/solidjs-community/solid-primitives/blob/main/packages/spring/dev/index.tsx)
+
+- **[CodeSandbox - Basic Example](https://codesandbox.io/p/devbox/ecstatic-borg-k2wqfr)**
+
+## Changelog
+
+See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/spring/dev/index.tsx b/packages/spring/dev/index.tsx
new file mode 100644
index 000000000..a6d5637e4
--- /dev/null
+++ b/packages/spring/dev/index.tsx
@@ -0,0 +1,120 @@
+import { createSpring } from "../src/index.js";
+
+export default function App() {
+ const [progress, setProgress] = createSpring(0);
+ const [radialProgress, setRadialProgress] = createSpring(0, {
+ stiffness: 0.1,
+ damping: 0.3,
+ });
+ const [xy, setXY] = createSpring({ x: 50, y: 50 }, { stiffness: 0.1, damping: 0.3 });
+ const [date, setDate] = createSpring(new Date());
+
+ function toggleProgress() {
+ if (progress() === 0) setProgress(1);
+ else setProgress(0);
+ }
+ function toggleRadialProgress() {
+ if (radialProgress() === 0) setRadialProgress(1);
+ else setRadialProgress(0);
+ }
+ let d = false;
+ function toggleXY() {
+ if ((d = !d)) setXY({ x: 200, y: 200 });
+ else setXY({ x: 50, y: 50 });
+ }
+ function toggleDate() {
+ if (date().getDate() === new Date("2024-12-01").getDate()) setDate(new Date("2024-04-14"));
+ else setDate(new Date("2024-12-01"));
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/packages/spring/package.json b/packages/spring/package.json
new file mode 100644
index 000000000..5a926044e
--- /dev/null
+++ b/packages/spring/package.json
@@ -0,0 +1,61 @@
+{
+ "name": "@solid-primitives/spring",
+ "version": "0.1.0",
+ "description": "Primitive that creates spring physics functions.",
+ "author": "Carlo Taleon ",
+ "contributors": [
+ "Damian Tarnawski "
+ ],
+ "license": "MIT",
+ "homepage": "https://primitives.solidjs.community/package/spring",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/solidjs-community/solid-primitives.git"
+ },
+ "bugs": {
+ "url": "https://github.com/solidjs-community/solid-primitives/issues"
+ },
+ "primitive": {
+ "name": "spring",
+ "stage": 0,
+ "list": [
+ "createSpring",
+ "createDerivedSpring"
+ ],
+ "category": "Animation"
+ },
+ "keywords": [
+ "animate",
+ "tween",
+ "spring",
+ "solid",
+ "primitives"
+ ],
+ "private": false,
+ "sideEffects": false,
+ "files": [
+ "dist"
+ ],
+ "type": "module",
+ "module": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "browser": {},
+ "exports": {
+ "@solid-primitives/source": "./src/index.ts",
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "typesVersions": {},
+ "scripts": {
+ "dev": "tsx ../../scripts/dev.ts",
+ "build": "tsx ../../scripts/build.ts",
+ "vitest": "vitest -c ../../configs/vitest.config.ts",
+ "test": "pnpm run vitest",
+ "test:ssr": "pnpm run vitest --mode ssr"
+ },
+ "peerDependencies": {
+ "solid-js": "^1.6.12"
+ }
+}
diff --git a/packages/spring/src/index.ts b/packages/spring/src/index.ts
new file mode 100644
index 000000000..5d68947e8
--- /dev/null
+++ b/packages/spring/src/index.ts
@@ -0,0 +1,219 @@
+import { Accessor, createEffect, createSignal, onCleanup } from "solid-js";
+import { isServer } from "solid-js/web";
+
+// https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/motion/utils.js
+
+function is_date(obj: any): obj is Date {
+ return Object.prototype.toString.call(obj) === "[object Date]";
+}
+
+// ===========================================================================
+// createSpring hook
+// ===========================================================================
+
+export type SpringOptions = {
+ /**
+ * Stiffness of the spring. Higher values will create more sudden movement.
+ * @default 0.15
+ */
+ stiffness?: number;
+ /**
+ * Strength of opposing force. If set to 0, spring will oscillate indefinitely.
+ * @default 0.8
+ */
+ damping?: number;
+ /**
+ * Precision is the threshold relative to the target value at which the
+ * animation will stop based on the current value.
+ *
+ * From 0, if the target value is 500, and the precision is 500, it will stop
+ * the animation instantly (no animation, similar to `hard: true`).
+ *
+ * From 0, if the target value is 500, and the precision is 0.01, it will stop the
+ * animation when the current value reaches 499.99 or 500.01 (longer animation).
+ *
+ * @default 0.01
+ */
+ precision?: number;
+};
+
+export type SpringTarget =
+ | number
+ | Date
+ | { [key: string]: number | Date | SpringTarget }
+ | readonly (number | Date)[]
+ | readonly SpringTarget[];
+
+/**
+ * "Widen" Utility Type so that number types are not converted to
+ * literal types when passed to `createSpring`.
+ *
+ * e.g. createSpring(0) returns `0`, not `number`.
+ */
+export type WidenSpringTarget = T extends number ? number : T;
+
+export type SpringSetterOptions = { hard?: boolean; soft?: boolean | number };
+export type SpringSetter = (
+ newValue: T | ((prev: T) => T),
+ opts?: SpringSetterOptions,
+) => Promise;
+
+/**
+ * Creates a signal and a setter that uses spring physics when interpolating from
+ * one value to another. This means when the value changes, instead of
+ * transitioning at a steady rate, it "bounces" like a spring would,
+ * depending on the physics paramters provided. This adds a level of realism to
+ * the transitions and can enhance the user experience.
+ *
+ * `T` - The type of the signal. It works for the basic data types that can be
+ * interpolated: `number`, a `Date`, `Array` or a nested object of T.
+ *
+ * @param initialValue The initial value of the signal.
+ * @param options Options to configure the physics of the spring.
+ * @returns Returns the spring value and a setter.
+ *
+ * @example
+ * const [progress, setProgress] = createSpring(0, { stiffness: 0.15, damping: 0.8 });
+ */
+export function createSpring(
+ initialValue: T,
+ options: SpringOptions = {},
+): [Accessor>, SpringSetter>] {
+ const [signal, setSignal] = createSignal(initialValue);
+ const { stiffness = 0.15, damping = 0.8, precision = 0.01 } = options;
+
+ if (isServer) {
+ return [
+ signal as any,
+ ((param: any, opts: SpringSetterOptions = {}) => {
+ if (opts.hard || (stiffness >= 1 && damping >= 1)) {
+ setSignal(param);
+ return Promise.resolve();
+ }
+ return new Promise(() => {});
+ }) as any,
+ ];
+ }
+
+ let value_current = initialValue;
+ let value_last = initialValue;
+ let value_target = initialValue;
+ let inv_mass = 1;
+ let inv_mass_recovery_rate = 0;
+ let raf_id = 0;
+ let settled = true;
+ let time_last = 0;
+ let time_delta = 0;
+ let resolve = () => {};
+
+ const cleanup = onCleanup(() => {
+ cancelAnimationFrame(raf_id);
+ raf_id = 0;
+ resolve();
+ });
+
+ const frame: FrameRequestCallback = time => {
+ time_delta = Math.max(1 / 60, ((time - time_last) * 60) / 1000); // guard against d<=0
+ time_last = time;
+
+ inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1);
+ settled = true;
+
+ const new_value = tick(value_last, value_current, value_target);
+ value_last = value_current;
+ setSignal((value_current = new_value));
+
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (settled) {
+ cleanup();
+ } else {
+ raf_id = requestAnimationFrame(frame);
+ }
+ };
+
+ const set: SpringSetter = (param, opts = {}) => {
+ value_target = typeof param === "function" ? param(value_current) : param;
+
+ if (opts.hard || (stiffness >= 1 && damping >= 1)) {
+ cleanup();
+ setSignal(_ => (value_current = value_last = value_target));
+ return Promise.resolve();
+ }
+
+ if (opts.soft) {
+ inv_mass_recovery_rate = 1 / (typeof opts.soft === "number" ? opts.soft * 60 : 30);
+ inv_mass = 0; // Infinite mass, unaffected by spring forces.
+ }
+
+ if (raf_id === 0) {
+ time_last = performance.now();
+ raf_id = requestAnimationFrame(frame);
+ }
+
+ return new Promise(r => (resolve = r));
+ };
+
+ const tick = (last: T, current: T, target: T): any => {
+ if (typeof current === "number" || is_date(current)) {
+ const delta = +target - +current;
+ const velocity = (+current - +last) / time_delta;
+ const spring = stiffness * delta;
+ const damper = damping * velocity;
+ const acceleration = (spring - damper) * inv_mass;
+ const d = (velocity + acceleration) * time_delta;
+
+ if (Math.abs(d) < precision && Math.abs(delta) < precision) {
+ return target; // settled
+ }
+
+ settled = false; // signal loop to keep ticking
+ return typeof current === "number" ? current + d : new Date(+current + d);
+ }
+
+ if (Array.isArray(current)) {
+ // @ts-expect-error
+ return current.map((_, i) => tick(last[i], current[i], target[i]));
+ }
+
+ if (typeof current === "object") {
+ const next = { ...current };
+ for (const k in current) {
+ // @ts-expect-error
+ next[k] = tick(last[k], current[k], target[k]);
+ }
+ return next;
+ }
+
+ throw new Error(`Cannot spring ${typeof current} values`);
+ };
+
+ return [signal as any, set as any];
+}
+
+// ===========================================================================
+// createDerivedSpring hook
+// ===========================================================================
+
+/**
+ * Creates a spring value that interpolates based on changes on a passed signal.
+ * Works similar to the `@solid-primitives/tween`
+ *
+ * @param target Target to be modified.
+ * @param options Options to configure the physics of the spring.
+ * @returns Returns the spring value only.
+ *
+ * @example
+ * const percent = createMemo(() => current() / total() * 100);
+ *
+ * const springedPercent = createDerivedSignal(percent, { stiffness: 0.15, damping: 0.8 });
+ */
+export function createDerivedSpring(
+ target: Accessor,
+ options?: SpringOptions,
+) {
+ const [springValue, setSpringValue] = createSpring(target(), options);
+
+ createEffect(() => setSpringValue(target() as WidenSpringTarget));
+
+ return springValue;
+}
diff --git a/packages/spring/test/index.test.ts b/packages/spring/test/index.test.ts
new file mode 100644
index 000000000..ecdd74def
--- /dev/null
+++ b/packages/spring/test/index.test.ts
@@ -0,0 +1,204 @@
+import { createEffect, createRoot, createSignal } from "solid-js";
+import { describe, expect, it, vi, afterAll } from "vitest";
+import { createDerivedSpring, createSpring } from "../src/index.js";
+
+let _time = 0;
+let _raf_last_id = 0;
+let _raf_callbacks_old = new Map();
+let _raf_callbacks_new = new Map();
+
+function _progress_time(by: number) {
+ _time += by;
+ _raf_callbacks_old = _raf_callbacks_new;
+ _raf_callbacks_new = new Map();
+ _raf_callbacks_old.forEach(c => c(_time));
+ _raf_callbacks_old.clear();
+}
+
+let _now = performance.now;
+performance.now = () => _time;
+afterAll(() => {
+ performance.now = _now;
+});
+
+vi.stubGlobal("requestAnimationFrame", function (callback: FrameRequestCallback): number {
+ const id = _raf_last_id++;
+ _raf_callbacks_new.set(id, callback);
+ return id;
+});
+vi.stubGlobal("cancelAnimationFrame", function (id: number): void {
+ _raf_callbacks_new.delete(id);
+});
+
+describe("createSpring", () => {
+ it("returns values", () => {
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring({ progress: 0 }), d]);
+ expect(spring().progress).toBe(0);
+ dispose();
+ });
+
+ it("Setter does not subscribe to self", () => {
+ let runs = 0;
+ const [signal, setSignal] = createSignal(0);
+
+ const [setSpring, dispose] = createRoot(dispose => {
+ const [, setSpring] = createSpring(0);
+
+ createEffect(() => {
+ runs++;
+ setSpring(
+ p => {
+ signal(); // this one should be tracked
+ return p + 1;
+ },
+ { hard: true },
+ );
+ });
+
+ return [setSpring, dispose];
+ });
+ expect(runs).toBe(1);
+
+ setSpring(p => p + 1, { hard: true });
+ expect(runs).toBe(1);
+
+ setSignal(1);
+ expect(runs).toBe(2);
+
+ dispose();
+ });
+
+ it("instantly updates `number` when set with hard.", () => {
+ const start = 0;
+ const end = 50;
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toBe(start);
+ setSpring(end, { hard: true });
+
+ expect(spring()).toBe(end);
+
+ dispose();
+ });
+
+ it("instantly updates `Date` when set with hard.", () => {
+ const start = new Date("2024-04-14T00:00:00.000Z");
+ const end = new Date("2024-04-14T00:00:00.000Z");
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring().getDate()).toBe(start.getDate());
+ setSpring(end, { hard: true }); // Set to 100 here.
+
+ expect(spring().getDate()).toBe(end.getDate());
+
+ dispose();
+ });
+
+ it("instantly updates `{ progress: 1 }` when set with hard.", () => {
+ const start = { progress: 1 };
+ const end = { progress: 100 };
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toMatchObject(start);
+ setSpring(end, { hard: true }); // Set to 100 here.
+
+ expect(spring()).toMatchObject(end);
+
+ dispose();
+ });
+
+ it("instantly updates `Array` when set with hard.", () => {
+ const start = [1, 2, 3];
+ const end = [20, 15, 20];
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toMatchObject(start);
+ setSpring(end, { hard: true }); // Set to 100 here.
+
+ expect(spring()).toMatchObject(end);
+
+ dispose();
+ });
+
+ it("instantly updates `number` when set with hard using a function as an argument.", () => {
+ const start = 0;
+ const end = 50;
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toBe(start);
+ setSpring(_ => end, { hard: true }); // Using a function as an argument.
+
+ expect(spring()).toBe(end);
+
+ dispose();
+ });
+
+ it("instantly updates `{ progress: 1 }` when set with hard using a function as an argument.", () => {
+ const start = { progress: 1 };
+ const end = { progress: 100 };
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toMatchObject(start);
+ setSpring(_ => ({ progress: 100 }), { hard: true }); // Using a function as an argument.
+
+ expect(spring()).toMatchObject(end);
+
+ dispose();
+ });
+
+ it("updates toward target", () => {
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(0), d]);
+
+ expect(spring()).toBe(0);
+ setSpring(50);
+ expect(spring()).toBe(0);
+
+ _progress_time(300);
+
+ // spring() should move towards 50 but not 50 after 300ms. (This is estimated spring interpolation is hard to pinpoint exactly)
+ expect(spring()).not.toBe(50);
+ expect(spring()).toBeGreaterThan(50 / 2);
+ dispose();
+ });
+
+ it("updates array of objects toward target", () => {
+ const start = [{ foo: 1 }, { foo: 2 }, { foo: 3 }];
+ const end = [{ foo: 20 }, { foo: 15 }, { foo: 20 }];
+
+ const [[spring, setSpring], dispose] = createRoot(d => [createSpring(start), d]);
+
+ expect(spring()).toMatchObject(start);
+ setSpring(end);
+
+ _progress_time(300);
+ for (let i = 0; i < start.length; i++) {
+ expect(spring()[i]!.foo).toBeGreaterThan(end[i]!.foo / 2);
+ }
+
+ dispose();
+ });
+});
+
+describe("createDerivedSpring", () => {
+ it("updates toward accessor target", () => {
+ const [signal, setSignal] = createSignal(0);
+ const [spring, dispose] = createRoot(d => [createDerivedSpring(signal), d]);
+
+ expect(spring()).toBe(0);
+ setSignal(50); // Set to 100 here.
+ expect(spring()).toBe(0);
+
+ _progress_time(300);
+
+ // spring() should move towards 50 but not 50 after 300ms. (This is estimated spring interpolation is hard to pinpoint exactly)
+ expect(spring()).not.toBe(50);
+ expect(spring()).toBeGreaterThan(50 / 2);
+ dispose();
+ });
+});
diff --git a/packages/spring/test/server.test.ts b/packages/spring/test/server.test.ts
new file mode 100644
index 000000000..2637cca72
--- /dev/null
+++ b/packages/spring/test/server.test.ts
@@ -0,0 +1,18 @@
+import { describe, test, expect } from "vitest";
+import { createDerivedSpring, createSpring } from "../src/index.js";
+import { createSignal } from "solid-js";
+
+describe("createSpring", () => {
+ test("doesn't break in SSR", () => {
+ const [value, setValue] = createSpring({ progress: 0 });
+ expect(value().progress, "initial value should be { progress: 0 }").toBe(0);
+ });
+});
+
+describe("createDerivedSpring", () => {
+ test("doesn't break in SSR", () => {
+ const [signal, setSignal] = createSignal({ progress: 0 });
+ const value = createDerivedSpring(signal);
+ expect(value().progress, "initial value should be { progress: 0 }").toBe(0);
+ });
+});
diff --git a/packages/spring/tsconfig.json b/packages/spring/tsconfig.json
new file mode 100644
index 000000000..38c71ce71
--- /dev/null
+++ b/packages/spring/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/start/CHANGELOG.md b/packages/start/CHANGELOG.md
deleted file mode 100644
index 6e4aeb109..000000000
--- a/packages/start/CHANGELOG.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# @solid-primitives/start
-
-## 0.0.4
-
-### Patch Changes
-
-- 74db287: Correct the "homepage" field in package.json
-
-## 0.0.3
-
-### Patch Changes
-
-- d23dd74: Add type exports for cjs
-
-## 0.0.2
-
-### Patch Changes
-
-- a5f01c30: Loosen solid-start peer dependency
diff --git a/packages/start/README.md b/packages/start/README.md
deleted file mode 100644
index c08e95051..000000000
--- a/packages/start/README.md
+++ /dev/null
@@ -1,184 +0,0 @@
-
-
-
-
-# @solid-primitives/start
-
-[](https://turborepo.org/)
-[](https://bundlephobia.com/package/@solid-primitives/start)
-[](https://www.npmjs.com/package/@solid-primitives/start)
-[](https://github.com/solidjs-community/solid-primitives#contribution-process)
-
-A set of primitives for Solid Start
-
-- [`createServerCookie`](#createservercookie) - Provides a getter and setter for a reactive cookie, which works isomorphically.
-- [`createUserTheme`](#createusertheme) - Creates a Server Cookie providing a type safe way to store a theme and access it on the server or client.
-
-## Installation
-
-```bash
-npm install @solid-primitives/start
-# or
-yarn add @solid-primitives/start
-# or
-pnpm add @solid-primitives/start
-```
-
-## How to use it
-
-## `createServerCookie`
-
-A primitive for creating a cookie that can be accessed isomorphically on the client, or the server.
-
-```ts
-const [cookie, setCookie] = createServerCookie("cookieName");
-
-cookie(); // => string | undefined
-```
-
-### Custom serialization
-
-Custom cookie serializers and deserializers can also be implemented
-
-```ts
-const [serverCookie, setServerCookie] = createServerCookie("coolCookie", {
- deserialize: str => (str ? str.split(" ") : []), // Deserializes cookie into a string[]
- serialize: val => (val ? val.join(" ") : ""), // serializes the value back into a string
-});
-
-serverCookie(); // => string[]
-```
-
-## `createUserTheme`
-
-Composes `createServerCookie` to provide a type safe way to store a theme and access it on the server or client.
-
-```ts
-const [theme, setTheme] = createUserTheme("cookieName");
-
-theme(); // => "light" | "dark" | undefined
-
-// with default value
-const [theme, setTheme] = createUserTheme("cookieName", {
- defaultValue: "light",
-});
-
-theme(); // => "light" | "dark"
-```
-
-## Examples
-
-### `root.tsx`
-
-This is the main entry file for SolidStart demonstrating usage of the `createUserTheme` function.
-
-- Initializes the theme with the `createUserTheme` function.
-- Toggles between the "dark" and "light" themes using a button.
-- Sets the `data-theme` attribute adding compatibility between `createUserTheme` and libraries like [DaisyUI](https://daisyui.com/).
-- Includes a clean implementation of theming through a `Body` class.
-- Provides additional examples on using variant classes to style components.
-
-```tsx
-import { Suspense } from "solid-js";
-import {
- A,
- Body,
- ErrorBoundary,
- FileRoutes,
- Head,
- Html,
- Meta,
- Routes,
- Scripts,
- Title,
-} from "solid-start";
-import Counter from "./components/Counter";
-import "./root.css";
-import { createUserTheme } from "~/primitives/start";
-
-export default function Root() {
- const [theme, setTheme] = createUserTheme("dark-light-theme", { defaultValue: "dark" });
- const toggleTheme = () => setTheme(currentTheme => (currentTheme === "dark" ? "light" : "dark"));
-
- return (
- // Set data-theme when using libraries like Daisyui:
-
-
- SolidStart - Bare
-
-
-
- // Apply theme as a class to the Body element:
-
-
-
-
- Toggle Theme
-
-
-
-
-
- );
-}
-```
-
-### `root.css`
-
-Here is an absolute basic CSS file to demonstrate styling with the themes.
-
-- The `body` selector has general styles.
-- The `.dark` and `.light` classes provide specific styles for each theme.
-- The `.dark span` and `.light span` selectors show how to style specific elements based on the theme.
-
-```css
-body {
- font-family: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
- height: "100vh";
- width: "100vh";
-}
-
-body.dark {
- background-color: black;
- color: aliceblue;
-}
-body.light {
- background-color: aliceblue;
- color: black;
-}
-
-.dark span {
- color: #b1d4ff;
-}
-.light span {
- color: #003677;
-}
-```
-
-### `Counter.css`
-
-Continuation of the basic CSS scoped to the component using variant classes.
-
-```css
-.dark-variant {
- border: 2px solid #569cf3;
-}
-
-.light-variant {
- border: 2px solid #05ff22;
-}
-```
-
-## Demo
-
-You can view a demo of this primitive here:
-
-## Changelog
-
-See [CHANGELOG.md](./CHANGELOG.md)
diff --git a/packages/start/tsconfig.json b/packages/start/tsconfig.json
deleted file mode 100644
index 4082f16a5..000000000
--- a/packages/start/tsconfig.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": "../../tsconfig.json"
-}
diff --git a/packages/state-machine/CHANGELOG.md b/packages/state-machine/CHANGELOG.md
index 7a37a5ac2..91f8af1ce 100644
--- a/packages/state-machine/CHANGELOG.md
+++ b/packages/state-machine/CHANGELOG.md
@@ -1,5 +1,11 @@
# @solid-primitives/state-machine
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
## 0.0.3
### Patch Changes
diff --git a/packages/state-machine/README.md b/packages/state-machine/README.md
index 63318eb97..ce7bf19bd 100644
--- a/packages/state-machine/README.md
+++ b/packages/state-machine/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/state-machine
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/state-machine)
[](https://www.npmjs.com/package/@solid-primitives/state-machine)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
diff --git a/packages/state-machine/package.json b/packages/state-machine/package.json
index 41545bb0d..5c3e0f814 100644
--- a/packages/state-machine/package.json
+++ b/packages/state-machine/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/state-machine",
- "version": "0.0.3",
+ "version": "0.1.0",
"description": "A primitive for creating reactive state machines.",
"author": "Damian Tarnawski ",
"contributors": [],
@@ -33,18 +33,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
diff --git a/packages/state-machine/tsconfig.json b/packages/state-machine/tsconfig.json
index 4082f16a5..38c71ce71 100644
--- a/packages/state-machine/tsconfig.json
+++ b/packages/state-machine/tsconfig.json
@@ -1,3 +1,12 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/static-store/CHANGELOG.md b/packages/static-store/CHANGELOG.md
index a5621381c..2b2013b42 100644
--- a/packages/static-store/CHANGELOG.md
+++ b/packages/static-store/CHANGELOG.md
@@ -1,5 +1,22 @@
# @solid-primitives/static-store
+## 0.1.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/utils@6.3.0
+
+## 0.0.9
+
+### Patch Changes
+
+- 56d9511: fix: static-store setValue with setterFn has correct arguments
+
## 0.0.8
### Patch Changes
diff --git a/packages/static-store/README.md b/packages/static-store/README.md
index 0f842cbe0..e75ce5116 100644
--- a/packages/static-store/README.md
+++ b/packages/static-store/README.md
@@ -4,15 +4,14 @@
# @solid-primitives/static-store
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/static-store)
[](https://www.npmjs.com/package/@solid-primitives/static-store)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
Primitives for creating small reactive objects that doesn't change their shape over time - don't need a proxy wrapper.
-- [`createStaticStore`](#createStaticStore) - Creates a writable static store object.
-- [`createDerivedStaticStore`](#createDerivedStaticStore) - Creates a static store that is derived from a source function.
+- [`createStaticStore`](#createstaticstore) - Creates a writable static store object.
+- [`createDerivedStaticStore`](#createderivedstaticstore) - Creates a static store that is derived from a source function.
## Installation
@@ -57,7 +56,7 @@ setSize("new-property", "value");
## `createDerivedStaticStore`
-A derived version of the [`createStaticStore`](#createStaticStore). It will use the update function to derive the value of the store. It will only update when the dependencies of the update function change.
+A derived version of the [`createStaticStore`](#createstaticstore). It will use the update function to derive the value of the store. It will only update when the dependencies of the update function change.
### How to use it
@@ -82,7 +81,7 @@ el.addEventListener("resize", () => {
## `createHydratableStaticStore`
-A "hydratable" version of the [`createStaticStore`](#createStaticStore) - it will use the `serverValue` on the server and the `update` function on the client. If initialized during hydration it will use `serverValue` as the initial value and update it once hydration is complete.
+A "hydratable" version of the [`createStaticStore`](#createstaticstore) - it will use the `serverValue` on the server and the `update` function on the client. If initialized during hydration it will use `serverValue` as the initial value and update it once hydration is complete.
> **Warning** This primitive version is experimental, and mostly used internally by other primitives. It is not recommended to use it directly.
diff --git a/packages/static-store/package.json b/packages/static-store/package.json
index 03d6c14bc..b850a80e8 100644
--- a/packages/static-store/package.json
+++ b/packages/static-store/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/static-store",
- "version": "0.0.8",
+ "version": "0.1.0",
"description": "Primitives for creating small reactive objects that doesn't change their shape over time - don't need a proxy wrapper.",
"author": "Damian Tarnawski ",
"contributors": [],
@@ -32,18 +32,14 @@
"dist"
],
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"browser": {},
"exports": {
+ "@solid-primitives/source": "./src/index.ts",
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
- },
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
}
},
"typesVersions": {},
diff --git a/packages/static-store/src/index.ts b/packages/static-store/src/index.ts
index fb8b652d7..ae91fcaf0 100644
--- a/packages/static-store/src/index.ts
+++ b/packages/static-store/src/index.ts
@@ -68,7 +68,7 @@ export function createStaticStore(
const setValue = (key: keyof T, value: SetterParam): void => {
const signal = cache[key];
if (signal) return signal[1](value);
- if (key in copy) copy[key] = accessWith(value, [copy[key]]);
+ if (key in copy) copy[key] = accessWith(value, copy[key]);
};
return [
diff --git a/packages/static-store/test/index.test.ts b/packages/static-store/test/index.test.ts
index 23f179030..886dcc856 100644
--- a/packages/static-store/test/index.test.ts
+++ b/packages/static-store/test/index.test.ts
@@ -32,6 +32,9 @@ describe("createStaticStore", () => {
d: [0, 1, 2],
});
+ setState("a", prev => prev + 1);
+ expect(state.a).toBe(10);
+
createEffect(() => {
state.a;
aUpdates++;
diff --git a/packages/static-store/tsconfig.json b/packages/static-store/tsconfig.json
index 4082f16a5..dc1970e16 100644
--- a/packages/static-store/tsconfig.json
+++ b/packages/static-store/tsconfig.json
@@ -1,3 +1,16 @@
{
- "extends": "../../tsconfig.json"
-}
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "references": [
+ {
+ "path": "../utils"
+ }
+ ],
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/storage/CHANGELOG.md b/packages/storage/CHANGELOG.md
index e812ab695..d9837811a 100644
--- a/packages/storage/CHANGELOG.md
+++ b/packages/storage/CHANGELOG.md
@@ -1,5 +1,106 @@
# @solid-primitives/storage
+## 4.3.0
+
+### Minor Changes
+
+- ea09f71: Remove CJS support. The package is ESM only now.
+
+### Patch Changes
+
+- Updated dependencies [ea09f71]
+ - @solid-primitives/utils@6.3.0
+
+## 4.2.1
+
+### Patch Changes
+
+- 7e50e35: Fix `makePersisted` type narrowing
+
+## 4.2.0
+
+### Minor Changes
+
+- 89117a6: storage: expose init promise/value, resource: docs clarification
+
+## 4.1.0
+
+### Minor Changes
+
+- fd4e161: fixes utf8 escaping in cookieStorage
+
+## 4.0.0
+
+### Major Changes
+
+- 94d1bcc: storage: remove deprecated primitives, simplify types, add makeObjectStorage
+
+## 3.8.0
+
+### Minor Changes
+
+- b9d7b28: fix ssr handling
+
+## 3.7.1
+
+### Patch Changes
+
+- bebccc1: Set-Cookie header will be sent with trailing `, ` in some cases which prefixes the name in the browser too
+
+ Don't append false boolean values from `cookieOptions`
+ Stability improvements: ignore unexpected `cookieOption` keys
+
+## 3.7.0
+
+### Minor Changes
+
+- 4ec7533: storage: fix exports
+
+## 3.6.0
+
+### Minor Changes
+
+- bb4fc57: move tauri storage to submodule
+- 1382d8f: fix cookie serialization
+
+## 3.5.0
+
+### Minor Changes
+
+- 72db58b: type fixes for makePersisted
+
+## 3.4.0
+
+### Minor Changes
+
+- 5d89290: add tauriStorage
+
+## 3.3.0
+
+### Minor Changes
+
+- 99c6e09: Add back the return statement to withOptions
+
+## 3.2.0
+
+### Minor Changes
+
+- 40380a2: safe sync deserialization
+- 407a630: Bugfix for newly introduced sync api. Now BroadcastChannel for `messageSync` should also work as expected.
+- c5145af: fix withOptions
+
+## 3.1.0
+
+### Minor Changes
+
+- 713cac6: add forgotten withOptions method to cookieStorage
+
+## 3.0.0
+
+### Major Changes
+
+- bf7605c: storage: new sync API, multiplexing, cookieStorage fixes, withOptions
+
## 2.1.4
### Patch Changes
diff --git a/packages/storage/README.md b/packages/storage/README.md
index cf54fb522..11d70ed92 100644
--- a/packages/storage/README.md
+++ b/packages/storage/README.md
@@ -4,7 +4,6 @@
# @solid-primitives/storage
-[](https://turborepo.org/)
[](https://bundlephobia.com/package/@solid-primitives/storage)
[](https://www.npmjs.com/package/@solid-primitives/storage)
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
@@ -25,11 +24,11 @@ yarn add @solid-primitives/storage
`makePersisted` allows you to persist a signal or store in any synchronous or asynchronous Storage API:
```ts
-const [signal, setSignal] = makePersisted(createSignal("initial"), {storage: sessionStorage});
-const [store, setStore] = makePersisted(createStore({test: true}), {name: "testing"});
+const [signal, setSignal, init] = makePersisted(createSignal("initial"), {storage: sessionStorage});
+const [store, setStore, init] = makePersisted(createStore({test: true}), {name: "testing"});
type PersistedOptions = {
// localStorage is default
- storage?: Storage | StorageWithOptions | AsyncStorage | AsyncStorageWithOptions,
+ storage?: Storage | StorageWithOptions | AsyncStorage | AsyncStorageWithOptions | LocalForage,
// only required for storage APIs with options
storageOptions?: StorageOptions,
// key in the storage API
@@ -38,6 +37,8 @@ type PersistedOptions = {
serialize?: (value: Type) => value.toString(),
// JSON.parse is the default
deserialize?: (value: string) => Type(value),
+ // sync API (see below)
+ sync?: PersistenceSyncAPI
};
```
@@ -62,6 +63,21 @@ If you are using an asynchronous storage to persist the state of a resource, it
initialized from the storage before or after the fetcher resolved. If the initialization resolves after the fetcher, its
result is discarded not to overwrite more current data.
+### Using `makePersisted` with Suspense
+
+In case you are using an asynchronous storage and want the initialisation mesh into Suspense instead of mixing it with Show, we provide the output of the initialisation as third part of the returned tuple:
+
+```ts
+const [state, setState, init] = makePersisted(createStore({}), {
+ name: "state",
+ storage: localForage,
+});
+// run the resource so it is triggered
+createResource(() => init)[0]();
+```
+
+Now Suspense should be blocked until the initialisation is resolved.
+
### Different storage APIs
#### LocalStorage, SessionStorage
@@ -74,201 +90,187 @@ the browser.
As another storage, `cookieStorage` from this package can be used, which is a `localStorage`-like API to set cookies. It
will work in the browser and on solid-start, by parsing the `Cookie` and `Set-Cookie` header and altering
-the `Set-Cookie` header. Using it in the server without solid-start will not cause errors (unless
-you are using stackblitz), but instead emit a warning message. You can also supply your own implementations
-of `cookieStorage._read(key, options)` and `cookieStorage._write(key, value, options)` if neither of those fit your
-need.
+the `Set-Cookie` header. Using it in the server without solid-start will not cause errors, but reading and setting cookies
+will not work, unless you supply `getRequestHeaders() => Headers` and `getResponseHeaders => Headers` to the options.
-If you are not using solid-start or are using stackblitz and want to use cookieStorage on the server, you can supply
-optional `getRequest` (either something like useRequest from solid-start or a function that returns the current request)
-and `setCookie` options.
+```ts
+// for example, in express.js request and response headers can usually be accessed like this:
+const cs = cookieStorage.withOptions({
+ getRequestHeaders: () => req.headers,
+ getResponseHeaders: () => res.headers,
+});
+```
+
+> Please mind that `cookieStorage` **doesn't care** about the path and domain when reading cookies. This might cause issues
+> when using multiple cookies with the same key, but different path or domain.
+
+`cookieStorage` has been augmented with the `.withOptions` method that binds options to the other methods. This allows you
+to use predefined options for your persisted state:
+
+```ts
+const [state, setState] = makePersisted(createSignal(), {
+ storage: cookieStorage.withOptions({ expires: new Date(+new Date() + 3e10) }),
+});
+```
-When you are using vite and solid-start you want to always provide the `useRequest` function from solid start to
-the `getRequest` option, because of a technical limitation of vite.
+> HTTP headers are limited to 32kb, each header itself is limited to 16kb. So depending on your current headers, the space in `cookieStorage` is rather small. If the overall space is exceeded, subsequent requests will fail. We have no mechanism to prevent that, since we cannot infer all headers that the browser will set.
-> Please mind that `cookieStorage` **doesn't care** about the path and domain when reading cookies. This might cause issues when using
-> multiple cookies with the same key, but different path or domain.
+> Browsers do not support most UTF8 and UTF16 characters in Cookies, so `cookieStorage` encodes those characters that are not supported using `encodeURIComponent`. To save space, only those characters not supported by all Browsers will be encoded.
-#### IndexedDB, WebSQL
+#### LocalForage
-There is also [`localForage`](https://localforage.github.io/localForage/), which uses `IndexedDB`, `WebSQL`
-or `localStorage` to provide an asynchronous Storage API that can ideally store much more than the few Megabytes that
-are available in most browsers.
+LocalForage uses indexedDB or WebSQL if available to greatly increase the size of what can be stored. Just drop it in as a storage (only supported in the client):
----
+```ts
+import { isServer } from "solid-js/web";
+import { makePersisted } from "@solid-primtives/storage";
+import localforage from "localforage";
-### Deprecated primitives:
+const [state, setState] = makePersisted(createSignal(), {
+ storage: !isServer ? localforage : undefined,
+});
+```
-The previous implementation proved to be confusing and cumbersome for most people who just wanted to persist their
-signals and stores, so they are now deprecated.
+Keep in mind that it will only run on the client, so unless you have
-`createStorage` is meant to wrap any `localStorage`-like API to be as accessible as
-a [Solid Store](https://www.solidjs.com/docs/latest/api#createstore). The main differences are
+#### TauriStorage
-- that this store is persisted in whatever API is used,
-- that you can only use the topmost layer of the object and
-- that you have additional methods in an object as the third part of the returned tuple:
+[Tauri](https://tauri.app) is a lightweight run-time for desktop (and soon also mobile) applications utilizing web front-end frameworks. While it supports `localStorage` and `sessionStorage`, it also has its own store plugin with the benefit of improved stability. To use it, install the required modules both on the side of JavaScript and Rust after setting up the project with the help of their command-line interface:
-```ts
-const [store, setStore, {
- remove: (key: string) => void;
- clear: () => void;
- toJSON: () => ({[key: string]: string
-})
-;
-}]
-= createStorage({api: sessionStorage, prefix: 'my-app'});
-
-setStore('key', 'value');
-store.key; // 'value'
+```bash
+npm run tauri add store
```
-The props object support the following parameters:
+Also, it requires a few permissions in `capabilities/default.json`:
+
+```js
+{
+ // other defaults
+ "permissions": [
+ // other permissions
+ "store:allow-get",
+ "store:allow-set",
+ "store:allow-delete",
+ "store:allow-keys",
+ "store:allow-clear"
+ ]
+}
+```
-`api`
-: An array of or a single `localStorage`-like storage API; default will be `localStorage` if it exists; an empty array
-or no API will not throw an error, but only ever get `null` and not actually persist anything
+Lastly, initialize the plugin in the setup:
-`prefix`
-: A string that will be prefixed every key inside the API on set and get operations
+```rs
+fn main() {
+ tauri::Builder::default()
+ // initialize store plugin:
+ .plugin(tauri_plugin_store::Builder::new().build())
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
+```
-`serializer / deserializer`
-: A set of function to filter the input and output; the `serializer` takes an arbitrary object and returns a string,
-e.g. `JSON.stringify`, whereas the `deserializer` takes a string and returns the requested object again.
+Once these preparations are finished, `tauriStorage(name?: string)` can be used as another storage option. To fallback to localStorage if the app does not run within tauri, you can check for `window.__TAURI_INTERNALS__`:
-`options`
-: For APIs that support options as third argument in the `getItem` and `setItem` method (see helper
-type `StorageWithOptions`), you can add options they will receive on every operation.
+```ts
+import { tauriStorage } from "@solid-primitives/storage/tauri";
----
+const storage = window.__TAURI_INTERNALS__ ? tauriStorage() : localStorage;
+```
-There are a number of convenience Methods primed with common storage APIs and our own version to use cookies:
+#### Object storage
+
+This package also provides a way to create a storage API wrapper for an object called `makeObjectStorage(object)`. This is especially useful as a server fallback if you want to store the data in your user session or database object:
```ts
-createLocalStorage();
-createSessionStorage();
-createCookieStorage();
+const [state, setState] = createPersisted(createSignal(), {
+ storage: globalThis.localStorage ?? makeObjectStorage(session.userState),
+});
```
----
+#### Multiplexed storages
-#### Asynchronous storage APIs
+You may want to persist your state to multiple storages as a fallback solution (e.g. localStorage vs. cookieStorage so it
+works offline and on the server).
-In case you have APIs that persist data on the server or via `ServiceWorker` in
-a [`CookieStore`](https://wicg.github.io/cookie-store/#CookieStore), you can wrap them into an asynchronous
-storage (`AsyncStorage` or `AsyncStorageWithOptions` API) and use them with `createAsyncStorage`:
+In order to do so, you can use `multiplexStorage`:
```ts
-type CookieStoreOptions = {
- path: string;
- domain: string;
- expires: DOMTimeStamp;
- sameSite: "None" | "Lax" | "Strict"
-}
-const CookieStoreAPI: AsyncStorageWithOptions = {
- getItem: (key) => cookieStore.get(key),
- getAll: () => cookieStore.getAll(),
- setItem: (key: string, value: string, options: CookieStoreOptions = {}) => cookieStore.set({
- ...options, name, value
- }),
- removeItem: (key) => cookieStore.delete(key),
- clear: async () => {
- const all = await cookieStore.getAll();
- for (const key of all) {
- await cookieStore.delete(key);
- }
- },
- key: async (index: number) => {
- const all = await cookieStore.getAll();
- return Object.keys(all)[index];
- }
-}
-)
-;
-
-const [cookies, setCookie, {
- remove: (key: string) => void;
- clear: () => void;
- toJSON: () => ({[key: string]: string
-})
-;
-}]
-= createAsyncStorage({api: CookieStoreAPI, prefix: 'my-app', sync: false});
-
-await setStore('key', 'value');
-await store.key; // 'value'
+const [mode, setMode] = makePersisted(createSignal("dark"), {
+ name: "mode",
+ storage: multiplexStorage(localStorage, cookieStorage),
+});
```
-It works exactly like a synchronous storage, with the exception that you have to `await` every single return value. Once
-the `CookieStore` API becomes more prevalent, we will integrate support out of the box.
+If none of your storage APIs is asynchronous, the resulting API is synchronous, otherwise it is async. For the getItem
+operation, the first storage that returns a valid value will be the source of truth. So you need to await deletion and
+writing before you can rely on the result in case of asynchronous storages.
-If you cannot use `document.cookie`, you can overwrite the entry point using the following tuple:
+### Sync API
-```ts
-import {cookieStorage} from '@solid-primitives/storage';
+The storage API has an interesting functionality: if you set an item in one instance of the same page, other instances
+are notified of the change via the storage event so they can elect to automatically update.
-cookieStorage._cookies = [object
-:
-{
- [name
-:
- string
-]:
- CookieProxy
-}
-,
-name: string
-]
-;
+#### storageSync
+
+With `storageSync`, you can use exactly this API in order to sync to external updates to the same storage.
+
+```ts
+const [state, setState] = makePersisted(createSignal(), { sync: storageSync });
```
-If you need to abstract an API yourself, you can use a getter and a setter:
+#### messageSync
+
+With `messageSync`, you can recreate the same functionality for other storages within the client using either the post message API
+or broadcast channel API. If no argument is given, it is using post message, otherwise provide the broadcast channel as argument
```ts
-const CookieAbstraction = {
- get cookie() {
- return myCookieJar.toString()
- }
- set cookie(cookie) {
- const data = {};
- cookie.replace(/([^=]+)=(?:([^;]+);?)/g, (_, key, value) => {
- data[key] = value
- });
- myCookieJar.set(data);
- }
-}
-cookieStorage._cookies = [CookieAbstraction, 'cookie'];
+const [state, setState] = makePersisted(createSignal(), {
+ storage: customStorage,
+ sync: messageSync(),
+});
```
----
+#### wsSync
-`createStorageSignal` is meant for those cases when you only need to conveniently access a single value instead of full
-access to the storage API:
+With `wsSync`, you can create your synchronization API based on a web socket connection (either created yourself or by our
+`@solid-primitives/websocket` package); this allows synchronization between client and server.
```ts
-const [value, setValue] = createStorageSignal("value", { api: cookieStorage });
+const [state, setState] = makePersisted(createSignal(), { sync: wsSync(makeWs(...)) });
+```
-setValue("value");
-value(); // 'value'
+#### multiplexSync
+
+You can also multiplex different synchronization APIs using multiplexSync:
+
+```ts
+const [state, setState] = makePersisted(createSignal(), {
+ sync: multiplexSync(storageSync, wsSync(ws)),
+});
```
-As a convenient additional method, you can also use `createCookieStorageSignal(key, initialValue, options)`.
+#### Custom synchronization API
----
+If you want to create your own sync API, you can use the following pattern:
-### Options
+```ts
+export type PersistenceSyncData = {
+ key: string;
+ newValue: string | null | undefined;
+ timeStamp: number;
+ url?: string;
+};
-The properties of your `createStorage`/`createAsyncStorage`/`createStorageSignal` props are:
+export type PersistenceSyncCallback = (data: PersistenceSyncData) => void;
-- `api`: the (synchronous or
- asynchronous) [Storage-like API](https://developer.mozilla.org/de/docs/Web/API/Web_Storage_API), default
- is `localStorage`
-- `deserializer` (optional): a `deserializer` or parser for the stored data
-- `serializer` (optional): a `serializer` or string converter for the stored data
-- `options` (optional): default options for the set-call of Storage-like API, if supported
-- `prefix` (optional): a prefix for the Storage keys
-- `sync` (optional): if set to
- false, [event synchronization](https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent) is disabled
+export type PersistenceSyncAPI = [
+ /** subscribes to sync */
+ subscribe: (subscriber: PersistenceSyncCallback) => void,
+ update: (key: string, value: string | null | undefined) => void,
+];
+```
+
+You can use APIs like Pusher or a WebRTC data connection to synchronize your state.
### Tools
@@ -278,6 +280,14 @@ If you want to build your own Storage and don't want to do a `.clear()` method y
const storageWithClearMethod = addClearMethod(storage_without_clear_method);
```
+If your storage API supports options and you want to add predefined options so it behaves like an API without options,
+you can add a `.withOptions` method:
+
+```ts
+const customStorage = addWithOptionsMethod(storage_supporting_options);
+const boundCustomStorage = customStorage.withOptions(myOptions);
+```
+
## Demo
[Live Demo](https://primitives.solidjs.community/playground/storage) - [Sources](https://github.com/solidjs-community/solid-primitives/tree/main/packages/storage/dev)
diff --git a/packages/storage/package.json b/packages/storage/package.json
index 8b9f23262..b3473f89d 100644
--- a/packages/storage/package.json
+++ b/packages/storage/package.json
@@ -1,6 +1,6 @@
{
"name": "@solid-primitives/storage",
- "version": "2.1.4",
+ "version": "4.3.0",
"description": "Primitive that provides reactive wrappers for storage access",
"author": "Alex Lohr ",
"contributors": [
@@ -16,14 +16,17 @@
"name": "storage",
"stage": 3,
"list": [
- "createStorage",
- "createCookieStorage",
- "createAsyncStorage",
- "createStorageSignal",
- "createLocalStorage",
- "createSessionStorage",
+ "makePersisted",
"cookieStorage",
- "makePersisted"
+ "tauriStorage",
+ "multiplexStorage",
+ "storageSync",
+ "messageSync",
+ "wsSync",
+ "multiplexSync",
+ "addClearMethod",
+ "addWithOptionsMethod",
+ "makeObjectStorage"
],
"category": "Browser APIs"
},
@@ -33,18 +36,29 @@
"private": false,
"sideEffects": false,
"type": "module",
- "main": "./dist/index.cjs",
"module": "./dist/index.js",
- "browser": {},
"types": "./dist/index.d.ts",
+ "browser": {},
"exports": {
- "import": {
- "types": "./dist/index.d.ts",
- "default": "./dist/index.js"
+ "@solid-primitives/source": "./src/index.ts",
+ ".": {
+ "import": {
+ "types": "./dist/index.d.ts",
+ "default": "./dist/index.js"
+ }
},
- "require": {
- "types": "./dist/index.d.cts",
- "default": "./dist/index.cjs"
+ "./tauri": {
+ "import": {
+ "types": "./dist/tauri.d.ts",
+ "default": "./dist/tauri.js"
+ }
+ }
+ },
+ "typesVersions": {
+ "*": {
+ "tauri": [
+ "./dist/tauri.d.ts"
+ ]
}
},
"scripts": {
@@ -65,17 +79,18 @@
"@solid-primitives/utils": "workspace:^"
},
"peerDependencies": {
- "solid-js": "^1.6.12",
- "solid-start": ">=0.2.26"
+ "@tauri-apps/plugin-store": "*",
+ "solid-js": "^1.6.12"
},
"peerDependenciesMeta": {
"solid-start": {
"optional": true
+ },
+ "@tauri-apps/plugin-store": {
+ "optional": true
}
},
- "typesVersions": {},
"devDependencies": {
- "solid-js": "^1.8.7",
- "solid-start": "^0.3.10"
+ "solid-js": "^1.8.15"
}
}
diff --git a/packages/storage/src/cookies.ts b/packages/storage/src/cookies.ts
index 609944353..fc4a0ea8c 100644
--- a/packages/storage/src/cookies.ts
+++ b/packages/storage/src/cookies.ts
@@ -1,15 +1,15 @@
-import { isServer } from "solid-js/web";
-import { StorageProps, StorageSignalProps, StorageWithOptions } from "./types.js";
-import { addClearMethod } from "./tools.js";
-import { createStorage, createStorageSignal } from "./storage.js";
-import type { PageEvent } from "solid-start";
+import { getRequestEvent, isServer, type RequestEvent } from "solid-js/web";
+import { SyncStorageWithOptions } from "./index.js";
+import { addWithOptionsMethod, addClearMethod } from "./tools.js";
-export type CookieOptions = CookieProperties & {
- getRequest?: (() => Request) | (() => PageEvent);
- setCookie?: (key: string, value: string, options: CookieOptions) => void;
-};
+export type CookieOptions =
+ | (CookieProperties & {
+ getRequestHeaders?: () => Headers;
+ getResponseHeaders?: () => Headers;
+ })
+ | undefined;
-type CookieProperties = {
+type CookiePropertyTypes = {
domain?: string;
expires?: Date | number | String;
path?: string;
@@ -19,53 +19,49 @@ type CookieProperties = {
sameSite?: "None" | "Lax" | "Strict";
};
-const cookiePropertyKeys = [
- "domain",
- "expires",
- "path",
- "secure",
- "httpOnly",
- "maxAge",
- "sameSite",
-] as const;
+type CookieProperties = {
+ [key in keyof CookiePropertyTypes]: CookiePropertyTypes[key];
+};
+
+const cookiePropertyMap = {
+ domain: "Domain",
+ expires: "Expires",
+ path: "Path",
+ secure: "Secure",
+ httpOnly: "HttpOnly",
+ maxAge: "Max-Age",
+ sameSite: "SameSite",
+} as const;
function serializeCookieOptions(options?: CookieOptions) {
- if (!options) {
- return "";
- }
- let memo = "";
- for (const key in options) {
- if (!cookiePropertyKeys.includes(key as keyof CookieProperties)) continue;
+ if (!options) return "";
+ const result = Object.entries(options)
+ .map(([key, value]) => {
+ const serializedKey: string | undefined = cookiePropertyMap[key as keyof CookiePropertyTypes];
+ if (!serializedKey) return undefined;
+
+ if (value instanceof Date) return `${serializedKey}=${value.toUTCString()}`;
+ if (typeof value === "boolean") return value ? `${serializedKey}` : undefined;
+ return `${serializedKey}=${value}`;
+ })
+ .filter(v => !!v);
- const value = options[key as keyof CookieProperties];
- memo +=
- value instanceof Date
- ? `; ${key}=${value.toUTCString()}`
- : typeof value === "boolean"
- ? `; ${key}`
- : `; ${key}=${value}`;
- }
- return memo;
+ return result.length != 0 ? `; ${result.join("; ")}` : "";
}
function deserializeCookieOptions(cookie: string, key: string) {
- return cookie.match(`(^|;)\\s*${key}\\s*=\\s*([^;]+)`)?.pop() ?? null;
+ const found = cookie.match(`(^|;)\\s*${key}\\s*=\\s*([^;]+)`)?.pop();
+ return found != null ? decodeURIComponent(found) : null;
}
-let useRequest: () => PageEvent | undefined;
-try {
- useRequest = require("solid-start/server").useRequest;
-} catch (e) {
- useRequest = () => {
- // eslint-disable-next-line no-console
- console.warn(
- "It seems you attempt to use cookieStorage on the server without having solid-start installed or use vite.",
- );
- return {
- request: { headers: { get: () => "" } } as unknown as Request,
- } as unknown as PageEvent;
- };
-}
+const getRequestHeaders = isServer
+ ? () => getRequestEvent()?.request.headers || new Headers()
+ : () => new Headers();
+const getResponseHeaders = isServer
+ ? () =>
+ (getRequestEvent() as (RequestEvent & { response: Response }) | undefined)?.response
+ .headers || new Headers()
+ : () => new Headers();
/**
* handle cookies exactly like you would handle localStorage
@@ -86,112 +82,79 @@ try {
* ```
* Also, you can use its _read and _write properties to change reading and writing
*/
-export const cookieStorage: StorageWithOptions = addClearMethod({
- _read: isServer
- ? (options?: CookieOptions) => {
- const eventOrRequest = options?.getRequest?.() || useRequest();
- const request =
- eventOrRequest && ("request" in eventOrRequest ? eventOrRequest.request : eventOrRequest);
- let result = "";
- if (eventOrRequest.responseHeaders) {
- // Check if we really got a pageEvent
- const responseHeaders = eventOrRequest.responseHeaders as Headers;
- result +=
+export const cookieStorage: SyncStorageWithOptions = addWithOptionsMethod(
+ addClearMethod({
+ _read: isServer
+ ? (options?: CookieOptions) => {
+ const requestCookies = (options?.getRequestHeaders?.() || getRequestHeaders()).get(
+ "Cookie",
+ );
+ const responseCookies = (options?.getResponseHeaders?.() || getResponseHeaders()).get(
+ "Set-Cookie",
+ );
+ const cookies: Record = {};
+ const addCookie = (_: string, key: string, val: string) => (cookies[key] = val);
+ requestCookies?.replace(/(?:^|;)([^=]+)=([^;]+)/g, addCookie);
+ responseCookies?.replace(/(?:^|, )([^=]+)=([^;]+)/g, addCookie);
+ return Object.entries(cookies)
+ .map(keyval => keyval.join("="))
+ .join("; ");
+ }
+ : () => document.cookie,
+ _write: isServer
+ ? (key: string, value: string, options?: CookieOptions) => {
+ const responseHeaders = getResponseHeaders();
+ const currentCookies =
responseHeaders
.get("Set-Cookie")
- ?.split(",")
- .map(cookie => !cookie.match(/\\w*\\s*=\\s*[^;]+/))
- .join(";") ?? "";
+ ?.split(", ")
+ .filter(cookie => cookie && !cookie.startsWith(`${key}=`)) ?? [];
+ responseHeaders.set(
+ "Set-Cookie",
+ [...currentCookies, `${key}=${value}${serializeCookieOptions(options)}`].join(", "),
+ );
}
- return `${result};${request?.headers?.get("Cookie") ?? ""}`; // because first cookie will be preferred we don't have to worry about duplicates
- }
- : () => document.cookie,
- _write: isServer
- ? (key: string, value: string, options?: CookieOptions) => {
- if (options?.setCookie) {
- options?.setCookie?.(key, value, options);
- return;
- }
- const pageEvent: PageEvent = options?.getRequest?.() || useRequest();
- if (!pageEvent.responseHeaders)
- // Check if we really got a pageEvent
- return;
- const responseHeaders = pageEvent.responseHeaders as Headers;
- const cookies =
- responseHeaders
- .get("Set-Cookie")
- ?.split(",")
- .filter(cookie => !cookie.match(`\\s*${key}\\s*=`)) ?? [];
- cookies.push(`${key}=${value}${serializeCookieOptions(options)}`);
- responseHeaders.set("Set-Cookie", cookies.join(","));
- }
- : (key: string, value: string, options?: CookieOptions) => {
- document.cookie = `${key}=${value}${serializeCookieOptions(options)}`;
- },
- getItem: (key: string, options?: CookieOptions) =>
- deserializeCookieOptions(cookieStorage._read(options), key),
- setItem: (key: string, value: string, options?: CookieOptions) => {
- const oldValue = isServer ? cookieStorage.getItem(key, options) : null;
- cookieStorage._write(key, value, options);
- if (!isServer) {
- // Storage events are only required on client when using multiple tabs
- const storageEvent = Object.assign(new Event("storage"), {
+ : (key: string, value: string, options?: CookieOptions) => {
+ document.cookie = `${key}=${value}${serializeCookieOptions(options)}`;
+ },
+ getItem: (key: string, options?: CookieOptions) =>
+ deserializeCookieOptions(cookieStorage._read(options), key),
+ setItem: (key: string, value: string, options?: CookieOptions) => {
+ cookieStorage._write(
key,
- oldValue,
- newValue: value,
- url: globalThis.document.URL,
- storageArea: cookieStorage,
+ value.replace(/[\u00c0-\uffff\&;]/g, c => encodeURIComponent(c)),
+ options,
+ );
+ },
+ removeItem: (key: string, options?: CookieOptions) => {
+ cookieStorage._write(key, "deleted", {
+ ...options,
+ expires: new Date(0),
});
- window.dispatchEvent(storageEvent);
- }
- },
- removeItem: (key: string, options?: CookieOptions) => {
- cookieStorage._write(key, "deleted", { ...options, expires: new Date(0) });
- },
- key: (index: number, options?: CookieOptions) => {
- let key: string | null = null;
- let count = 0;
- cookieStorage
- ._read(options)
- .replace(/(?:^|;)\s*(.+?)\s*=\s*[^;]+/g, (_: string, found: string) => {
- if (!key && found && count++ === index) {
- key = found;
- }
+ },
+ key: (index: number, options?: CookieOptions) => {
+ let key: string | null = null;
+ let count = 0;
+ cookieStorage
+ ._read(options)
+ .replace(/(?:^|;)\s*(.+?)\s*=\s*[^;]+/g, (_: string, found: string) => {
+ if (!key && found && count++ === index) {
+ key = found;
+ }
+ return "";
+ });
+ return key;
+ },
+ getLength: (options?: CookieOptions) => {
+ let length = 0;
+ cookieStorage._read(options).replace(/(?:^|;)\s*.+?\s*=\s*[^;]+/g, (found: string) => {
+ length += found ? 1 : 0;
return "";
});
- return key;
- },
- getLength: (options?: CookieOptions) => {
- let length = 0;
- cookieStorage._read(options).replace(/(?:^|;)\s*.+?\s*=\s*[^;]+/g, (found: string) => {
- length += found ? 1 : 0;
- return "";
- });
- return length;
- },
- get length() {
- return this.getLength();
- },
-});
-
-/**
- * creates a reactive store but bound to document.cookie
- * @deprecated in favor of makePersisted
- */
-export const createCookieStorage = >(
- props?: Omit, "api">,
-) => createStorage({ ...props, api: cookieStorage } as any);
-
-/**
- * creates a reactive signal, but bound to document.cookie
- * @deprecated in favor of makePersisted
- */
-export const createCookieStorageSignal = <
- T,
- O = CookieOptions,
- A = StorageWithOptions,
->(
- key: string,
- initialValue?: T,
- props?: Omit, "api">,
-) => createStorageSignal(key, initialValue, { ...props, api: cookieStorage } as any);
+ return length;
+ },
+ get length() {
+ return this.getLength();
+ },
+ }),
+);
diff --git a/packages/storage/src/index.ts b/packages/storage/src/index.ts
index 62e11225e..31d2d3094 100644
--- a/packages/storage/src/index.ts
+++ b/packages/storage/src/index.ts
@@ -1,39 +1,43 @@
-export type {
- StorageWithOptions,
- StorageDeserializer,
- StorageSerializer,
- StringStorageProps,
- AnyStorageProps,
- StorageProps,
- StorageObject,
- StorageSetter,
- AsyncStorage,
- AsyncStorageWithOptions,
- AsyncStorageObject,
- AsyncStorageSetter,
- StorageSignalProps,
-} from "./types.js";
-
+import { type CookieOptions, cookieStorage } from "./cookies.js";
import {
- createStorage,
- createAsyncStorage,
- createStorageSignal,
- createLocalStorage,
- createSessionStorage,
-} from "./storage.js";
-import { CookieOptions, cookieStorage, createCookieStorage } from "./cookies.js";
-import { addClearMethod } from "./tools.js";
-import { PersistenceOptions, makePersisted } from "./persisted.js";
+ addClearMethod,
+ addWithOptionsMethod,
+ multiplexStorage,
+ makeObjectStorage,
+} from "./tools.js";
+import {
+ type SyncStorage,
+ type SyncStorageWithOptions,
+ type AsyncStorage,
+ type AsyncStorageWithOptions,
+ type PersistenceOptions,
+ type PersistenceSyncAPI,
+ type PersistenceSyncData,
+ type PersistenceSyncCallback,
+ makePersisted,
+ multiplexSync,
+ storageSync,
+ messageSync,
+ wsSync,
+} from "./persisted.js";
export {
- createStorage,
- createAsyncStorage,
- createStorageSignal,
- createLocalStorage,
- createCookieStorage,
- createSessionStorage,
type CookieOptions,
cookieStorage,
addClearMethod,
+ addWithOptionsMethod,
+ multiplexStorage,
+ makeObjectStorage,
+ type SyncStorage,
+ type SyncStorageWithOptions,
+ type AsyncStorage,
+ type AsyncStorageWithOptions,
type PersistenceOptions,
+ type PersistenceSyncCallback,
+ type PersistenceSyncData,
+ type PersistenceSyncAPI,
makePersisted,
+ multiplexSync,
+ storageSync,
+ wsSync,
+ messageSync,
};
diff --git a/packages/storage/src/persisted.ts b/packages/storage/src/persisted.ts
index 788880f7f..a8f26a1ba 100644
--- a/packages/storage/src/persisted.ts
+++ b/packages/storage/src/persisted.ts
@@ -1,123 +1,86 @@
import type { Accessor, Setter, Signal } from "solid-js";
import { createUniqueId, untrack } from "solid-js";
+import { isServer, isDev } from "solid-js/web";
import type { SetStoreFunction, Store } from "solid-js/store";
import { reconcile } from "solid-js/store";
-import type { AsyncStorage, AsyncStorageWithOptions, StorageWithOptions } from "./types.js";
-export type PersistenceBaseOptions = {
- name?: string;
- serialize?: (data: T) => string;
- deserialize?: (data: string) => T;
+export type SyncStorage = {
+ getItem: (key: string) => string | null;
+ setItem: (key: string, value: string) => void;
+ removeItem: (key: string) => void;
+ [key: string]: any;
+};
+export type AsyncStorage = {
+ getItem: (key: string) => Promise;
+ setItem: (key: string, value: string) => Promise;
+ removeItem: (key: string) => Promise;
+ [key: string]: any;
};
+export type SyncStorageWithOptions = undefined extends O
+ ? {
+ getItem: (key: string, options?: O) => string | null;
+ setItem: (key: string, value: string, options?: O) => void;
+ removeItem: (key: string, options?: O) => void;
+ [key: string]: any;
+ }
+ : {
+ getItem: (key: string, options: O) => string | null;
+ setItem: (key: string, value: string, options: O) => void;
+ removeItem: (key: string, options: O) => void;
+ [key: string]: any;
+ };
+export type AsyncStorageWithOptions = undefined extends O
+ ? {
+ getItem: (key: string, options?: O) => Promise;
+ setItem: (key: string, value: string, options?: O) => Promise;
+ removeItem: (key: string, options?: O) => Promise;
+ [key: string]: any;
+ }
+ : {
+ getItem: (key: string, options: O) => Promise;
+ setItem: (key: string, value: string, options: O) => Promise;
+ removeItem: (key: string, options: O) => Promise;
+ [key: string]: any;
+ };
-export type PersistenceOptions> = PersistenceBaseOptions &
- (
- | {
- storage: StorageWithOptions | AsyncStorageWithOptions;
- storageOptions: O;
- }
- | { storage?: Storage | AsyncStorage }
- );
+export type PersistenceSyncData = {
+ key: string;
+ newValue: string | null | undefined;
+ timeStamp: number;
+ url?: string;
+};
-/**
- * Persists a signal, store or similar API
- * ```ts
- * const [getter, setter] = makePersisted(createSignal("data"), options);
- * const options = {
- * storage: cookieStorage, // can be any synchronous or asynchronous storage
- * storageOptions: { ... }, // for storages with options, otherwise not needed
- * name: "solid-data", // optional
- * serialize: (value: string) => value, // optional
- * deserialize: (data: string) => data, // optional
- * };
- * ```
- * Can be used with `createSignal` or `createStore`. The initial value from the storage will overwrite the initial
- * value of the signal or store unless overwritten. Overwriting a signal with `null` or `undefined` will remove the
- * item from the storage.
- *
- * @param {Signal | [get: Store, set: SetStoreFunction]} signal - The signal or store to be persisted.
- * @param {PersistenceOptions} options - The options for persistence.
- * @returns {Signal | [get: Store, set: SetStoreFunction]} - The persisted signal or store.
- */
-export function makePersisted(
- signal: [Accessor, Setter],
- options?: PersistenceOptions,
-): [Accessor, Setter];
+export type PersistenceSyncCallback = (data: PersistenceSyncData) => void;
-/**
- * Persists a signal, store or similar API
- * ```ts
- * const [getter, setter] = makePersisted(createSignal("data"), options);
- * const options = {
- * storage: cookieStorage, // can be any synchronous or asynchronous storage
- * storageOptions: { ... }, // for storages with options, otherwise not needed
- * name: "solid-data", // optional
- * serialize: (value: string) => value, // optional
- * deserialize: (data: string) => data, // optional
- * };
- * ```
- * Can be used with `createSignal` or `createStore`. The initial value from the storage will overwrite the initial
- * value of the signal or store unless overwritten. Overwriting a signal with `null` or `undefined` will remove the
- * item from the storage.
- *
- * @param {Signal | [get: Store