edges-svelte 1.0.3 → 1.1.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 +36 -18
- package/dist/provider/Provider.d.ts +9 -5
- package/dist/provider/Provider.js +46 -14
- package/dist/store/State.svelte.js +14 -9
- package/package.json +1 -1
package/README.md
CHANGED
@@ -10,7 +10,7 @@ No context boilerplate. No hydration headaches. Just drop-in SSR-compatible stat
|
|
10
10
|
- 🧠 Persistent per-request memory via `AsyncLocalStorage`
|
11
11
|
- 💧 Tiny API
|
12
12
|
- 💥 Instant serialization without magic
|
13
|
-
- 🧩
|
13
|
+
- 🧩 Dependency injection, zero runtime overhead
|
14
14
|
|
15
15
|
> Designed for **SvelteKit**.
|
16
16
|
|
@@ -49,12 +49,12 @@ export const handle: Handle = async ({ event, resolve }) => {
|
|
49
49
|
|
50
50
|
## Basic usage
|
51
51
|
|
52
|
-
### `
|
52
|
+
### `createStore` - creates a store function that can manage states
|
53
53
|
|
54
54
|
```ts
|
55
|
-
import {
|
56
|
-
|
57
|
-
const
|
55
|
+
import { createStore } from 'edges-svelte';
|
56
|
+
// First argument is a unique name. Each store must havew a unique name.
|
57
|
+
const myStore = createStore('MyStore', ({ createState, createDerivedState }) => {
|
58
58
|
// createState creates a writable, SSR-safe store with a unique key
|
59
59
|
const collection = createState<number[]>([]);
|
60
60
|
// createDerivedState creates a derived store, SSR-safe as well
|
@@ -70,9 +70,9 @@ const myProvider = createProvider('MyProvider', ({ createState, createDerivedSta
|
|
70
70
|
|
71
71
|
```svelte
|
72
72
|
<script lang="ts">
|
73
|
-
import {
|
73
|
+
import { myStore } from 'your-alias';
|
74
74
|
|
75
|
-
const { collection, collectionLengthDoubled, updateAction } =
|
75
|
+
const { collection, collectionLengthDoubled, updateAction } = myStore();
|
76
76
|
</script>
|
77
77
|
|
78
78
|
{$collection.join(', ')}
|
@@ -84,17 +84,17 @@ const myProvider = createProvider('MyProvider', ({ createState, createDerivedSta
|
|
84
84
|
<!-- Will update the state -->
|
85
85
|
```
|
86
86
|
|
87
|
-
- 💡 All stores created inside `
|
87
|
+
- 💡 All stores created inside `createStore` use unique keys automatically and are request-scoped
|
88
88
|
- 🛡️ Fully SSR-safe — stores are isolated per request and serialized automatically
|
89
89
|
|
90
90
|
---
|
91
91
|
|
92
|
-
##
|
92
|
+
## Store Caching (built-in)
|
93
93
|
|
94
|
-
|
94
|
+
Stores are cached per request by their unique name (cache key). Calling the same store multiple times in the same request returns the cached instance.
|
95
95
|
|
96
96
|
```ts
|
97
|
-
const
|
97
|
+
const myCachedStore = createStore('MyCachedStore', ({ createState }) => {
|
98
98
|
const data = createState(() => 'cached data');
|
99
99
|
return { data };
|
100
100
|
});
|
@@ -108,7 +108,7 @@ const myCachedProvider = createProvider('MyCachedProvider', ({ createState }) =>
|
|
108
108
|
|
109
109
|
State is isolated per request using `AsyncLocalStorage` internally. You never share data between users.
|
110
110
|
|
111
|
-
You **must** always create stores inside
|
111
|
+
You **must** always create stores inside using `createStore`.
|
112
112
|
|
113
113
|
---
|
114
114
|
|
@@ -154,10 +154,10 @@ counter.value += 1;
|
|
154
154
|
|
155
155
|
## Dependency Injection
|
156
156
|
|
157
|
-
You can inject dependencies into providers with `
|
157
|
+
You can inject dependencies into providers with `createStoreFactory`:
|
158
158
|
|
159
159
|
```ts
|
160
|
-
const withDeps =
|
160
|
+
const withDeps = createStoreFactory({ user: getUserFromSession });
|
161
161
|
|
162
162
|
const useUserStore = withDeps('UserStore', ({ user, createState }) => {
|
163
163
|
const userState = createState(user);
|
@@ -167,12 +167,30 @@ const useUserStore = withDeps('UserStore', ({ user, createState }) => {
|
|
167
167
|
|
168
168
|
---
|
169
169
|
|
170
|
+
## createPresenter
|
171
|
+
|
172
|
+
A cached provider for UI logic without direct state management primitives. Perfect for separating business logic from state management.
|
173
|
+
|
174
|
+
#### When to use
|
175
|
+
|
176
|
+
createPresenter is ideal when you want to:
|
177
|
+
|
178
|
+
1. Keep state management separate from UI logic
|
179
|
+
2. Create reusable business logic that doesn't directly manage state
|
180
|
+
3. Build presenters that orchestrate between services and stores
|
181
|
+
4. Follow clean architecture patterns with clear separation of concerns
|
182
|
+
|
183
|
+
Difference from createStore
|
184
|
+
While createStore provides state primitives (createState, createDerivedState, createRawState), createPresenter focuses purely on business logic and coordination. It maintains all the caching and dependency injection features of createStore but without state management utilities.
|
185
|
+
|
186
|
+
---
|
187
|
+
|
170
188
|
## Exports summary
|
171
189
|
|
172
|
-
| Feature
|
173
|
-
|
|
174
|
-
| `
|
175
|
-
| `edgesHandle`
|
190
|
+
| Feature | Import from |
|
191
|
+
| -------------------------------------------------------------------------------- | --------------------- |
|
192
|
+
| `createStore`, `createStoreFactory`, `createPresenter`, `createPresenterFactory` | `edges-svelte` |
|
193
|
+
| `edgesHandle` | `edges-svelte/server` |
|
176
194
|
|
177
195
|
---
|
178
196
|
|
@@ -1,5 +1,10 @@
|
|
1
1
|
import { createDerivedState as BaseCreateDerivedState } from '../store/index.js';
|
2
2
|
import type { Writable } from 'svelte/store';
|
3
|
+
type NoConflict<I, D> = {
|
4
|
+
[K in keyof I]: K extends keyof D ? never : I[K];
|
5
|
+
};
|
6
|
+
export declare const clearCache: (pattern?: string) => void;
|
7
|
+
export declare const createUiProvider: <T, F, D extends Record<string, unknown> | ((cacheKey: string) => Record<string, unknown>), I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (deps: F) => T, dependencies: D, inject?: I) => (() => T);
|
3
8
|
type StoreDeps = {
|
4
9
|
createRawState: <T>(initial: T | (() => T)) => {
|
5
10
|
value: T;
|
@@ -7,9 +12,8 @@ type StoreDeps = {
|
|
7
12
|
createState: <T>(initial: T | (() => T)) => Writable<T>;
|
8
13
|
createDerivedState: typeof BaseCreateDerivedState;
|
9
14
|
};
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
export declare const
|
14
|
-
export declare const createProviderFactory: <I extends Record<string, unknown>>(inject: I) => <T>(name: string, factory: (args: StoreDeps & NoConflict<I>) => T) => () => T;
|
15
|
+
export declare const createStore: <T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T, inject?: I) => (() => T);
|
16
|
+
export declare const createStoreFactory: <I extends Record<string, unknown>>(inject: I) => <T>(name: string, factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T) => () => T;
|
17
|
+
export declare const createPresenter: <T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: I) => T, inject?: I) => (() => T);
|
18
|
+
export declare const createPresenterFactory: <I extends Record<string, unknown>>(inject: I) => <T>(name: string, factory: (args: I) => T) => () => T;
|
15
19
|
export {};
|
@@ -2,7 +2,21 @@ import { createState as BaseCreateState, createDerivedState as BaseCreateDerived
|
|
2
2
|
import { RequestContext } from '../context/index.js';
|
3
3
|
import { browser } from '$app/environment';
|
4
4
|
const globalClientCache = new Map();
|
5
|
-
export const
|
5
|
+
export const clearCache = (pattern) => {
|
6
|
+
if (browser) {
|
7
|
+
if (pattern) {
|
8
|
+
for (const [key] of globalClientCache) {
|
9
|
+
if (key.includes(pattern)) {
|
10
|
+
globalClientCache.delete(key);
|
11
|
+
}
|
12
|
+
}
|
13
|
+
}
|
14
|
+
else {
|
15
|
+
globalClientCache.clear();
|
16
|
+
}
|
17
|
+
}
|
18
|
+
};
|
19
|
+
export const createUiProvider = (name, factory, dependencies, inject) => {
|
6
20
|
const cacheKey = name;
|
7
21
|
return () => {
|
8
22
|
let contextMap;
|
@@ -17,32 +31,50 @@ export const createProvider = (name, factory, inject) => {
|
|
17
31
|
contextMap = context.data.providers;
|
18
32
|
}
|
19
33
|
if (cacheKey && contextMap.has(cacheKey)) {
|
20
|
-
|
34
|
+
const cached = contextMap.get(cacheKey);
|
35
|
+
if (cached !== undefined) {
|
36
|
+
return cached;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
const deps = {
|
40
|
+
...(typeof dependencies === 'function' ? dependencies(cacheKey) : dependencies),
|
41
|
+
...inject
|
42
|
+
};
|
43
|
+
const instance = factory(deps);
|
44
|
+
if (cacheKey) {
|
45
|
+
contextMap.set(cacheKey, instance);
|
21
46
|
}
|
47
|
+
return instance;
|
48
|
+
};
|
49
|
+
};
|
50
|
+
export const createStore = (name, factory, inject) => {
|
51
|
+
return createUiProvider(name, factory, (cacheKey) => {
|
22
52
|
let stateCounter = 0;
|
23
|
-
|
24
|
-
...inject,
|
53
|
+
return {
|
25
54
|
createState: (initial) => {
|
26
|
-
const key = `${cacheKey
|
55
|
+
const key = `${cacheKey}::state::${stateCounter++}`;
|
27
56
|
const initFn = typeof initial === 'function' ? initial : () => initial;
|
28
57
|
return BaseCreateState(key, initFn);
|
29
58
|
},
|
30
59
|
createRawState: (initial) => {
|
31
|
-
const key = `${cacheKey
|
60
|
+
const key = `${cacheKey}::rawstate::${stateCounter++}`;
|
32
61
|
const initFn = typeof initial === 'function' ? initial : () => initial;
|
33
62
|
return BaseCreateRawState(key, initFn);
|
34
63
|
},
|
35
64
|
createDerivedState: BaseCreateDerivedState
|
36
65
|
};
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return
|
66
|
+
}, inject);
|
67
|
+
};
|
68
|
+
export const createStoreFactory = (inject) => {
|
69
|
+
return function createInjectedStore(name, factory) {
|
70
|
+
return createStore(name, factory, inject);
|
42
71
|
};
|
43
72
|
};
|
44
|
-
export const
|
45
|
-
return
|
46
|
-
|
73
|
+
export const createPresenter = (name, factory, inject) => {
|
74
|
+
return createUiProvider(name, factory, {}, inject);
|
75
|
+
};
|
76
|
+
export const createPresenterFactory = (inject) => {
|
77
|
+
return function createInjectedStore(name, factory) {
|
78
|
+
return createPresenter(name, factory, inject);
|
47
79
|
};
|
48
80
|
};
|
@@ -42,7 +42,7 @@ export const createRawState = (key, initial) => {
|
|
42
42
|
return {
|
43
43
|
get value() {
|
44
44
|
if (!map)
|
45
|
-
return
|
45
|
+
return initial();
|
46
46
|
if (!map.has(key))
|
47
47
|
map.set(key, structuredClone(initial()));
|
48
48
|
return map.get(key);
|
@@ -63,22 +63,22 @@ export const createState = (key, initial) => {
|
|
63
63
|
throw new Error('No RequestContext available');
|
64
64
|
if (!map.has(key))
|
65
65
|
map.set(key, structuredClone(initial()));
|
66
|
-
const subscribers = new Set();
|
66
|
+
//const subscribers: Set<(val: T) => void> = new Set();
|
67
67
|
return {
|
68
68
|
subscribe(run) {
|
69
69
|
run(map.get(key));
|
70
|
-
subscribers.add(run);
|
71
|
-
return () =>
|
70
|
+
//subscribers.add(run);
|
71
|
+
return () => { };
|
72
72
|
},
|
73
73
|
set(val) {
|
74
74
|
map.set(key, val);
|
75
|
-
subscribers.forEach((fn) => fn(val));
|
75
|
+
//subscribers.forEach((fn) => fn(val));
|
76
76
|
},
|
77
77
|
update(updater) {
|
78
78
|
const oldVal = map.get(key);
|
79
79
|
const newVal = updater(oldVal);
|
80
80
|
map.set(key, newVal);
|
81
|
-
subscribers.forEach((fn) => fn(newVal));
|
81
|
+
//subscribers.forEach((fn) => fn(newVal));
|
82
82
|
}
|
83
83
|
};
|
84
84
|
};
|
@@ -90,14 +90,19 @@ export const createDerivedState = (stores, deriveFn) => {
|
|
90
90
|
subscribe(run) {
|
91
91
|
const values = new Array(stores.length);
|
92
92
|
let initializedCount = 0;
|
93
|
+
let isInitialized = false;
|
94
|
+
const checkAndRun = () => {
|
95
|
+
if (initializedCount >= stores.length && !isInitialized) {
|
96
|
+
isInitialized = true;
|
97
|
+
run(deriveFn(values));
|
98
|
+
}
|
99
|
+
};
|
93
100
|
const unsubscribers = stores.map((store, i) => store.subscribe((value) => {
|
94
101
|
values[i] = value;
|
95
102
|
if (initializedCount < stores.length) {
|
96
103
|
initializedCount++;
|
97
104
|
}
|
98
|
-
|
99
|
-
run(deriveFn(values));
|
100
|
-
}
|
105
|
+
checkAndRun();
|
101
106
|
}));
|
102
107
|
return () => {
|
103
108
|
unsubscribers.forEach((unsub) => unsub());
|