edges-svelte 0.6.1 → 1.0.1
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 +56 -100
- package/dist/provider/Provider.d.ts +12 -11
- package/dist/provider/Provider.js +31 -28
- package/dist/store/State.svelte.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# EdgeS
|
2
2
|
|
3
|
-
### A blazing-fast, extremely lightweight and SSR-friendly store for
|
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
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
```
|
71
|
+
```svelte
|
83
72
|
<script lang="ts">
|
84
|
-
|
73
|
+
import { myProvider } from 'your-alias';
|
85
74
|
|
86
|
-
|
75
|
+
const { collection, collectionLengthDoubled, updateAction } = myProvider();
|
87
76
|
</script>
|
88
77
|
|
89
|
-
{$collection.join(', ')}
|
90
|
-
{$collectionLengthDoubled}
|
91
|
-
|
92
|
-
|
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
|
-
- 💡
|
96
|
-
- 🛡️ Fully SSR-safe —
|
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
|
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
|
-
|
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
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
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(
|
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
|
-
>
|
127
|
+
> Behaves like Svelte’s `writable`, but is fully SSR-safe and scoped per-request.
|
160
128
|
|
161
129
|
---
|
162
130
|
|
163
|
-
### `
|
131
|
+
### `createDerivedState`
|
164
132
|
|
165
133
|
```ts
|
166
|
-
const count = createState(
|
134
|
+
const count = createState(1);
|
167
135
|
const doubled = createDerivedState([count], ([$n]) => $n * 2);
|
168
136
|
|
169
137
|
$doubled;
|
170
138
|
```
|
171
139
|
|
172
|
-
>
|
140
|
+
> Like Svelte’s `derived`.
|
173
141
|
|
174
142
|
---
|
175
143
|
|
176
144
|
### `createRawState`
|
177
145
|
|
178
146
|
```ts
|
179
|
-
const counter = createRawState(
|
147
|
+
const counter = createRawState(0);
|
180
148
|
counter.value += 1;
|
181
149
|
```
|
182
150
|
|
183
|
-
> Lightweight
|
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
|
-
|
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
|
-
|
202
|
-
|
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
|
-
##
|
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
|
-
##
|
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
|
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
|
-
|
210
|
+
### What is the difference between `createState` and `createRawState`?
|
255
211
|
|
256
|
-
|
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`
|
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,15 @@
|
|
1
|
-
import {
|
1
|
+
import { createDerivedState as BaseCreateDerivedState } from '../store/index.js';
|
2
|
+
import type { Writable } from 'svelte/store';
|
2
3
|
type StoreDeps = {
|
3
|
-
createRawState:
|
4
|
-
|
5
|
-
|
4
|
+
createRawState: <T>(initial: T | (() => T)) => {
|
5
|
+
value: T;
|
6
|
+
};
|
7
|
+
createState: <T>(initial: T | (() => T)) => Writable<T>;
|
8
|
+
createDerivedState: typeof BaseCreateDerivedState;
|
6
9
|
};
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
+
type NoConflict<I> = {
|
11
|
+
[K in keyof I]: K extends keyof StoreDeps ? never : I[K];
|
12
|
+
};
|
13
|
+
export declare const createProvider: <T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: StoreDeps & NoConflict<I>) => T, inject?: I) => (() => T);
|
14
|
+
export declare const createProviderFactory: <I extends Record<string, unknown>>(inject: I) => <T>(name: string, factory: (args: StoreDeps & NoConflict<I>) => T) => () => T;
|
14
15
|
export {};
|
@@ -1,45 +1,48 @@
|
|
1
|
-
import { createState, createDerivedState, createRawState } from '../store/index.js';
|
1
|
+
import { createState as BaseCreateState, createDerivedState as BaseCreateDerivedState, createRawState as BaseCreateRawState } from '../store/index.js';
|
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 = (
|
6
|
-
const
|
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;
|
7
|
+
let stateCounter = 0;
|
15
8
|
return () => {
|
16
|
-
|
17
|
-
return options.factory(deps);
|
18
|
-
}
|
9
|
+
let contextMap;
|
19
10
|
if (browser) {
|
20
|
-
|
21
|
-
globalClientCache.set(cacheKey, options.factory(deps));
|
22
|
-
}
|
23
|
-
return globalClientCache.get(cacheKey);
|
11
|
+
contextMap = globalClientCache;
|
24
12
|
}
|
25
13
|
else {
|
26
14
|
const context = RequestContext.current();
|
27
15
|
if (!context.data.providers) {
|
28
16
|
context.data.providers = new Map();
|
29
17
|
}
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
18
|
+
contextMap = context.data.providers;
|
19
|
+
}
|
20
|
+
if (cacheKey && contextMap.has(cacheKey)) {
|
21
|
+
return contextMap.get(cacheKey);
|
22
|
+
}
|
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 BaseCreateState(key, initFn);
|
29
|
+
},
|
30
|
+
createRawState: (initial) => {
|
31
|
+
const key = `${cacheKey ?? 'provider'}::rawstate::${stateCounter++}`;
|
32
|
+
const initFn = typeof initial === 'function' ? initial : () => initial;
|
33
|
+
return BaseCreateRawState(key, initFn);
|
34
|
+
},
|
35
|
+
createDerivedState: BaseCreateDerivedState
|
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(
|
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;
|