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 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
- - 🧩 Provider-based dependency injection, zero runtime overhead
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
- ### `createProvider` - creates a provider function that can manage states
52
+ ### `createStore` - creates a store function that can manage states
53
53
 
54
54
  ```ts
55
- import { createProvider } from 'edges-svelte';
56
-
57
- const myProvider = createProvider('MyProvider', ({ createState, createDerivedState }) => {
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 { myProvider } from 'your-alias';
73
+ import { myStore } from 'your-alias';
74
74
 
75
- const { collection, collectionLengthDoubled, updateAction } = myProvider();
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 `createProvider` use unique keys automatically and are request-scoped
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
- ## Provider Caching (built-in)
92
+ ## Store Caching (built-in)
93
93
 
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.
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 myCachedProvider = createProvider('MyCachedProvider', ({ createState }) => {
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 providers returned by `createProvider`.
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 `createProviderFactory`:
157
+ You can inject dependencies into providers with `createStoreFactory`:
158
158
 
159
159
  ```ts
160
- const withDeps = createProviderFactory({ user: getUserFromSession });
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 | Import from |
173
- | ----------------------------------------- | --------------------- |
174
- | `createProvider`, `createProviderFactory` | `edges-svelte` |
175
- | `edgesHandle` | `edges-svelte/server` |
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
- 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;
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 createProvider = (name, factory, inject) => {
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
- return contextMap.get(cacheKey);
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
- const autoKeyDeps = {
24
- ...inject,
53
+ return {
25
54
  createState: (initial) => {
26
- const key = `${cacheKey ?? 'provider'}::state::${stateCounter++}`;
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 ?? 'provider'}::rawstate::${stateCounter++}`;
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
- const instance = factory(autoKeyDeps);
38
- if (cacheKey) {
39
- contextMap.set(cacheKey, instance);
40
- }
41
- return instance;
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 createProviderFactory = (inject) => {
45
- return function createInjectedProvider(name, factory) {
46
- return createProvider(name, factory, inject);
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 undefined;
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 () => subscribers.delete(run);
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
- if (initializedCount >= stores.length) {
99
- run(deriveFn(values));
100
- }
105
+ checkAndRun();
101
106
  }));
102
107
  return () => {
103
108
  unsubscribers.forEach((unsub) => unsub());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edges-svelte",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "author": "Pixel1917",
6
6
  "description": "A blazing-fast, extremely lightweight and SSR-friendly store for Svelte",