svelte-persistent-runes 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ # The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 [MacFJA](https://github.com/MacFJA)
4
+ Copyright (c) 2025 [andrescera](https://github.com/andrescera)
5
+
6
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ > of this software and associated documentation files (the "Software"), to deal
8
+ > in the Software without restriction, including without limitation the rights
9
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ > copies of the Software, and to permit persons to whom the Software is
11
+ > furnished to do so, subject to the following conditions:
12
+ >
13
+ > The above copyright notice and this permission notice shall be included in
14
+ > all copies or substantial portions of the Software.
15
+ >
16
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ > THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ # Svelte persistent runes
2
+
3
+ A Svelte reactive rune that keep its value through pages and reloads
4
+
5
+ ![GitHub Repo stars](https://img.shields.io/github/stars/andrescera/svelte-persistent-runes?style=social)
6
+ ![NPM bundle size](https://img.shields.io/bundlephobia/minzip/svelte-persistent-runes)
7
+ ![Download per week](https://img.shields.io/npm/dw/svelte-persistent-runes)
8
+ ![License](https://img.shields.io/npm/l/svelte-persistent-runes)
9
+ ![NPM version](https://img.shields.io/npm/v/svelte-persistent-runes)
10
+ [![Checked with Biome](https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev)
11
+
12
+ ## Installation
13
+
14
+ With [NPM](https://www.npmjs.com/package/svelte-persistent-runes)
15
+ ```sh
16
+ npm install --save-dev svelte-persistent-runes
17
+ # or
18
+ yarn add --save-dev svelte-persistent-runes
19
+ # or
20
+ pnpm add --save-dev svelte-persistent-runes
21
+ # or
22
+ deno install --dev npm:svelte-persistent-runes
23
+ ```
24
+
25
+ ## Quickstart
26
+
27
+ Update your `./svelte.config.js` to add a new preprocessor:
28
+ ```diff
29
+ import adapter from '@sveltejs/adapter-auto';
30
+ +import persist from "svelte-persistent-runes/preprocessor"
31
+ const config = {
32
+ + preprocess: [persist()],
33
+ kit: {
34
+ adapter: adapter()
35
+ }
36
+ };
37
+ export default config;
38
+ ```
39
+
40
+ Replace your `$state` with `$persist`:
41
+ ```diff
42
+ <script>
43
+ +import "svelte-persistent-runes"
44
+ -let count = $state(0);
45
+ +let count = $persist(0, 'counter');
46
+ </script>
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ This library have 2 parts:
52
+ - A preprocessor to add the `$persist` rune (and optionally a Vite plugin).
53
+ - A set of configuration to persist your data.
54
+
55
+ You MUST add the preprocessor to use `$persist`.
56
+ It's as simple as to add it in your Svelte configuration (`svelte.config.js`) with the import of `svelte-persistent-runes/plugins`
57
+
58
+ <details>
59
+ <summary>./svelte.config.js</summary>
60
+
61
+ ```js
62
+ import adapter from '@sveltejs/adapter-auto';
63
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
64
+ import { persistPreprocessor } from "svelte-persistent-runes/plugins"
65
+
66
+ /** @type {import('@sveltejs/kit').Config} */
67
+ const config = {
68
+ preprocess: [vitePreprocess(), persistPreprocessor()],
69
+ kit: {
70
+ adapter: adapter()
71
+ }
72
+ };
73
+
74
+ export default config;
75
+ ```
76
+
77
+ </details>
78
+
79
+ > [!IMPORTANT]
80
+ > If you are using `*.svelte.js`/`*.svelte.ts` file you need to also add a Vite plugin:
81
+ > <details><summary>./vite.config.ts</summary>
82
+ >
83
+ > ```ts
84
+ > import { sveltekit } from '@sveltejs/kit/vite';
85
+ > import { defineConfig } from 'vite';
86
+ > import { persistPlugin as persist } from "svelte-persistent-runes/plugins";
87
+ >
88
+ > export default defineConfig({
89
+ > plugins: [persist(), sveltekit()]
90
+ > });
91
+ > ```
92
+ >
93
+ > </details>
94
+
95
+ Now that the preprocessor is added, you can use the `$persist` rune instead of the `$state` rune.
96
+
97
+ <details>
98
+ <summary>./src/anywhere/component.svelte</summary>
99
+
100
+ ```html
101
+ <script>
102
+ import "svelte-persistent-runes"
103
+ let count = $persist(0, 'counter');
104
+ </script>
105
+ <div class="counter">
106
+ <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
107
+ <div><strong>{count}</strong></div>
108
+ <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
109
+ </div>
110
+ ```
111
+
112
+ </details>
113
+
114
+ <details>
115
+ <summary>./src/anywhere/data.svelte.ts</summary>
116
+
117
+ ```ts
118
+ import "svelte-persistent-runes"
119
+ export class Person {
120
+ name = $persist('John', 'user-name')
121
+ age = $persist(33, 'user-age')
122
+
123
+ greet(): string {
124
+ return `Hello ${this.name}`;
125
+ }
126
+ birthday(): string {
127
+ this.age += 1;
128
+ return `Happy birthday ${this.name}!`
129
+ }
130
+ }
131
+ export const currentUser = new Person()
132
+ ```
133
+
134
+ </details>
135
+
136
+ > [!IMPORTANT]
137
+ > You need to import `import "svelte-persistent-runes"` to prevent Typescript to complain about the unknown function `$persist`
138
+ >
139
+ > ---
140
+ >
141
+ > You can add this import in an ambient Typescript Module (like `./src/app.d.ts` in SvelteKit), and you won't need to import it in every file
142
+
143
+ ### Definition
144
+
145
+ ```ts
146
+ type PersistentRunesOptions = {
147
+ /**
148
+ * Convert the source data into its string representation
149
+ * @param input The source data
150
+ * @return The string representation of data
151
+ */
152
+ serialize<T>(input: T): string;
153
+ /**
154
+ * Convert back the string representation into the source data
155
+ * @param input The string representation of the date
156
+ * @return The new data based on its string representation
157
+ */
158
+ deserialize<T>(input: string): T;
159
+ /**
160
+ * Write data into the store
161
+ * @param key The storage key to write
162
+ * @param value The data to write
163
+ */
164
+ storageWrite(key: string, value: string): void;
165
+ /**
166
+ * Read data from the storage
167
+ * @param key The storage key to read
168
+ * @returns The data or `undefined` if the data don't exist in the storage
169
+ */
170
+ storageRead(key: string): string | undefined;
171
+ };
172
+
173
+ /**
174
+ * A reactive state, that can restore its state upon page reload
175
+ * @param initial The initial value of the state
176
+ * @param key The storage key of the state. Must be unique in your application
177
+ * @param options The persistence options (how and where)
178
+ */
179
+ declare function $persist<T>(initial: T, key: string, options?: Partial<PersistentRunesOption>)
180
+ ```
181
+
182
+ ### Options
183
+
184
+ You can customize how and where the state value is persisted.
185
+
186
+ The `$persist` runes take a third (and optional) parameter of type `PersistentRunesOption`.
187
+
188
+ The options consist of 2 main part: the serializer and the storage. It can be defined as a plain object or as the result of the `buildOptions` (imported from `svelte-persistent-runes/options`)
189
+
190
+ ```ts
191
+ /**
192
+ * Create a `PersistentRunesOptions` from a serializer and a storage
193
+ * @param serializer The serializer to use (if `undefined` then `JsonSerializer` will be used)
194
+ * @param storage The storage to use (if `undefined` then `BrowserLocalStorage` will be used)
195
+ */
196
+ declare function buildOptions(
197
+ serializer: PersistentRunesSerializer | undefined,
198
+ storage: PersistentRunesStorage | undefined
199
+ ): PersistentRunesOptions;
200
+ ```
201
+
202
+
203
+ #### The serializer
204
+
205
+ The serializer part of the option are:
206
+ - `serialize`: This function is responsible for converting the original type into a string
207
+ - `deserialize`: This function is responsible to convert back a string to the original type
208
+
209
+ The library have several built-in serializer:
210
+ - `JsonSerializerFactory`: factory to create a JSON based serializer
211
+ - `JsonSerializer`: A basic JSON serializer (no replacer, nor reviver)
212
+ - `DevalueSerializerFactory`: factory to create a [Devalue] based serializer
213
+ - `DevalueSerializer`: A basic [Devalue] serializer (no reducers, nor revivers)
214
+ - `ESSerializerSerializerFactory`: factory to create a [ESSerializer] based serializer
215
+ - `ESSerializerSerializer`: A basic [ESSerializer] serializer (no SerializeOptions, nor classes)
216
+ - `MacfjaSerializerFactory`: factory to create a [@macfja/serializer] based serializer
217
+ - `MacfjaSerializer`: A basic [@macfja/serializer] serializer (no additional classes mapping)
218
+ - `SuperJsonSerializer`: A [superjson] serializer
219
+ - `NextJsonSerializerFactory`: factory to create a [next-json] based serializer
220
+ - `NextJsonSerializerFactory`: A basic [next-json] serializer (no options, nor replacers, nor revivers)
221
+ - `PhpSerializeSerializerFactory`: factory to create a [php-serialize] based serializer
222
+ - `PhpSerializeSerializer`: A basic [php-serialize] serializer (no options)
223
+ - `SerializeAnythingSerializerFactory`: factory to create a [serialize-anything] based serializer
224
+ - `SerializeAnythingSerializer`: A basic [serialize-anything] serializer (no options)
225
+
226
+ [ESSerializer]: https://www.npmjs.com/package/esserializer
227
+ [Devalue]: https://www.npmjs.com/package/devalue
228
+ [@macfja/serializer]: https://www.npmjs.com/package/@macfja/serializer
229
+ [superjson]: https://www.npmjs.com/package/superjson
230
+ [next-json]: https://www.npmjs.com/package/next-json
231
+ [php-serialize]: https://www.npmjs.com/package/php-serialize
232
+ [serialize-anything]: https://www.npmjs.com/package/serialize-anything
233
+
234
+ #### The storage
235
+
236
+ The storage part of the option are:
237
+ - `storageWrite`: This function is responsible to write data into the storage
238
+ - `storageRead`: This function is responsible to read data from the storage
239
+
240
+ The library have several built-in storage:
241
+ - `BrowserCookieStorageFactory`: factory to create a Cookie based storage (DOM API, browser only)
242
+ - `BrowserCookieStorage`: A basic Cookie storage (no particular options, except for `samesite: Strict`)
243
+ - `BrowserLocalStorage`: a browser localStorage storage ([DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Storage), browser only)
244
+ - `BrowserSessionStorage`: a browser sessionStorage storage ([DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Storage), browser only)
245
+ - `addEncryptionStorage`: a wrapper function to add AES [GCM encryption] on stored data
246
+
247
+ [GCM encryption]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
248
+
249
+ #### Example
250
+
251
+ <details>
252
+
253
+ <summary>Browser session storage and @macfja/serializer</summary>
254
+
255
+ ```html
256
+ <script>
257
+ import "svelte-persistent-runes"
258
+ import {
259
+ buildOptions,
260
+ MacfjaSerializer,
261
+ BrowserSessionStorage
262
+ } from "svelte-persistent-runes/options"
263
+ let count = $persist(0, 'counter', buildOptions(MacfjaSerializer, BrowserSessionStorage));
264
+ </script>
265
+ <div class="counter">
266
+ <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
267
+ <div><strong>{count}</strong></div>
268
+ <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
269
+ </div>
270
+ ```
271
+
272
+ </details>
273
+
274
+ <details>
275
+
276
+ <summary>Browser local storage, encrypted and SuperJson </summary>
277
+
278
+ ```html
279
+ <script>
280
+ import "svelte-persistent-runes"
281
+ import {
282
+ buildOptions,
283
+ SuperJsonSerializer,
284
+ BrowserLocalStorage,
285
+ addEncryptionStorage
286
+ } from "svelte-persistent-runes/options"
287
+ let count = $persist(0, 'counter', buildOptions(
288
+ SuperJsonSerializer,
289
+ addEncryptionStorage(BrowserLocalStorage, '12345678901234567890123456879012')
290
+ ));
291
+ </script>
292
+ <div class="counter">
293
+ <button onclick={() => (count -= 1)} aria-label="Decrease the counter by one">-</button>
294
+ <div><strong>{count}</strong></div>
295
+ <button onclick={() => (count += 1)} aria-label="Increase the counter by one">+</button>
296
+ </div>
297
+ ```
298
+
299
+ </details>
@@ -0,0 +1,40 @@
1
+ type PersistentRunesOptions = {
2
+ /**
3
+ * Convert the source data into its string representation
4
+ * @param input The source data
5
+ * @return The string representation of data
6
+ */
7
+ serialize<T>(input: T): string;
8
+ /**
9
+ * Convert back the string representation into the source data
10
+ * @param input The string representation of the date
11
+ * @return The new data based on its string representation
12
+ */
13
+ deserialize<T>(input: string): T;
14
+ /**
15
+ * Write data into the store
16
+ * @param key The storage key to write
17
+ * @param value The data to write
18
+ */
19
+ storageWrite(key: string, value: string): void;
20
+ /**
21
+ * Read data from the storage
22
+ * @param key The storage key to read
23
+ * @returns The data or `undefined` if the data don't exist in the storage
24
+ */
25
+ storageRead(key: string): string | undefined;
26
+ };
27
+ declare global {
28
+ /**
29
+ * A reactive state, that can restore its state upon page reload
30
+ * @param initial The initial value of the state
31
+ * @param key The storage key of the state. Must be unique in your application
32
+ * @param options The persistence options (how and where)
33
+ */
34
+ export function $persist<T>(initial: T, key: string, options?: Partial<PersistentRunesOptions>): T;
35
+ }
36
+ declare function load<T>(key: string, options?: Partial<PersistentRunesOptions>): T | undefined;
37
+ declare function save<T>(key: string, value: T, options?: Partial<PersistentRunesOptions>): void;
38
+
39
+ export { load, save };
40
+ export type { PersistentRunesOptions };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty;var e=(o,i)=>m(o,"name",{value:i,configurable:!0});import{buildOptions as n}from"./options.js";import"browser-cookies";import"sjcl-codec-hex/from-bits.js";import"sjcl-codec-hex/to-bits.js";import"sjcl-es";import"superjson";import"@macfja/serializer";import"devalue";import"esserializer";import"next-json";import"php-serialize";import"serialize-anything";function s(o,i){const t={...n(void 0,void 0),...i},r=t.storageRead(o);if(typeof r=="string")return t.deserialize(r)}e(s,"load");function d(o,i,t){if(i===void 0)return;const r={...n(void 0,void 0),...t},p=r.serialize(i);r.storageWrite(o,p)}e(d,"save");export{s as load,d as save};
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var m=Object.defineProperty;var e=(o,i)=>m(o,"name",{value:i,configurable:!0});import{buildOptions as n}from"./options.mjs";import"browser-cookies";import"sjcl-codec-hex/from-bits.js";import"sjcl-codec-hex/to-bits.js";import"sjcl-es";import"superjson";import"@macfja/serializer";import"devalue";import"esserializer";import"next-json";import"php-serialize";import"serialize-anything";function s(o,i){const t={...n(void 0,void 0),...i},r=t.storageRead(o);if(typeof r=="string")return t.deserialize(r)}e(s,"load");function d(o,i,t){if(i===void 0)return;const r={...n(void 0,void 0),...t},p=r.serialize(i);r.storageWrite(o,p)}e(d,"save");export{s as load,d as save};
@@ -0,0 +1,62 @@
1
+ import { CookieOptions } from 'browser-cookies';
2
+ import { PersistentRunesOptions } from './index.js';
3
+ import * as macfja from '@macfja/serializer';
4
+ import * as dv from 'devalue';
5
+ import ESSerializer from 'esserializer';
6
+ import { NJSON } from 'next-json';
7
+ import * as PhpSerialize from 'php-serialize';
8
+
9
+ declare function PhpSerializeSerializerFactory(options?: {
10
+ givenOptions?: Parameters<typeof PhpSerialize.serialize>[2];
11
+ scope?: Parameters<typeof PhpSerialize.serialize>[1];
12
+ }): PersistentRunesSerializer;
13
+ declare function SerializeAnythingSerializerFactory(options?: {
14
+ maxDepth?: number;
15
+ pretty?: boolean;
16
+ }): PersistentRunesSerializer;
17
+ declare function JsonSerializerFactory(options?: {
18
+ replacer?: Parameters<typeof JSON.stringify>[1];
19
+ reviver?: Parameters<typeof JSON.parse>[1];
20
+ space?: Parameters<typeof JSON.stringify>[2];
21
+ }): PersistentRunesSerializer;
22
+ declare function DevalueSerializerFactory(options?: {
23
+ reducers?: Parameters<typeof dv.stringify>[1];
24
+ revivers?: Parameters<typeof dv.parse>[1];
25
+ }): PersistentRunesSerializer;
26
+ declare function ESSerializerSerializerFactory(options?: {
27
+ serializeOption?: Parameters<typeof ESSerializer.serialize>[1];
28
+ classes?: Parameters<typeof ESSerializer.deserialize>[1];
29
+ }): PersistentRunesSerializer;
30
+ declare function MacfjaSerializerFactory(options?: {
31
+ allowedClasses?: Parameters<typeof macfja.deserialize>[1];
32
+ }): PersistentRunesSerializer;
33
+ declare function NextJsonSerializerFactory(options?: {
34
+ stringifyOptionsOrReplacer?: Parameters<typeof NJSON.stringify>[1];
35
+ space?: Parameters<typeof NJSON.stringify>[2];
36
+ parseOptionsOrReviver?: Parameters<typeof NJSON.parse>[1];
37
+ }): PersistentRunesSerializer;
38
+
39
+ type PersistentRunesStorage = Pick<PersistentRunesOptions, "storageWrite" | "storageRead">;
40
+ type PersistentRunesSerializer = Pick<PersistentRunesOptions, "serialize" | "deserialize">;
41
+ declare const SuperJsonSerializer: PersistentRunesSerializer;
42
+ declare function BrowserCookieStorageFactory(cookieOptions?: CookieOptions): PersistentRunesStorage;
43
+ declare const BrowserLocalStorage: PersistentRunesStorage;
44
+ declare const BrowserSessionStorage: PersistentRunesStorage;
45
+ declare function addEncryptionStorage(onStorage: PersistentRunesStorage, encryptionKey: string, iv?: string): PersistentRunesStorage;
46
+ declare const BrowserCookieStorage: PersistentRunesStorage;
47
+ declare const JsonSerializer: PersistentRunesSerializer;
48
+ declare const DevalueSerializer: PersistentRunesSerializer;
49
+ declare const ESSerializerSerializer: PersistentRunesSerializer;
50
+ declare const MacfjaSerializer: PersistentRunesSerializer;
51
+ declare const NextJsonSerializer: PersistentRunesSerializer;
52
+ declare const PhpSerializeSerializer: PersistentRunesSerializer;
53
+ declare const SerializeAnythingSerializer: PersistentRunesSerializer;
54
+ /**
55
+ * Create a `PersistentRunesOptions` from a serializer and a storage
56
+ * @param serializer The serializer to use (if `undefined` then `JsonSerializer` will be used)
57
+ * @param storage The storage to use (if `undefined` then `BrowserLocalStorage` will be used)
58
+ */
59
+ declare function buildOptions(serializer: PersistentRunesSerializer | undefined, storage: PersistentRunesStorage | undefined): PersistentRunesOptions;
60
+
61
+ export { BrowserCookieStorage, BrowserCookieStorageFactory, BrowserLocalStorage, BrowserSessionStorage, DevalueSerializer, DevalueSerializerFactory, ESSerializerSerializer, ESSerializerSerializerFactory, JsonSerializer, JsonSerializerFactory, MacfjaSerializer, MacfjaSerializerFactory, NextJsonSerializer, NextJsonSerializerFactory, PhpSerializeSerializer, PhpSerializeSerializerFactory, SerializeAnythingSerializer, SerializeAnythingSerializerFactory, SuperJsonSerializer, addEncryptionStorage, buildOptions };
62
+ export type { PersistentRunesSerializer, PersistentRunesStorage };
@@ -0,0 +1 @@
1
+ var N=Object.defineProperty;var i=(e,r)=>N(e,"name",{value:r,configurable:!0});import{get as j,set as x}from"browser-cookies";import g from"sjcl-codec-hex/from-bits.js";import n from"sjcl-codec-hex/to-bits.js";import t from"sjcl-es";import*as u from"superjson";import*as d from"@macfja/serializer";import*as z from"devalue";import S from"esserializer";import{NJSON as f}from"next-json";import*as m from"php-serialize";import p from"serialize-anything";function w(e){return{deserialize(r){return m.unserialize(r,e?.scope,e?.givenOptions)},serialize(r){return m.serialize(r,e?.scope,e?.givenOptions)}}}i(w,"PhpSerializeSerializerFactory");function h(e){return{deserialize(r){return p.deserialize(r)},serialize(r){return p.serialize(r,e)}}}i(h,"SerializeAnythingSerializerFactory");function y(e){return{serialize(r){return JSON.stringify(r,e?.replacer,e?.space)},deserialize(r){return JSON.parse(r,e?.reviver)}}}i(y,"JsonSerializerFactory");function v(e){return{serialize(r){return z.stringify(r,e?.reducers)},deserialize(r){return z.parse(r,e?.revivers)}}}i(v,"DevalueSerializerFactory");function b(e){return{serialize(r){return S.serialize(r,e?.serializeOption)},deserialize(r){return S.deserialize(r,e?.classes)}}}i(b,"ESSerializerSerializerFactory");function T(e){return{serialize(r){return d.serialize(r)},deserialize(r){return d.deserialize(r,e?.allowedClasses)}}}i(T,"MacfjaSerializerFactory");function O(e){return{serialize(r){return f.stringify(r,e?.stringifyOptionsOrReplacer,e?.space)},deserialize(r){return f.parse(r,e?.parseOptionsOrReviver)}}}i(O,"NextJsonSerializerFactory");const I={serialize(e){return u.stringify(e)},deserialize(e){return u.parse(e)}};function F(e){return{storageWrite(r,a){x(r,a,{samesite:"Strict",...e})},storageRead(r){return j(r)||void 0}}}i(F,"BrowserCookieStorageFactory");const J={storageWrite(e,r){globalThis?.window&&"localStorage"in globalThis.window&&globalThis.window.localStorage.setItem(e,r)},storageRead(e){return globalThis?.window&&"localStorage"in globalThis.window&&globalThis.window.localStorage.getItem(e)||void 0}},W={storageWrite(e,r){globalThis?.window&&"sessionStorage"in globalThis.window&&globalThis.window.sessionStorage.setItem(e,r)},storageRead(e){return globalThis?.window&&"sessionStorage"in globalThis.window&&globalThis.window.sessionStorage.getItem(e)||void 0}};function E(e,r,a="spr"){const l=new t.cipher.aes(n(r));return{storageRead(s){const o=e.storageRead(s);if(o!==void 0)return t.codec.utf8String.fromBits(t.mode.gcm.decrypt(l,n(o.split(":")[0]),n(o.split(":")[1])))},storageWrite(s,o){const c=t.codec.utf8String.toBits(a),R=`${g(t.mode.gcm.encrypt(l,t.codec.utf8String.toBits(o),c,[],256))}:${g(c)}`;e.storageWrite(s,R)}}}i(E,"addEncryptionStorage");const A=F(),B=y(),C=v(),P=b(),k=T(),D=O(),H=w(),M=h();function $(e,r){return{...e??B,...r??J}}i($,"buildOptions");export{A as BrowserCookieStorage,F as BrowserCookieStorageFactory,J as BrowserLocalStorage,W as BrowserSessionStorage,C as DevalueSerializer,v as DevalueSerializerFactory,P as ESSerializerSerializer,b as ESSerializerSerializerFactory,B as JsonSerializer,y as JsonSerializerFactory,k as MacfjaSerializer,T as MacfjaSerializerFactory,D as NextJsonSerializer,O as NextJsonSerializerFactory,H as PhpSerializeSerializer,w as PhpSerializeSerializerFactory,M as SerializeAnythingSerializer,h as SerializeAnythingSerializerFactory,I as SuperJsonSerializer,E as addEncryptionStorage,$ as buildOptions};
@@ -0,0 +1 @@
1
+ var N=Object.defineProperty;var i=(e,r)=>N(e,"name",{value:r,configurable:!0});import{get as j,set as x}from"browser-cookies";import g from"sjcl-codec-hex/from-bits.js";import n from"sjcl-codec-hex/to-bits.js";import t from"sjcl-es";import*as u from"superjson";import*as d from"@macfja/serializer";import*as z from"devalue";import S from"esserializer";import{NJSON as f}from"next-json";import*as m from"php-serialize";import p from"serialize-anything";function w(e){return{deserialize(r){return m.unserialize(r,e?.scope,e?.givenOptions)},serialize(r){return m.serialize(r,e?.scope,e?.givenOptions)}}}i(w,"PhpSerializeSerializerFactory");function h(e){return{deserialize(r){return p.deserialize(r)},serialize(r){return p.serialize(r,e)}}}i(h,"SerializeAnythingSerializerFactory");function y(e){return{serialize(r){return JSON.stringify(r,e?.replacer,e?.space)},deserialize(r){return JSON.parse(r,e?.reviver)}}}i(y,"JsonSerializerFactory");function v(e){return{serialize(r){return z.stringify(r,e?.reducers)},deserialize(r){return z.parse(r,e?.revivers)}}}i(v,"DevalueSerializerFactory");function b(e){return{serialize(r){return S.serialize(r,e?.serializeOption)},deserialize(r){return S.deserialize(r,e?.classes)}}}i(b,"ESSerializerSerializerFactory");function T(e){return{serialize(r){return d.serialize(r)},deserialize(r){return d.deserialize(r,e?.allowedClasses)}}}i(T,"MacfjaSerializerFactory");function O(e){return{serialize(r){return f.stringify(r,e?.stringifyOptionsOrReplacer,e?.space)},deserialize(r){return f.parse(r,e?.parseOptionsOrReviver)}}}i(O,"NextJsonSerializerFactory");const I={serialize(e){return u.stringify(e)},deserialize(e){return u.parse(e)}};function F(e){return{storageWrite(r,a){x(r,a,{samesite:"Strict",...e})},storageRead(r){return j(r)||void 0}}}i(F,"BrowserCookieStorageFactory");const J={storageWrite(e,r){globalThis?.window&&"localStorage"in globalThis.window&&globalThis.window.localStorage.setItem(e,r)},storageRead(e){return globalThis?.window&&"localStorage"in globalThis.window&&globalThis.window.localStorage.getItem(e)||void 0}},W={storageWrite(e,r){globalThis?.window&&"sessionStorage"in globalThis.window&&globalThis.window.sessionStorage.setItem(e,r)},storageRead(e){return globalThis?.window&&"sessionStorage"in globalThis.window&&globalThis.window.sessionStorage.getItem(e)||void 0}};function E(e,r,a="spr"){const l=new t.cipher.aes(n(r));return{storageRead(s){const o=e.storageRead(s);if(o!==void 0)return t.codec.utf8String.fromBits(t.mode.gcm.decrypt(l,n(o.split(":")[0]),n(o.split(":")[1])))},storageWrite(s,o){const c=t.codec.utf8String.toBits(a),R=`${g(t.mode.gcm.encrypt(l,t.codec.utf8String.toBits(o),c,[],256))}:${g(c)}`;e.storageWrite(s,R)}}}i(E,"addEncryptionStorage");const A=F(),B=y(),C=v(),P=b(),k=T(),D=O(),H=w(),M=h();function $(e,r){return{...e??B,...r??J}}i($,"buildOptions");export{A as BrowserCookieStorage,F as BrowserCookieStorageFactory,J as BrowserLocalStorage,W as BrowserSessionStorage,C as DevalueSerializer,v as DevalueSerializerFactory,P as ESSerializerSerializer,b as ESSerializerSerializerFactory,B as JsonSerializer,y as JsonSerializerFactory,k as MacfjaSerializer,T as MacfjaSerializerFactory,D as NextJsonSerializer,O as NextJsonSerializerFactory,H as PhpSerializeSerializer,w as PhpSerializeSerializerFactory,M as SerializeAnythingSerializer,h as SerializeAnythingSerializerFactory,I as SuperJsonSerializer,E as addEncryptionStorage,$ as buildOptions};
@@ -0,0 +1,7 @@
1
+ import { PreprocessorGroup } from 'svelte/compiler';
2
+ import { Plugin } from 'vite';
3
+
4
+ declare function persistPlugin(): Plugin;
5
+ declare function persistPreprocessor(): PreprocessorGroup;
6
+
7
+ export { persistPlugin, persistPreprocessor };
@@ -0,0 +1,18 @@
1
+ var v=Object.defineProperty;var l=(t,r)=>v(t,"name",{value:r,configurable:!0});import M from"magic-string";const h=/\$persist\s*\(/g,w=`import * as __persist from "svelte-persistent-runes";
2
+ `;function C(t,r){let e=1,n=r,a=null,o=!1;for(;n<t.length&&e>0;){const c=t[n];if(o){o=!1,n++;continue}if(c==="\\"){o=!0,n++;continue}a?c===a&&(a=null):c==='"'||c==="'"||c==="`"?a=c:c==="("?e++:c===")"&&e--,n++}return n}l(C,"findMatchingParen");function P(t){const r=[];let e="",n=0,a=null,o=!1;for(let c=0;c<t.length;c++){const s=t[c];if(o){o=!1,e+=s;continue}if(s==="\\"){o=!0,e+=s;continue}a?(e+=s,s===a&&(a=null)):s==='"'||s==="'"||s==="`"?(a=s,e+=s):s==="("||s==="["||s==="{"?(n++,e+=s):s===")"||s==="]"||s==="}"?(n--,e+=s):s===","&&n===0?(r.push(e.trim()),e=""):e+=s}return e.trim()&&r.push(e.trim()),r}l(P,"splitArguments");function S(t,r){const e=t.lastIndexOf(`
3
+ `,r-1)+1;if(t.slice(e,r).includes("//"))return!0;const a=t.slice(0,r),o=a.lastIndexOf("/*"),c=a.lastIndexOf("*/");return o!==-1&&o>c}l(S,"isInsideComment");function _(t,r){if(S(t,r))return null;const e=t.slice(0,r),n=e.match(/(?:(?:let|const|var)\s+)?(\w+)\s*=\s*$/);if(n){const a=e.match(/class\s+\w+[^{]*\{[^}]*$/);return{varName:n[1],isClassProperty:!!a}}return null}l(_,"findVarName");function y(t){const r=[];h.lastIndex=0;let e=h.exec(t);for(;e!==null;){const n=e.index,a=n+e[0].length,o=C(t,a),c=t.slice(a,o-1),s=P(c);if(s.length>=2){const i=_(t,n);i&&r.push({start:n,end:o,varName:i.varName,initial:s[0],key:s[1],options:s[2]||"undefined",isClassProperty:i.isClassProperty})}e=h.exec(t)}return r}l(y,"findPersistCalls");function N(t,r){if(!t.includes("$persist"))return null;const e=y(t);if(e.length===0)return null;const n=new M(t);n.prepend(w);const a=new Map,o=[];for(const s of e){if(s.isClassProperty){const u=t.slice(0,s.start).match(/class\s+(\w+)[^{]*\{[^}]*$/);if(u){const m=u[1],p=a.get(m)??[];p.push(s),a.set(m,p)}}else o.push(s);const i=`$state(__persist.load(${s.key}, ${s.options}) ?? ${s.initial})`;n.overwrite(s.start,s.end,i)}if(o.length>0){const s=o.map(i=>`$effect(() => __persist.save(${i.key}, $state.snapshot(${i.varName}), ${i.options}));`).join(`
4
+ `);n.append(`
5
+ $effect.root(() => {
6
+ ${s}
7
+ });
8
+ `)}for(const[s,i]of a){const u=new RegExp(`class\\s+${s}(?:\\s+extends\\s+\\w+)?\\s*\\{`).exec(t);if(u){const m=u.index+u[0].length,p=t.slice(m).match(/constructor\s*\([^)]*\)\s*\{/),d=`$effect.root(() => {
9
+ ${i.map(f=>`$effect(() => __persist.save(${f.key}, $state.snapshot(this.${f.varName}), ${f.options}));`).join(`
10
+ `)}
11
+ });`;if(p&&p.index!==void 0){const f=m+p.index+p[0].length;n.appendLeft(f,`
12
+ ${d}
13
+ `)}else{const f=t.slice(u.index).match(/class\s+\w+\s+extends\s+(\w+)/),x=`
14
+ constructor(${f?"...args: any[]":""}) {
15
+ ${f?`super(...args);
16
+ `:""}${d}
17
+ }
18
+ `;n.appendLeft(m,x)}}}const c=n.generateMap({source:r,file:r,includeContent:!0,hires:!0});return{code:n.toString(),map:{version:c.version,file:c.file??r,sources:c.sources,sourcesContent:c.sourcesContent?.filter(s=>s!==null),names:c.names,mappings:c.mappings}}}l(N,"transformContent");function k(){const t=g();return{name:"svelte-persistent-runes",transform(r,e){if(!/\.svelte\.(c|m)?[jt]s$/.test(e))return null;const n=t.script?.({content:r,filename:e,attributes:{},markup:""});return!n||n.code===r?null:{code:n.code,map:n.map}}}}l(k,"persistPlugin");function g(){return{name:"svelte-persistent-runes",script({content:t,filename:r="unknown.js"}){const e=N(t,r);return e?{code:e.code,map:e.map}:{code:t}}}}l(g,"persistPreprocessor");export{k as persistPlugin,g as persistPreprocessor};
@@ -0,0 +1,18 @@
1
+ var v=Object.defineProperty;var l=(t,r)=>v(t,"name",{value:r,configurable:!0});import M from"magic-string";const h=/\$persist\s*\(/g,w=`import * as __persist from "svelte-persistent-runes";
2
+ `;function C(t,r){let e=1,n=r,a=null,o=!1;for(;n<t.length&&e>0;){const c=t[n];if(o){o=!1,n++;continue}if(c==="\\"){o=!0,n++;continue}a?c===a&&(a=null):c==='"'||c==="'"||c==="`"?a=c:c==="("?e++:c===")"&&e--,n++}return n}l(C,"findMatchingParen");function P(t){const r=[];let e="",n=0,a=null,o=!1;for(let c=0;c<t.length;c++){const s=t[c];if(o){o=!1,e+=s;continue}if(s==="\\"){o=!0,e+=s;continue}a?(e+=s,s===a&&(a=null)):s==='"'||s==="'"||s==="`"?(a=s,e+=s):s==="("||s==="["||s==="{"?(n++,e+=s):s===")"||s==="]"||s==="}"?(n--,e+=s):s===","&&n===0?(r.push(e.trim()),e=""):e+=s}return e.trim()&&r.push(e.trim()),r}l(P,"splitArguments");function S(t,r){const e=t.lastIndexOf(`
3
+ `,r-1)+1;if(t.slice(e,r).includes("//"))return!0;const a=t.slice(0,r),o=a.lastIndexOf("/*"),c=a.lastIndexOf("*/");return o!==-1&&o>c}l(S,"isInsideComment");function _(t,r){if(S(t,r))return null;const e=t.slice(0,r),n=e.match(/(?:(?:let|const|var)\s+)?(\w+)\s*=\s*$/);if(n){const a=e.match(/class\s+\w+[^{]*\{[^}]*$/);return{varName:n[1],isClassProperty:!!a}}return null}l(_,"findVarName");function y(t){const r=[];h.lastIndex=0;let e=h.exec(t);for(;e!==null;){const n=e.index,a=n+e[0].length,o=C(t,a),c=t.slice(a,o-1),s=P(c);if(s.length>=2){const i=_(t,n);i&&r.push({start:n,end:o,varName:i.varName,initial:s[0],key:s[1],options:s[2]||"undefined",isClassProperty:i.isClassProperty})}e=h.exec(t)}return r}l(y,"findPersistCalls");function N(t,r){if(!t.includes("$persist"))return null;const e=y(t);if(e.length===0)return null;const n=new M(t);n.prepend(w);const a=new Map,o=[];for(const s of e){if(s.isClassProperty){const u=t.slice(0,s.start).match(/class\s+(\w+)[^{]*\{[^}]*$/);if(u){const m=u[1],p=a.get(m)??[];p.push(s),a.set(m,p)}}else o.push(s);const i=`$state(__persist.load(${s.key}, ${s.options}) ?? ${s.initial})`;n.overwrite(s.start,s.end,i)}if(o.length>0){const s=o.map(i=>`$effect(() => __persist.save(${i.key}, $state.snapshot(${i.varName}), ${i.options}));`).join(`
4
+ `);n.append(`
5
+ $effect.root(() => {
6
+ ${s}
7
+ });
8
+ `)}for(const[s,i]of a){const u=new RegExp(`class\\s+${s}(?:\\s+extends\\s+\\w+)?\\s*\\{`).exec(t);if(u){const m=u.index+u[0].length,p=t.slice(m).match(/constructor\s*\([^)]*\)\s*\{/),d=`$effect.root(() => {
9
+ ${i.map(f=>`$effect(() => __persist.save(${f.key}, $state.snapshot(this.${f.varName}), ${f.options}));`).join(`
10
+ `)}
11
+ });`;if(p&&p.index!==void 0){const f=m+p.index+p[0].length;n.appendLeft(f,`
12
+ ${d}
13
+ `)}else{const f=t.slice(u.index).match(/class\s+\w+\s+extends\s+(\w+)/),x=`
14
+ constructor(${f?"...args: any[]":""}) {
15
+ ${f?`super(...args);
16
+ `:""}${d}
17
+ }
18
+ `;n.appendLeft(m,x)}}}const c=n.generateMap({source:r,file:r,includeContent:!0,hires:!0});return{code:n.toString(),map:{version:c.version,file:c.file??r,sources:c.sources,sourcesContent:c.sourcesContent?.filter(s=>s!==null),names:c.names,mappings:c.mappings}}}l(N,"transformContent");function k(){const t=g();return{name:"svelte-persistent-runes",transform(r,e){if(!/\.svelte\.(c|m)?[jt]s$/.test(e))return null;const n=t.script?.({content:r,filename:e,attributes:{},markup:""});return!n||n.code===r?null:{code:n.code,map:n.map}}}}l(k,"persistPlugin");function g(){return{name:"svelte-persistent-runes",script({content:t,filename:r="unknown.js"}){const e=N(t,r);return e?{code:e.code,map:e.map}:{code:t}}}}l(g,"persistPreprocessor");export{k as persistPlugin,g as persistPreprocessor};
@@ -0,0 +1,3 @@
1
+ export { persistPreprocessor as default } from './plugins.js';
2
+ import 'svelte/compiler';
3
+ import 'vite';
@@ -0,0 +1 @@
1
+ import{persistPreprocessor as s}from"./plugins.js";import"magic-string";export{s as default};
@@ -0,0 +1 @@
1
+ import{persistPreprocessor as s}from"./plugins.mjs";import"magic-string";export{s as default};
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "svelte-persistent-runes",
3
+ "version": "2.0.0",
4
+ "description": "A Svelte reactive rune that keep its value through pages and reloads",
5
+ "license": "MIT",
6
+ "author": "andrescera",
7
+ "homepage": "https://github.com/andrescera/svelte-persistent-runes",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/andrescera/svelte-persistent-runes"
11
+ },
12
+ "bugs": "https://github.com/andrescera/svelte-persistent-runes/issues",
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "./preprocessor": {
21
+ "types": "./dist/preprocessor.d.ts",
22
+ "import": "./dist/preprocessor.mjs",
23
+ "default": "./dist/preprocessor.js"
24
+ },
25
+ "./plugins": {
26
+ "types": "./dist/plugins.d.ts",
27
+ "import": "./dist/plugins.mjs",
28
+ "default": "./dist/plugins.js"
29
+ },
30
+ "./options": {
31
+ "types": "./dist/options.d.ts",
32
+ "import": "./dist/options.mjs",
33
+ "default": "./dist/options.js"
34
+ }
35
+ },
36
+ "main": "dist/index.js",
37
+ "files": [
38
+ "dist/"
39
+ ],
40
+ "scripts": {
41
+ "build": "pkgroll --minify --clean-dist",
42
+ "fix": "biome check --write .",
43
+ "lint": "biome check .",
44
+ "prepublishOnly": "npm run build",
45
+ "test": "ava",
46
+ "test:watch": "ava --watch"
47
+ },
48
+ "types": "dist/index.d.ts",
49
+ "dependencies": {
50
+ "@macfja/serializer": "^1.1.4",
51
+ "browser-cookies": "^1.2.0",
52
+ "cookie": "^1.1.1",
53
+ "devalue": "^5.6.1",
54
+ "esserializer": "^1.3.11",
55
+ "magic-string": "^0.30.21",
56
+ "next-json": "^0.5.1",
57
+ "php-serialize": "^5.1.3",
58
+ "serialize-anything": "^1.2.3",
59
+ "sjcl-codec-hex": "^1.0.0",
60
+ "sjcl-es": "^2.0.0",
61
+ "superjson": "^2.2.6"
62
+ },
63
+ "peerDependencies": {
64
+ "svelte": "^5.0.0",
65
+ "vite": "^5 || ^6 || ^7"
66
+ },
67
+ "devDependencies": {
68
+ "@biomejs/biome": "^2.3.10",
69
+ "ava": "^6.4.1",
70
+ "pkgroll": "^2.21.4",
71
+ "rollup": "^4.54.0",
72
+ "tsimp": "^2.0.12",
73
+ "typescript": "^5.9.3",
74
+ "vite": "^7.3.0"
75
+ },
76
+ "keywords": [
77
+ "cookie",
78
+ "encryptedStorage",
79
+ "localStorage",
80
+ "persist",
81
+ "persistent",
82
+ "rune",
83
+ "runes",
84
+ "sessionStorage",
85
+ "svelte",
86
+ "sveltejs",
87
+ "sveltekit"
88
+ ],
89
+ "ava": {
90
+ "extensions": {
91
+ "ts": "module"
92
+ },
93
+ "nodeArguments": [
94
+ "--import=tsimp"
95
+ ]
96
+ }
97
+ }