edges-svelte 0.6.1 → 1.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # EdgeS
2
2
 
3
- ### A blazing-fast, extremely lightweight and SSR-friendly store for Svelte.
3
+ ### A blazing-fast, extremely lightweight and SSR-friendly store for SvelteKit.
4
4
 
5
5
  **EdgeS** brings seamless, per-request state management to Svelte apps — fully reactive, server-aware, and serialization-safe by default.
6
6
 
@@ -49,87 +49,54 @@ export const handle: Handle = async ({ event, resolve }) => {
49
49
 
50
50
  ## Basic usage
51
51
 
52
- ### `createProvider` - creates store with access to `createState` and `createDerivedState`
52
+ ### `createProvider` - creates a provider function that can manage states
53
53
 
54
54
  ```ts
55
55
  import { createProvider } from 'edges-svelte';
56
56
 
57
- const myProvider = createProvider({
58
- factory: ({ createState, createDerivedState }) => {
59
- // Works just like writable
60
- const collection = createState<number[]>('unique-key', () => []);
61
- // Works just like derived
62
- const collectionLengthDoubled = createDerivedState([collection], ([$c]) => $c.length * 2);
63
- // Advanced derived
64
- const collectionLengthMultiplied = createDerivedState([collection], ([$c]) => (count: number) => {
65
- return $c.length * count;
66
- });
67
-
68
- const updateAction = (num: number) => {
69
- collection.update((n) => {
70
- n = [...n, num];
71
- return n;
72
- });
73
- };
74
-
75
- // ...Your code;
76
-
77
- return { collection, collectionLengthDoubled, collectionLengthMultiplied, updateAction };
78
- }
57
+ const myProvider = createProvider('MyProvider', ({ createState, createDerivedState }) => {
58
+ // createState creates a writable, SSR-safe store with a unique key
59
+ const collection = createState<number[]>([]);
60
+ // createDerivedState creates a derived store, SSR-safe as well
61
+ const collectionLengthDoubled = createDerivedState([collection], ([$c]) => $c.length * 2);
62
+
63
+ const updateAction = (num: number) => {
64
+ collection.update((n) => [...n, num]);
65
+ };
66
+
67
+ return { collection, collectionLengthDoubled, updateAction };
79
68
  });
80
69
  ```
81
70
 
82
- ```sveltehtml
71
+ ```svelte
83
72
  <script lang="ts">
84
- import {myProvider} from 'your-alias';
73
+ import { myProvider } from 'your-alias';
85
74
 
86
- const {collection, collectionLengthDoubled, collectionLengthMultiplied, updateAction} = myProvider();
75
+ const { collection, collectionLengthDoubled, updateAction } = myProvider();
87
76
  </script>
88
77
 
89
- {$collection.join(', ')} <!-- Empty string before button click, 25 after button click -->
90
- {$collectionLengthDoubled} <!-- 0 before button click, 2 after button click -->
91
- {$collectionLengthMultiplied(5)} <!-- 0 before button click, 5 after button click -->
92
- <button onclick={() => updateAction(25)}></button> <!-- Will update the state -->
78
+ {$collection.join(', ')}
79
+ {$collectionLengthDoubled}
80
+ <!-- 0 before button click, 2 after button click -->
81
+ {$collectionLengthMultiplied(5)}
82
+ <!-- 0 before button click, 5 after button click -->
83
+ <button onclick={() => updateAction(25)}>count update</button>
84
+ <!-- Will update the state -->
93
85
  ```
94
86
 
95
- - 💡 You get access to `createRawState`, `createState`, and `createDerivedState` in providers created by `createProvider`
96
- - 🛡️ Fully SSR-safe — all internal state is per-request
87
+ - 💡 All stores created inside `createProvider` use unique keys automatically and are request-scoped
88
+ - 🛡️ Fully SSR-safe — stores are isolated per request and serialized automatically
97
89
 
98
90
  ---
99
91
 
100
- ## Provider Caching with `cacheKey`
101
-
102
- To improve performance and avoid redundant computations, **EdgeS** supports caching providers by a unique `cacheKey`.
103
-
104
- ### What is `cacheKey`?
105
-
106
- - A `cacheKey` is a string uniquely identifying a specific provider invocation.
107
- - It enables **caching the result of the provider’s factory function**
108
- - Prevents repeated calls to the same provider with identical parameters.
109
- With caching:
92
+ ## Provider Caching (built-in)
110
93
 
111
- - The provider is called **only once per unique `cacheKey`** within a request.
112
- - Subsequent calls with the same key return the cached result instantly.
113
-
114
- ### How to use `cacheKey`
94
+ Providers are cached per request by their unique provider name (cache key). Calling the same provider multiple times in the same request returns the cached instance.
115
95
 
116
96
  ```ts
117
- import { createProvider } from 'edges-svelte';
118
-
119
- const myProvider = createProvider({
120
- cacheKey: 'MyUniqueProviderName',
121
- factory: ({ createState }, params) => {
122
- const myService = new MyService();
123
- const someData = createState('userData', () => undefined);
124
-
125
- const setUserData = async () => {
126
- await myService.getData().then((user) => {
127
- userData.set(user);
128
- });
129
- };
130
-
131
- return { userData };
132
- }
97
+ const myCachedProvider = createProvider('MyCachedProvider', ({ createState }) => {
98
+ const data = createState(() => 'cached data');
99
+ return { data };
133
100
  });
134
101
  ```
135
102
 
@@ -139,7 +106,9 @@ const myProvider = createProvider({
139
106
 
140
107
  ### SSR-safe state access
141
108
 
142
- All state is isolated per request and never shared between users thanks to `AsyncLocalStorage`. Access to state primitives (`createRawState`, `createState`, `createDerivedState`) is only possible through provider functions — ensuring that you never accidentally share state across requests.
109
+ State is isolated per request using `AsyncLocalStorage` internally. You never share data between users.
110
+
111
+ You **must** always create stores inside providers returned by `createProvider`.
143
112
 
144
113
  ---
145
114
 
@@ -148,66 +117,57 @@ All state is isolated per request and never shared between users thanks to `Asyn
148
117
  ### `createState`
149
118
 
150
119
  ```ts
151
- const count = createState('count', () => 0);
120
+ const count = createState(0);
152
121
 
153
- $count;
154
- // in template: {$count}
122
+ $count; // reactive store value
155
123
 
156
124
  count.update((n) => n + 1);
157
125
  ```
158
126
 
159
- > This behaves like native Svelte `writable`, but scoped to request. Fully SSR-safe.
127
+ > Behaves like Svelte’s `writable`, but is fully SSR-safe and scoped per-request.
160
128
 
161
129
  ---
162
130
 
163
- ### `createDerivedStore`
131
+ ### `createDerivedState`
164
132
 
165
133
  ```ts
166
- const count = createState('count', () => 1);
134
+ const count = createState(1);
167
135
  const doubled = createDerivedState([count], ([$n]) => $n * 2);
168
136
 
169
137
  $doubled;
170
138
  ```
171
139
 
172
- > Works just like Svelte’s `derived`, but fully SSR-compatible. On the server, computes once and reuses the value.
140
+ > Like Svelte’s `derived`.
173
141
 
174
142
  ---
175
143
 
176
144
  ### `createRawState`
177
145
 
178
146
  ```ts
179
- const counter = createRawState('counter', () => 0);
147
+ const counter = createRawState(0);
180
148
  counter.value += 1;
181
149
  ```
182
150
 
183
- > Lightweight, reactive, SSR-safe variable. No subscriptions. Access through `.value`.
184
-
185
- - ✅ Fully server-aware
186
- - ✅ Serialization-safe
187
- - ✅ Sync updates
151
+ > Lightweight reactive variable, SSR-safe, no subscriptions, direct `.value` access.
188
152
 
189
153
  ---
190
154
 
191
155
  ## Dependency Injection
192
156
 
193
- ### `createProviderFactory`
194
-
195
- For shared injected dependencies:
157
+ You can inject dependencies into providers with `createProviderFactory`:
196
158
 
197
159
  ```ts
198
160
  const withDeps = createProviderFactory({ user: getUserFromSession });
199
161
 
200
- const useUserStore = withDeps({
201
- factory: ({ user, createState }) => {
202
- const userState = createState('user', () => user);
203
- return { userState };
204
- }
162
+ const useUserStore = withDeps('UserStore', ({ user, createState }) => {
163
+ const userState = createState(user);
164
+ return { userState };
205
165
  });
206
166
  ```
207
167
 
208
168
  ---
209
169
 
210
- ## Imports
170
+ ## Exports summary
211
171
 
212
172
  | Feature | Import from |
213
173
  | ----------------------------------------- | --------------------- |
@@ -216,15 +176,7 @@ const useUserStore = withDeps({
216
176
 
217
177
  ---
218
178
 
219
- ## Best Practices
220
-
221
- - Use unique keys for each state to avoid collisions
222
- - Always create your states via providers to avoid shared memory across requests
223
- - Prefer `createProvider` even for simple state logic — it scales better and stays testable
224
-
225
- ---
226
-
227
- ## About edgesHandle
179
+ ## About `edgesHandle`
228
180
 
229
181
  ```ts
230
182
  /**
@@ -249,14 +201,18 @@ type EdgesHandle = (
249
201
 
250
202
  ## FAQ
251
203
 
252
- ### Why not just use `writable, derived`?
204
+ ### Why not just use `writable`, `derived` from Svelte?
205
+
206
+ Because those stores share state between requests when used on the server, potentially leaking data between users.
207
+
208
+ EdgeS ensures per-request isolation, so your server state is never shared accidentally.
253
209
 
254
- Because `writable` and `derived` shares state between requests when used on the server. That means users could leak state into each other’s responses. **EdgeS** solves that by isolating state per request.
210
+ ### What is the difference between `createState` and `createRawState`?
255
211
 
256
- ### Difference between `createState` and `createRawState`
212
+ - `createState` returns a full Svelte writable store with subscription and `$` store syntax.
213
+ - `createRawState` is a minimal reactive variable, no subscriptions, accessed via `.value`.
257
214
 
258
- `createRawState` is just like `$state`, but ssr-safe. It is a lightweight reactive variable and has no subscription. Access and change value through `.value`.
259
- 💡 Use `myState.value` to get/set the value directly — no `$` sugar and set, update methods.
215
+ Use `createRawState` for simple values where you don’t need reactive subscriptions.
260
216
 
261
217
  ---
262
218
 
@@ -1,14 +1,12 @@
1
- import { createState, createDerivedState, createRawState } from '../store/index.js';
1
+ import { createDerivedState } from '../store/index.js';
2
+ import type { Writable } from 'svelte/store';
2
3
  type StoreDeps = {
3
- createRawState: typeof createRawState;
4
- createState: typeof createState;
4
+ createRawState: <T>(initial: T | (() => T)) => {
5
+ value: T;
6
+ };
7
+ createState: <T>(initial: T | (() => T)) => Writable<T>;
5
8
  createDerivedState: typeof createDerivedState;
6
9
  };
7
- interface CreateProviderOptions<T, I extends Record<string, unknown> = Record<string, unknown>> {
8
- inject?: I;
9
- cacheKey?: string;
10
- factory: (args: StoreDeps & I) => T;
11
- }
12
- export declare const createProvider: <T, I extends Record<string, unknown> = Record<string, unknown>>(options: CreateProviderOptions<T, I>) => (() => T);
13
- export declare const createProviderFactory: <I extends Record<string, unknown>>(inject: I) => <T>(options: CreateProviderOptions<T, I>) => () => T;
10
+ export declare const createProvider: <T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: StoreDeps & I) => T, inject?: I) => (() => T);
11
+ export declare const createProviderFactory: <I extends Record<string, unknown>>(inject: I) => <T>(name: string, factory: (args: StoreDeps & I) => T) => () => T;
14
12
  export {};
@@ -2,44 +2,47 @@ import { createState, createDerivedState, createRawState } from '../store/index.
2
2
  import { RequestContext } from '../context/index.js';
3
3
  import { browser } from '$app/environment';
4
4
  const globalClientCache = new Map();
5
- export const createProvider = (options) => {
6
- const deps = {
7
- ...{
8
- createState,
9
- createRawState,
10
- createDerivedState
11
- },
12
- ...(options.inject || {})
13
- };
14
- const cacheKey = options.cacheKey;
5
+ export const createProvider = (name, factory, inject) => {
6
+ const cacheKey = name;
15
7
  return () => {
16
- if (!cacheKey) {
17
- return options.factory(deps);
18
- }
8
+ let contextMap;
19
9
  if (browser) {
20
- if (!globalClientCache.has(cacheKey)) {
21
- globalClientCache.set(cacheKey, options.factory(deps));
22
- }
23
- return globalClientCache.get(cacheKey);
10
+ contextMap = globalClientCache;
24
11
  }
25
12
  else {
26
13
  const context = RequestContext.current();
27
14
  if (!context.data.providers) {
28
15
  context.data.providers = new Map();
29
16
  }
30
- const map = context.data.providers;
31
- if (!map.has(cacheKey)) {
32
- map.set(cacheKey, options.factory(deps));
33
- }
34
- return map.get(cacheKey);
17
+ contextMap = context.data.providers;
18
+ }
19
+ if (cacheKey && contextMap.has(cacheKey)) {
20
+ return contextMap.get(cacheKey);
21
+ }
22
+ let stateCounter = 0;
23
+ const autoKeyDeps = {
24
+ ...inject,
25
+ createState: (initial) => {
26
+ const key = `${cacheKey ?? 'provider'}::state::${stateCounter++}`;
27
+ const initFn = typeof initial === 'function' ? initial : () => initial;
28
+ return createState(key, initFn);
29
+ },
30
+ createRawState: (initial) => {
31
+ const key = `${cacheKey ?? 'provider'}::rawstate::${stateCounter++}`;
32
+ const initFn = typeof initial === 'function' ? initial : () => initial;
33
+ return createRawState(key, initFn);
34
+ },
35
+ createDerivedState
36
+ };
37
+ const instance = factory(autoKeyDeps);
38
+ if (cacheKey) {
39
+ contextMap.set(cacheKey, instance);
35
40
  }
41
+ return instance;
36
42
  };
37
43
  };
38
44
  export const createProviderFactory = (inject) => {
39
- return function createInjectedProvider(options) {
40
- return createProvider({
41
- ...options,
42
- inject
43
- });
45
+ return function createInjectedProvider(name, factory) {
46
+ return createProvider(name, factory, inject);
44
47
  };
45
48
  };
@@ -28,7 +28,7 @@ const getBrowserState = (key, initial) => {
28
28
  };
29
29
  export const createRawState = (key, initial) => {
30
30
  if (browser) {
31
- let state = getBrowserState(key, initial());
31
+ let state = $state(getBrowserState(key, initial()));
32
32
  return {
33
33
  get value() {
34
34
  return state;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edges-svelte",
3
- "version": "0.6.1",
3
+ "version": "1.0.0",
4
4
  "license": "MIT",
5
5
  "author": "Pixel1917",
6
6
  "description": "A blazing-fast, extremely lightweight and SSR-friendly store for Svelte",