edges-svelte 2.2.0 → 2.2.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 +1 -1
- package/dist/client/NavigationSync.svelte.js +1 -8
- package/dist/plugin/EdgesAutoHandlePlugin.d.ts +0 -62
- package/dist/plugin/EdgesAutoHandlePlugin.js +1 -64
- package/dist/provider/Provider.d.ts +0 -38
- package/dist/provider/Provider.js +3 -21
- package/dist/server/AutoWrapHandle.js +0 -4
- package/dist/server/EdgesHandle.d.ts +0 -3
- package/dist/server/EdgesHandle.js +0 -4
- package/dist/server/EdgesHandleSimplified.d.ts +0 -8
- package/dist/server/EdgesHandleSimplified.js +0 -8
- package/dist/store/State.svelte.js +0 -17
- package/dist/types.d.ts +0 -80
- package/dist/utils/batch.d.ts +0 -21
- package/dist/utils/batch.js +0 -40
- package/dist/utils/dev.d.ts +0 -21
- package/dist/utils/dev.js +2 -29
- package/dist/utils/environment.d.ts +0 -4
- package/dist/utils/environment.js +0 -4
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -130,7 +130,7 @@ const doubled = createDerivedState([count], ([$n]) => $n * 2);
|
|
|
130
130
|
$doubled;
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
> Like Svelte’s `derived`.
|
|
133
|
+
> Like Svelte’s `derived`. On the server side will not subscribe to store like derived, just reads initial value, on client side works like derived.
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
|
@@ -159,18 +159,13 @@ if (browser) {
|
|
|
159
159
|
return interceptEdgesStateFromResponse(response);
|
|
160
160
|
};
|
|
161
161
|
if (typeof MutationObserver !== 'undefined') {
|
|
162
|
-
// Optimized: Only observe <body> instead of entire document tree
|
|
163
|
-
// This reduces CPU usage by 30-50% on pages with frequent DOM updates
|
|
164
162
|
const observer = new MutationObserver((mutations) => {
|
|
165
163
|
for (const mutation of mutations) {
|
|
166
164
|
if (mutation.type === 'childList') {
|
|
167
165
|
for (const node of mutation.addedNodes) {
|
|
168
|
-
// Filter early: only process script elements
|
|
169
166
|
if (node instanceof HTMLScriptElement) {
|
|
170
167
|
const text = node.textContent || '';
|
|
171
|
-
// Quick check for our state markers
|
|
172
168
|
if (text.includes('__SAFE_SSR_STATE__') && text.includes('__EDGES_REVIVER__')) {
|
|
173
|
-
// Use microtask instead of setTimeout(0) for better performance
|
|
174
169
|
queueMicrotask(() => {
|
|
175
170
|
if (window.__SAFE_SSR_STATE__) {
|
|
176
171
|
for (const [key, value] of window.__SAFE_SSR_STATE__) {
|
|
@@ -187,11 +182,9 @@ if (browser) {
|
|
|
187
182
|
}
|
|
188
183
|
}
|
|
189
184
|
});
|
|
190
|
-
// Optimized: Observe only <body> with subtree: false to reduce overhead
|
|
191
|
-
// State scripts are always injected into body, not head
|
|
192
185
|
observer.observe(document.body || document.documentElement, {
|
|
193
186
|
childList: true,
|
|
194
|
-
subtree: false
|
|
187
|
+
subtree: false
|
|
195
188
|
});
|
|
196
189
|
if (typeof window !== 'undefined') {
|
|
197
190
|
window.addEventListener('beforeunload', () => observer.disconnect());
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
export interface EdgesPluginOptions {
|
|
3
|
-
/**
|
|
4
|
-
* State compression options
|
|
5
|
-
*/
|
|
6
3
|
compression?: {
|
|
7
|
-
/**
|
|
8
|
-
* Enable compression for large state objects
|
|
9
|
-
* @default false
|
|
10
|
-
*/
|
|
11
4
|
enabled?: boolean;
|
|
12
|
-
/**
|
|
13
|
-
* Minimum size in bytes before compression is applied
|
|
14
|
-
* @default 1024 (1KB)
|
|
15
|
-
*/
|
|
16
5
|
threshold?: number;
|
|
17
6
|
};
|
|
18
|
-
/**
|
|
19
|
-
* Silence Chrome DevTools requests
|
|
20
|
-
* @default true
|
|
21
|
-
*/
|
|
22
7
|
silentChromeDevtools?: boolean;
|
|
23
8
|
}
|
|
24
9
|
/**
|
|
@@ -26,7 +11,6 @@ export interface EdgesPluginOptions {
|
|
|
26
11
|
*
|
|
27
12
|
* Use this when:
|
|
28
13
|
* - Creating a wrapper package that re-exports edges-svelte functionality
|
|
29
|
-
* - Developing the package itself (use `$lib/server` as serverPath)
|
|
30
14
|
*
|
|
31
15
|
* @param packageName - The name that will be used in generated imports (e.g., 'edges-svelte', 'my-wrapper')
|
|
32
16
|
* @param serverPath - The import path to the server module (e.g., 'edges-svelte/server', '$lib/server')
|
|
@@ -38,25 +22,11 @@ export interface EdgesPluginOptions {
|
|
|
38
22
|
*
|
|
39
23
|
* export const myWrapperPlugin = createEdgesPluginFactory('my-wrapper', 'my-wrapper/server');
|
|
40
24
|
* ```
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* // For package development (testing the package itself)
|
|
45
|
-
* import { createEdgesPluginFactory } from './src/lib/plugin/index.js';
|
|
46
|
-
*
|
|
47
|
-
* const edgesPluginDev = createEdgesPluginFactory('edges-svelte', '$lib/server');
|
|
48
|
-
*
|
|
49
|
-
* export default defineConfig({
|
|
50
|
-
* plugins: [sveltekit(), edgesPluginDev()]
|
|
51
|
-
* });
|
|
52
|
-
* ```
|
|
53
25
|
*/
|
|
54
26
|
export declare function createEdgesPluginFactory(packageName: string, serverPath: string): (options?: EdgesPluginOptions) => Plugin;
|
|
55
27
|
/**
|
|
56
|
-
* Default edges-svelte plugin for end users.
|
|
57
28
|
*
|
|
58
29
|
* This plugin automatically wraps the SvelteKit handle hook with edgesHandle,
|
|
59
|
-
* eliminating the need to manually wrap your handle function.
|
|
60
30
|
*
|
|
61
31
|
* @example
|
|
62
32
|
* ```ts
|
|
@@ -69,37 +39,5 @@ export declare function createEdgesPluginFactory(packageName: string, serverPath
|
|
|
69
39
|
* plugins: [sveltekit(), edgesPlugin()]
|
|
70
40
|
* });
|
|
71
41
|
* ```
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```ts
|
|
75
|
-
* // vite.config.ts - With compression
|
|
76
|
-
* export default defineConfig({
|
|
77
|
-
* plugins: [
|
|
78
|
-
* sveltekit(),
|
|
79
|
-
* edgesPlugin({
|
|
80
|
-
* compression: {
|
|
81
|
-
* enabled: true,
|
|
82
|
-
* threshold: 2048 // Compress states larger than 2KB
|
|
83
|
-
* }
|
|
84
|
-
* })
|
|
85
|
-
* ]
|
|
86
|
-
* });
|
|
87
|
-
* ```
|
|
88
|
-
*
|
|
89
|
-
* After adding the plugin, you can write your hooks.server.ts normally:
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```ts
|
|
93
|
-
* // hooks.server.ts - No manual wrapping needed!
|
|
94
|
-
*
|
|
95
|
-
* // Option 1: No handle defined - plugin creates default
|
|
96
|
-
* // (nothing to write, it just works)
|
|
97
|
-
*
|
|
98
|
-
* // Option 2: Custom handle - plugin automatically wraps it
|
|
99
|
-
* export const handle = async ({ event, resolve }) => {
|
|
100
|
-
* console.log('My custom middleware');
|
|
101
|
-
* return resolve(event);
|
|
102
|
-
* };
|
|
103
|
-
* ```
|
|
104
42
|
*/
|
|
105
43
|
export declare const edgesPlugin: (options?: EdgesPluginOptions) => Plugin;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Use this when:
|
|
5
5
|
* - Creating a wrapper package that re-exports edges-svelte functionality
|
|
6
|
-
* - Developing the package itself (use `$lib/server` as serverPath)
|
|
7
6
|
*
|
|
8
7
|
* @param packageName - The name that will be used in generated imports (e.g., 'edges-svelte', 'my-wrapper')
|
|
9
8
|
* @param serverPath - The import path to the server module (e.g., 'edges-svelte/server', '$lib/server')
|
|
@@ -15,18 +14,6 @@
|
|
|
15
14
|
*
|
|
16
15
|
* export const myWrapperPlugin = createEdgesPluginFactory('my-wrapper', 'my-wrapper/server');
|
|
17
16
|
* ```
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* // For package development (testing the package itself)
|
|
22
|
-
* import { createEdgesPluginFactory } from './src/lib/plugin/index.js';
|
|
23
|
-
*
|
|
24
|
-
* const edgesPluginDev = createEdgesPluginFactory('edges-svelte', '$lib/server');
|
|
25
|
-
*
|
|
26
|
-
* export default defineConfig({
|
|
27
|
-
* plugins: [sveltekit(), edgesPluginDev()]
|
|
28
|
-
* });
|
|
29
|
-
* ```
|
|
30
17
|
*/
|
|
31
18
|
export function createEdgesPluginFactory(packageName, serverPath) {
|
|
32
19
|
// Compile regex patterns once per factory (performance optimization)
|
|
@@ -37,46 +24,31 @@ export function createEdgesPluginFactory(packageName, serverPath) {
|
|
|
37
24
|
const { compression = {}, silentChromeDevtools = true } = options || {};
|
|
38
25
|
return {
|
|
39
26
|
name: `${packageName}-auto-handle`,
|
|
40
|
-
enforce: 'pre',
|
|
27
|
+
enforce: 'pre',
|
|
41
28
|
transform(code, id) {
|
|
42
|
-
// Only transform hooks.server.ts
|
|
43
29
|
if (!id.includes('hooks.server.ts'))
|
|
44
30
|
return null;
|
|
45
|
-
// If already wrapped by the plugin, skip
|
|
46
31
|
if (code.includes('__EDGES_AUTO_WRAPPED__'))
|
|
47
32
|
return null;
|
|
48
|
-
// If user is manually using the package, skip auto-wrapping
|
|
49
|
-
// Optimized: Use pre-compiled regex pattern
|
|
50
33
|
if (MANUAL_IMPORT_PATTERN.test(code)) {
|
|
51
34
|
return null;
|
|
52
35
|
}
|
|
53
|
-
// Check if user defined a handle export
|
|
54
|
-
// Optimized: Use pre-compiled regex pattern
|
|
55
36
|
const hasHandleExport = HANDLE_EXPORT_PATTERN.test(code);
|
|
56
|
-
// Build compression options string
|
|
57
37
|
const compressionOptions = compression.enabled ? `, { compress: true, compressionThreshold: ${compression.threshold || 1024} }` : '';
|
|
58
|
-
// Build silent devtools option
|
|
59
38
|
const silentOption = silentChromeDevtools ? '' : `, false`;
|
|
60
|
-
// Find the position after the last import statement to preserve import order
|
|
61
|
-
// Optimized: Use regex instead of line-by-line parsing for 20x performance improvement
|
|
62
39
|
const findImportInsertPosition = (sourceCode) => {
|
|
63
|
-
// Match all import/export statements
|
|
64
40
|
const importRegex = /(?:^|\n)((?:import|export)\s+(?:type\s+)?(?:\{[^}]*\}|\*|\w+)(?:\s+from)?\s+['"][^'"]+['"];?)/gm;
|
|
65
41
|
let lastMatch = null;
|
|
66
42
|
let match;
|
|
67
|
-
// Find the last import/export statement
|
|
68
43
|
while ((match = importRegex.exec(sourceCode)) !== null) {
|
|
69
44
|
lastMatch = match;
|
|
70
45
|
}
|
|
71
46
|
if (!lastMatch) {
|
|
72
|
-
// No imports found, insert at the beginning
|
|
73
47
|
return 0;
|
|
74
48
|
}
|
|
75
|
-
// Return position after the last import
|
|
76
49
|
return lastMatch.index + lastMatch[0].length;
|
|
77
50
|
};
|
|
78
51
|
if (!hasHandleExport) {
|
|
79
|
-
// No handle defined - create default with compression options
|
|
80
52
|
const insertPos = findImportInsertPosition(code);
|
|
81
53
|
const beforeImports = code.slice(0, insertPos);
|
|
82
54
|
const afterImports = code.slice(insertPos);
|
|
@@ -91,7 +63,6 @@ export function createEdgesPluginFactory(packageName, serverPath) {
|
|
|
91
63
|
map: null
|
|
92
64
|
};
|
|
93
65
|
}
|
|
94
|
-
// User defined a handle - wrap it with options
|
|
95
66
|
const insertPos = findImportInsertPosition(code);
|
|
96
67
|
const beforeImports = code.slice(0, insertPos);
|
|
97
68
|
const afterImports = code.slice(insertPos);
|
|
@@ -112,10 +83,8 @@ export function createEdgesPluginFactory(packageName, serverPath) {
|
|
|
112
83
|
};
|
|
113
84
|
}
|
|
114
85
|
/**
|
|
115
|
-
* Default edges-svelte plugin for end users.
|
|
116
86
|
*
|
|
117
87
|
* This plugin automatically wraps the SvelteKit handle hook with edgesHandle,
|
|
118
|
-
* eliminating the need to manually wrap your handle function.
|
|
119
88
|
*
|
|
120
89
|
* @example
|
|
121
90
|
* ```ts
|
|
@@ -128,37 +97,5 @@ export function createEdgesPluginFactory(packageName, serverPath) {
|
|
|
128
97
|
* plugins: [sveltekit(), edgesPlugin()]
|
|
129
98
|
* });
|
|
130
99
|
* ```
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```ts
|
|
134
|
-
* // vite.config.ts - With compression
|
|
135
|
-
* export default defineConfig({
|
|
136
|
-
* plugins: [
|
|
137
|
-
* sveltekit(),
|
|
138
|
-
* edgesPlugin({
|
|
139
|
-
* compression: {
|
|
140
|
-
* enabled: true,
|
|
141
|
-
* threshold: 2048 // Compress states larger than 2KB
|
|
142
|
-
* }
|
|
143
|
-
* })
|
|
144
|
-
* ]
|
|
145
|
-
* });
|
|
146
|
-
* ```
|
|
147
|
-
*
|
|
148
|
-
* After adding the plugin, you can write your hooks.server.ts normally:
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* ```ts
|
|
152
|
-
* // hooks.server.ts - No manual wrapping needed!
|
|
153
|
-
*
|
|
154
|
-
* // Option 1: No handle defined - plugin creates default
|
|
155
|
-
* // (nothing to write, it just works)
|
|
156
|
-
*
|
|
157
|
-
* // Option 2: Custom handle - plugin automatically wraps it
|
|
158
|
-
* export const handle = async ({ event, resolve }) => {
|
|
159
|
-
* console.log('My custom middleware');
|
|
160
|
-
* return resolve(event);
|
|
161
|
-
* };
|
|
162
|
-
* ```
|
|
163
100
|
*/
|
|
164
101
|
export const edgesPlugin = createEdgesPluginFactory('edges-svelte', 'edges-svelte/server');
|
|
@@ -11,52 +11,14 @@ type StoreDeps = {
|
|
|
11
11
|
createState: <T>(initial: T | (() => T)) => Writable<T>;
|
|
12
12
|
createDerivedState: typeof BaseCreateDerivedState;
|
|
13
13
|
};
|
|
14
|
-
/**
|
|
15
|
-
* Creates store with optional name
|
|
16
|
-
* @example
|
|
17
|
-
* // Without key (auto-generated)
|
|
18
|
-
* export const useUserStore = createStore(({ createState }) => {
|
|
19
|
-
* const user = createState(null);
|
|
20
|
-
* return { user };
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* // With explicit key (recommended for production)
|
|
25
|
-
* export const useUserStore = createStore('user-store', ({ createState }) => {
|
|
26
|
-
* const user = createState(null);
|
|
27
|
-
* return { user };
|
|
28
|
-
* });
|
|
29
|
-
*/
|
|
30
14
|
export declare function createStore<T, I extends Record<string, unknown> = Record<string, unknown>>(factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T, inject?: I): () => T;
|
|
31
15
|
export declare function createStore<T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T, inject?: I): () => T;
|
|
32
|
-
/**
|
|
33
|
-
* Store factory
|
|
34
|
-
*/
|
|
35
16
|
export declare const createStoreFactory: <I extends Record<string, unknown>>(inject: I) => {
|
|
36
17
|
<T>(factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T): () => T;
|
|
37
18
|
<T>(name: string, factory: (args: StoreDeps & NoConflict<I, StoreDeps>) => T): () => T;
|
|
38
19
|
};
|
|
39
|
-
/**
|
|
40
|
-
* Creates presenter with optional name
|
|
41
|
-
* @example
|
|
42
|
-
* // Without key (auto-generated)
|
|
43
|
-
* export const useAuthPresenter = createPresenter(() => {
|
|
44
|
-
* const login = async () => { ... };
|
|
45
|
-
* return { login };
|
|
46
|
-
* });
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* // With explicit key
|
|
50
|
-
* export const useAuthPresenter = createPresenter('auth-presenter', () => {
|
|
51
|
-
* const login = async () => { ... };
|
|
52
|
-
* return { login };
|
|
53
|
-
* });
|
|
54
|
-
*/
|
|
55
20
|
export declare function createPresenter<T, I extends Record<string, unknown> = Record<string, unknown>>(factory: (args: I) => T, inject?: I): () => T;
|
|
56
21
|
export declare function createPresenter<T, I extends Record<string, unknown> = Record<string, unknown>>(name: string, factory: (args: I) => T, inject?: I): () => T;
|
|
57
|
-
/**
|
|
58
|
-
* Presenter factory
|
|
59
|
-
*/
|
|
60
22
|
export declare const createPresenterFactory: <I extends Record<string, unknown>>(inject: I) => {
|
|
61
23
|
<T>(factory: (args: I) => T): () => T;
|
|
62
24
|
<T>(name: string, factory: (args: I) => T): () => T;
|
|
@@ -2,9 +2,7 @@ import { createState as BaseCreateState, createDerivedState as BaseCreateDerived
|
|
|
2
2
|
import { RequestContext } from '../context/index.js';
|
|
3
3
|
import { browser } from '../utils/environment.js';
|
|
4
4
|
import { DevTools } from '../utils/dev.js';
|
|
5
|
-
// Global client cache
|
|
6
5
|
const globalClientCache = new Map();
|
|
7
|
-
// Key auto-generate
|
|
8
6
|
class AutoKeyGenerator {
|
|
9
7
|
static cache = new WeakMap();
|
|
10
8
|
static counters = new Map();
|
|
@@ -30,9 +28,6 @@ class AutoKeyGenerator {
|
|
|
30
28
|
}
|
|
31
29
|
if (cache.has(factory))
|
|
32
30
|
return cache.get(factory);
|
|
33
|
-
// Use multiple sources for stable key generation
|
|
34
|
-
// Priority: displayName > name > toString
|
|
35
|
-
// This ensures stability even with minification
|
|
36
31
|
const fnIdentifier = factory.__storeKey__ || factory.displayName || factory.name || factory.toString();
|
|
37
32
|
const hash = this.hash(fnIdentifier);
|
|
38
33
|
const baseKey = `store_${Math.abs(hash).toString(36)}`;
|
|
@@ -46,17 +41,14 @@ class AutoKeyGenerator {
|
|
|
46
41
|
counters.set(baseKey, 0);
|
|
47
42
|
}
|
|
48
43
|
cache.set(factory, finalKey);
|
|
49
|
-
// Dev mode validation
|
|
50
44
|
DevTools.validateFactoryUniqueness(factory, finalKey);
|
|
51
45
|
return finalKey;
|
|
52
46
|
}
|
|
53
47
|
static hash(str) {
|
|
54
|
-
|
|
55
|
-
// Provides 50% fewer collisions for typical factory names
|
|
56
|
-
let hash = 2166136261; // FNV offset basis (32-bit)
|
|
48
|
+
let hash = 2166136261;
|
|
57
49
|
for (let i = 0; i < str.length; i++) {
|
|
58
|
-
hash = (hash ^ str.charCodeAt(i)) * 16777619;
|
|
59
|
-
hash = hash >>> 0;
|
|
50
|
+
hash = (hash ^ str.charCodeAt(i)) * 16777619;
|
|
51
|
+
hash = hash >>> 0;
|
|
60
52
|
}
|
|
61
53
|
return hash;
|
|
62
54
|
}
|
|
@@ -109,12 +101,10 @@ const createUiProvider = (cacheKey, factory, dependencies, inject) => {
|
|
|
109
101
|
};
|
|
110
102
|
};
|
|
111
103
|
export function createStore(nameOrFactory, factoryOrInject, inject) {
|
|
112
|
-
// Handle overloads
|
|
113
104
|
const isNameProvided = typeof nameOrFactory === 'string';
|
|
114
105
|
const name = isNameProvided ? nameOrFactory : undefined;
|
|
115
106
|
const factory = isNameProvided ? factoryOrInject : nameOrFactory;
|
|
116
107
|
const injections = isNameProvided ? inject : factoryOrInject;
|
|
117
|
-
// Use provided name or auto-generate
|
|
118
108
|
const cacheKey = name || AutoKeyGenerator.generate(factory);
|
|
119
109
|
return createUiProvider(cacheKey, factory, (key) => {
|
|
120
110
|
let stateCounter = 0;
|
|
@@ -133,9 +123,6 @@ export function createStore(nameOrFactory, factoryOrInject, inject) {
|
|
|
133
123
|
};
|
|
134
124
|
}, injections);
|
|
135
125
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Store factory
|
|
138
|
-
*/
|
|
139
126
|
export const createStoreFactory = (inject) => {
|
|
140
127
|
function storeFactory(nameOrFactory, factory) {
|
|
141
128
|
if (typeof nameOrFactory === 'string') {
|
|
@@ -148,18 +135,13 @@ export const createStoreFactory = (inject) => {
|
|
|
148
135
|
return storeFactory;
|
|
149
136
|
};
|
|
150
137
|
export function createPresenter(nameOrFactory, factoryOrInject, inject) {
|
|
151
|
-
// Handle overloads
|
|
152
138
|
const isNameProvided = typeof nameOrFactory === 'string';
|
|
153
139
|
const name = isNameProvided ? nameOrFactory : undefined;
|
|
154
140
|
const factory = isNameProvided ? factoryOrInject : nameOrFactory;
|
|
155
141
|
const injections = isNameProvided ? inject : factoryOrInject;
|
|
156
|
-
// Use provided name or auto-generate
|
|
157
142
|
const cacheKey = name || AutoKeyGenerator.generate(factory);
|
|
158
143
|
return createUiProvider(cacheKey, factory, {}, injections);
|
|
159
144
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Presenter factory
|
|
162
|
-
*/
|
|
163
145
|
export const createPresenterFactory = (inject) => {
|
|
164
146
|
function presenterFactory(nameOrFactory, factory) {
|
|
165
147
|
if (typeof nameOrFactory === 'string') {
|
|
@@ -12,21 +12,17 @@ import { edgesHandle } from './EdgesHandleSimplified.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export function __autoWrapHandle(userHandle, compressionOptions, silentChromeDevtools = true) {
|
|
14
14
|
if (!userHandle) {
|
|
15
|
-
// No user handle - return default edgesHandle with compression options
|
|
16
15
|
return edgesHandle(({ serialize, edgesEvent, resolve }) => resolve(edgesEvent, {
|
|
17
16
|
transformPageChunk: ({ html }) => serialize(html, compressionOptions)
|
|
18
17
|
}), silentChromeDevtools);
|
|
19
18
|
}
|
|
20
|
-
// Wrap user's handle with edgesHandle
|
|
21
19
|
return edgesHandle(({ serialize, edgesEvent, resolve }) => {
|
|
22
20
|
return userHandle({
|
|
23
21
|
event: edgesEvent,
|
|
24
22
|
resolve: (e, opts) => resolve(e, {
|
|
25
23
|
...opts,
|
|
26
24
|
transformPageChunk: ({ html, done }) => {
|
|
27
|
-
// Apply user's transform first (if any)
|
|
28
25
|
const userTransformed = opts?.transformPageChunk?.({ html, done }) ?? html;
|
|
29
|
-
// Then apply edges serialization with compression options
|
|
30
26
|
return typeof userTransformed === 'string' ? serialize(userTransformed, compressionOptions) : userTransformed;
|
|
31
27
|
}
|
|
32
28
|
})
|
|
@@ -7,8 +7,5 @@ type EdgesHandle = (event: RequestEvent, callback: (params: {
|
|
|
7
7
|
edgesEvent: RequestEvent;
|
|
8
8
|
serialize: (html: string, options?: SerializeOptions) => string;
|
|
9
9
|
}) => Promise<Response> | Response, silentChromeDevtools?: boolean) => Promise<Response>;
|
|
10
|
-
/**
|
|
11
|
-
* Wraps request handling in an AsyncLocalStorage context
|
|
12
|
-
*/
|
|
13
10
|
export declare const edgesHandle: EdgesHandle;
|
|
14
11
|
export {};
|
|
@@ -14,9 +14,6 @@ const safeReplacer = (key, value) => {
|
|
|
14
14
|
}
|
|
15
15
|
return value;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
* Wraps request handling in an AsyncLocalStorage context
|
|
19
|
-
*/
|
|
20
17
|
export const edgesHandle = async (event, callback, silentChromeDevtools = false) => {
|
|
21
18
|
const requestSymbol = Symbol('request');
|
|
22
19
|
return await storage.run({
|
|
@@ -89,7 +86,6 @@ export const edgesHandle = async (event, callback, silentChromeDevtools = false)
|
|
|
89
86
|
}
|
|
90
87
|
catch (e) {
|
|
91
88
|
console.error('[edges] Failed to inject state into JSON response:', e);
|
|
92
|
-
// Original response
|
|
93
89
|
return response;
|
|
94
90
|
}
|
|
95
91
|
}
|
|
@@ -9,16 +9,8 @@ type SimplifiedCallback = (params: {
|
|
|
9
9
|
resolve: (event: RequestEvent, opts?: ResolveOptions) => Response | Promise<Response>;
|
|
10
10
|
}) => Response | Promise<Response>;
|
|
11
11
|
/**
|
|
12
|
-
* Simplified wrapper around edgesHandle that provides a more convenient API.
|
|
13
|
-
*
|
|
14
12
|
* @example
|
|
15
13
|
* ```ts
|
|
16
|
-
* // Simple usage with default behavior
|
|
17
|
-
* export const handle = edgesHandle(({ serialize, edgesEvent, resolve }) =>
|
|
18
|
-
* resolve(edgesEvent, { transformPageChunk: ({ html }) => serialize(html) })
|
|
19
|
-
* );
|
|
20
|
-
*
|
|
21
|
-
* // You can still access resolve for custom logic
|
|
22
14
|
* export const handle = edgesHandle(({ serialize, edgesEvent, resolve }) => {
|
|
23
15
|
* // Custom logic here
|
|
24
16
|
* return resolve(edgesEvent, { transformPageChunk: ({ html }) => serialize(html) });
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { edgesHandle as originalEdgesHandle } from './EdgesHandle.js';
|
|
2
2
|
/**
|
|
3
|
-
* Simplified wrapper around edgesHandle that provides a more convenient API.
|
|
4
|
-
*
|
|
5
3
|
* @example
|
|
6
4
|
* ```ts
|
|
7
|
-
* // Simple usage with default behavior
|
|
8
|
-
* export const handle = edgesHandle(({ serialize, edgesEvent, resolve }) =>
|
|
9
|
-
* resolve(edgesEvent, { transformPageChunk: ({ html }) => serialize(html) })
|
|
10
|
-
* );
|
|
11
|
-
*
|
|
12
|
-
* // You can still access resolve for custom logic
|
|
13
5
|
* export const handle = edgesHandle(({ serialize, edgesEvent, resolve }) => {
|
|
14
6
|
* // Custom logic here
|
|
15
7
|
* return resolve(edgesEvent, { transformPageChunk: ({ html }) => serialize(html) });
|
|
@@ -5,7 +5,6 @@ import { registerStateUpdate } from '../client/NavigationSync.svelte.js';
|
|
|
5
5
|
const RequestStores = new WeakMap();
|
|
6
6
|
const UNDEFINED_MARKER = '__EDGES_UNDEFINED__';
|
|
7
7
|
const NULL_MARKER = '__EDGES_NULL__';
|
|
8
|
-
// Pre-compiled reviver code for better performance (cached once instead of regenerated per request)
|
|
9
8
|
const REVIVER_CODE = `window.__EDGES_REVIVER__=function(k,v){if(v&&typeof v==='object'){if('${UNDEFINED_MARKER}' in v)return undefined;if('${NULL_MARKER}' in v)return null}return v};`;
|
|
10
9
|
const safeReplacer = (key, value) => {
|
|
11
10
|
if (value === undefined) {
|
|
@@ -19,17 +18,6 @@ const safeReplacer = (key, value) => {
|
|
|
19
18
|
}
|
|
20
19
|
return value;
|
|
21
20
|
};
|
|
22
|
-
// const safeReviver = (key: string, value: unknown): unknown => {
|
|
23
|
-
// if (value && typeof value === 'object') {
|
|
24
|
-
// if (UNDEFINED_MARKER in value) {
|
|
25
|
-
// return undefined;
|
|
26
|
-
// }
|
|
27
|
-
// if (NULL_MARKER in value) {
|
|
28
|
-
// return null;
|
|
29
|
-
// }
|
|
30
|
-
// }
|
|
31
|
-
// return value;
|
|
32
|
-
// };
|
|
33
21
|
export const stateSerialize = (options) => {
|
|
34
22
|
const map = getRequestContext();
|
|
35
23
|
if (!map || map.size === 0)
|
|
@@ -39,10 +27,7 @@ export const stateSerialize = (options) => {
|
|
|
39
27
|
const threshold = options?.threshold ?? 1024; // 1KB default
|
|
40
28
|
for (const [key, value] of map) {
|
|
41
29
|
const serialized = JSON.stringify(value, safeReplacer);
|
|
42
|
-
// Check if we should compress this entry
|
|
43
30
|
if (shouldCompress && serialized.length > threshold) {
|
|
44
|
-
// For large values, use base64 encoding as a simple compression
|
|
45
|
-
// Optimized: Use Array.from instead of reduce for 10x performance improvement
|
|
46
31
|
const bytes = new TextEncoder().encode(serialized);
|
|
47
32
|
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');
|
|
48
33
|
const encoded = btoa(binary);
|
|
@@ -55,12 +40,10 @@ export const stateSerialize = (options) => {
|
|
|
55
40
|
}`);
|
|
56
41
|
}
|
|
57
42
|
else {
|
|
58
|
-
// Optimized: Single-pass escape instead of two separate replace calls
|
|
59
43
|
const escaped = serialized.replace(/[\\']/g, (ch) => '\\' + ch);
|
|
60
44
|
entries.push(`window.__SAFE_SSR_STATE__.set('${key}',JSON.parse('${escaped}',window.__EDGES_REVIVER__))`);
|
|
61
45
|
}
|
|
62
46
|
}
|
|
63
|
-
// Use pre-compiled reviver code for better performance
|
|
64
47
|
return `<script>${REVIVER_CODE}window.__SAFE_SSR_STATE__=new Map();${entries.join(';')}</script>`;
|
|
65
48
|
};
|
|
66
49
|
const getRequestContext = () => {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
import type { Writable, Readable } from 'svelte/store';
|
|
2
|
-
/**
|
|
3
|
-
* Configuration for store creation with minification resistance
|
|
4
|
-
*/
|
|
5
2
|
export interface StoreConfig {
|
|
6
|
-
/**
|
|
7
|
-
* Explicit key for the store to avoid minification issues
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* const factory = (deps) => { ... };
|
|
11
|
-
* factory.__storeKey__ = 'userStore';
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
3
|
__storeKey__?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Display name for debugging
|
|
17
|
-
*/
|
|
18
4
|
displayName?: string;
|
|
19
5
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Store factory function with configuration
|
|
22
|
-
*/
|
|
23
6
|
export type StoreFactory<T, I = Record<string, unknown>> = ((args: {
|
|
24
7
|
createState: <T>(initial: T | (() => T)) => Writable<T>;
|
|
25
8
|
createRawState: <T>(initial: T | (() => T)) => {
|
|
@@ -27,89 +10,26 @@ export type StoreFactory<T, I = Record<string, unknown>> = ((args: {
|
|
|
27
10
|
};
|
|
28
11
|
createDerivedState: <T extends Readable<unknown>[], D>(stores: T, deriveFn: (values: unknown[]) => D) => Readable<D>;
|
|
29
12
|
} & I) => T) & StoreConfig;
|
|
30
|
-
/**
|
|
31
|
-
* Presenter factory function with configuration
|
|
32
|
-
*/
|
|
33
13
|
export type PresenterFactory<T, I = Record<string, unknown>> = ((args: I) => T) & StoreConfig;
|
|
34
|
-
/**
|
|
35
|
-
* Options for batched state updates
|
|
36
|
-
*/
|
|
37
14
|
export interface BatchOptions {
|
|
38
|
-
/**
|
|
39
|
-
* Whether to defer DOM updates to the next microtask
|
|
40
|
-
* @default true
|
|
41
|
-
*/
|
|
42
15
|
defer?: boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Callback when batch completes
|
|
45
|
-
*/
|
|
46
16
|
onComplete?: () => void;
|
|
47
17
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Compression options for state serialization
|
|
50
|
-
*/
|
|
51
18
|
export interface CompressionOptions {
|
|
52
|
-
/**
|
|
53
|
-
* Enable compression for large state objects
|
|
54
|
-
* @default false
|
|
55
|
-
*/
|
|
56
19
|
enabled?: boolean;
|
|
57
|
-
/**
|
|
58
|
-
* Minimum size in bytes before compression is applied
|
|
59
|
-
* @default 1024 (1KB)
|
|
60
|
-
*/
|
|
61
20
|
threshold?: number;
|
|
62
|
-
/**
|
|
63
|
-
* Compression algorithm to use
|
|
64
|
-
* @default 'base64'
|
|
65
|
-
*/
|
|
66
21
|
algorithm?: 'base64' | 'gzip' | 'brotli';
|
|
67
22
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Development tools configuration
|
|
70
|
-
*/
|
|
71
23
|
export interface DevToolsConfig {
|
|
72
|
-
/**
|
|
73
|
-
* Enable development mode validations
|
|
74
|
-
* @default true in dev, false in production
|
|
75
|
-
*/
|
|
76
24
|
enabled?: boolean;
|
|
77
|
-
/**
|
|
78
|
-
* Warn about large state objects
|
|
79
|
-
* @default true
|
|
80
|
-
*/
|
|
81
25
|
warnLargeState?: boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Size threshold for large state warnings (in bytes)
|
|
84
|
-
* @default 50000 (50KB)
|
|
85
|
-
*/
|
|
86
26
|
largeStateThreshold?: number;
|
|
87
|
-
/**
|
|
88
|
-
* Check for direct state mutations
|
|
89
|
-
* @default true
|
|
90
|
-
*/
|
|
91
27
|
checkMutations?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Log performance metrics
|
|
94
|
-
* @default false
|
|
95
|
-
*/
|
|
96
28
|
logPerformance?: boolean;
|
|
97
29
|
}
|
|
98
|
-
/**
|
|
99
|
-
* Global configuration for edges-svelte
|
|
100
|
-
*/
|
|
101
30
|
export interface EdgesConfig {
|
|
102
|
-
/**
|
|
103
|
-
* Development tools configuration
|
|
104
|
-
*/
|
|
105
31
|
devTools?: DevToolsConfig;
|
|
106
|
-
/**
|
|
107
|
-
* Default compression options for state serialization
|
|
108
|
-
*/
|
|
109
32
|
compression?: CompressionOptions;
|
|
110
|
-
/**
|
|
111
|
-
* Default batch options
|
|
112
|
-
*/
|
|
113
33
|
batch?: BatchOptions;
|
|
114
34
|
}
|
|
115
35
|
export type UnknownFunc = {
|
package/dist/utils/batch.d.ts
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Batch multiple state updates to avoid unnecessary re-renders
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```ts
|
|
6
|
-
* batch(() => {
|
|
7
|
-
* store1.set(value1);
|
|
8
|
-
* store2.set(value2);
|
|
9
|
-
* store3.set(value3);
|
|
10
|
-
* });
|
|
11
|
-
* ```
|
|
12
|
-
*/
|
|
13
1
|
export declare const batch: (fn: () => void) => void;
|
|
14
|
-
/**
|
|
15
|
-
* Queue an update for batching (internal use)
|
|
16
|
-
*/
|
|
17
2
|
export declare const queueUpdate: (key: string, value: unknown, callback?: (value: unknown) => void) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Register a callback for batch completion (internal use)
|
|
20
|
-
*/
|
|
21
3
|
export declare const onBatchComplete: (callback: () => void) => void;
|
|
22
|
-
/**
|
|
23
|
-
* Transaction-like API for complex state updates
|
|
24
|
-
*/
|
|
25
4
|
export declare const transaction: <T>(fn: () => Promise<T>) => Promise<T>;
|
package/dist/utils/batch.js
CHANGED
|
@@ -3,16 +3,11 @@ class BatchManager {
|
|
|
3
3
|
pendingUpdates = new Map();
|
|
4
4
|
scheduled = false;
|
|
5
5
|
batchCallbacks = new Set();
|
|
6
|
-
/**
|
|
7
|
-
* Batch multiple state updates into a single render cycle
|
|
8
|
-
*/
|
|
9
6
|
batch(fn) {
|
|
10
7
|
if (!browser) {
|
|
11
|
-
// On server, execute immediately
|
|
12
8
|
fn();
|
|
13
9
|
return;
|
|
14
10
|
}
|
|
15
|
-
// Collect all updates
|
|
16
11
|
const startBatching = !this.scheduled;
|
|
17
12
|
if (startBatching) {
|
|
18
13
|
this.scheduled = true;
|
|
@@ -26,44 +21,31 @@ class BatchManager {
|
|
|
26
21
|
}
|
|
27
22
|
}
|
|
28
23
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Add an update to the batch queue
|
|
31
|
-
*/
|
|
32
24
|
queueUpdate(key, value, callback) {
|
|
33
25
|
if (!browser || !this.scheduled) {
|
|
34
|
-
// Execute immediately if not batching
|
|
35
26
|
if (callback)
|
|
36
27
|
callback(value);
|
|
37
28
|
return;
|
|
38
29
|
}
|
|
39
30
|
this.pendingUpdates.set(key, { key, value, callback });
|
|
40
31
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Register a callback to be called when batch completes
|
|
43
|
-
*/
|
|
44
32
|
onBatchComplete(callback) {
|
|
45
33
|
this.batchCallbacks.add(callback);
|
|
46
34
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Flush all pending updates
|
|
49
|
-
*/
|
|
50
35
|
flush() {
|
|
51
36
|
if (this.pendingUpdates.size === 0) {
|
|
52
37
|
this.scheduled = false;
|
|
53
38
|
return;
|
|
54
39
|
}
|
|
55
|
-
// Use microtask to batch DOM updates
|
|
56
40
|
queueMicrotask(() => {
|
|
57
41
|
const updates = Array.from(this.pendingUpdates.values());
|
|
58
42
|
this.pendingUpdates.clear();
|
|
59
43
|
this.scheduled = false;
|
|
60
|
-
// Apply all updates
|
|
61
44
|
for (const update of updates) {
|
|
62
45
|
if (update.callback) {
|
|
63
46
|
update.callback(update.value);
|
|
64
47
|
}
|
|
65
48
|
}
|
|
66
|
-
// Notify batch complete
|
|
67
49
|
for (const callback of this.batchCallbacks) {
|
|
68
50
|
callback();
|
|
69
51
|
}
|
|
@@ -71,32 +53,10 @@ class BatchManager {
|
|
|
71
53
|
});
|
|
72
54
|
}
|
|
73
55
|
}
|
|
74
|
-
// Global batch manager instance
|
|
75
56
|
const batchManager = new BatchManager();
|
|
76
|
-
/**
|
|
77
|
-
* Batch multiple state updates to avoid unnecessary re-renders
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```ts
|
|
81
|
-
* batch(() => {
|
|
82
|
-
* store1.set(value1);
|
|
83
|
-
* store2.set(value2);
|
|
84
|
-
* store3.set(value3);
|
|
85
|
-
* });
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
57
|
export const batch = (fn) => batchManager.batch(fn);
|
|
89
|
-
/**
|
|
90
|
-
* Queue an update for batching (internal use)
|
|
91
|
-
*/
|
|
92
58
|
export const queueUpdate = (key, value, callback) => batchManager.queueUpdate(key, value, callback);
|
|
93
|
-
/**
|
|
94
|
-
* Register a callback for batch completion (internal use)
|
|
95
|
-
*/
|
|
96
59
|
export const onBatchComplete = (callback) => batchManager.onBatchComplete(callback);
|
|
97
|
-
/**
|
|
98
|
-
* Transaction-like API for complex state updates
|
|
99
|
-
*/
|
|
100
60
|
export const transaction = async (fn) => {
|
|
101
61
|
return new Promise((resolve, reject) => {
|
|
102
62
|
batch(() => {
|
package/dist/utils/dev.d.ts
CHANGED
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
import type { UnknownFunc } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Development-mode validations and warnings
|
|
4
|
-
*/
|
|
5
2
|
export declare const DevTools: {
|
|
6
|
-
/**
|
|
7
|
-
* Validates factory uniqueness in development
|
|
8
|
-
*/
|
|
9
3
|
validateFactoryUniqueness(factory: UnknownFunc, key: string): void;
|
|
10
|
-
/**
|
|
11
|
-
* Get cached size of a value (optimized to avoid repeated JSON.stringify)
|
|
12
|
-
*/
|
|
13
4
|
getSize(value: unknown): number;
|
|
14
|
-
/**
|
|
15
|
-
* Warns about large state objects (optimized with memoization)
|
|
16
|
-
*/
|
|
17
5
|
warnOnLargeState(key: string, value: unknown): void;
|
|
18
|
-
/**
|
|
19
|
-
* Validates state mutations
|
|
20
|
-
*/
|
|
21
6
|
checkStateMutation(key: string, oldValue: unknown, newValue: unknown): void;
|
|
22
|
-
/**
|
|
23
|
-
* Performance metrics collection
|
|
24
|
-
*/
|
|
25
7
|
measurePerformance<T>(name: string, fn: () => T): T;
|
|
26
|
-
/**
|
|
27
|
-
* Debug state tree visualization
|
|
28
|
-
*/
|
|
29
8
|
visualizeStateTree(stateMap: Map<string, unknown>): void;
|
|
30
9
|
};
|
package/dist/utils/dev.js
CHANGED
|
@@ -1,28 +1,18 @@
|
|
|
1
1
|
import { browser, dev } from './environment.js';
|
|
2
2
|
const seenFactories = new WeakSet();
|
|
3
3
|
const largeStateWarnings = new Set();
|
|
4
|
-
// Memoize size calculations to avoid repeated JSON.stringify calls
|
|
5
4
|
const sizeCache = new WeakMap();
|
|
6
|
-
/**
|
|
7
|
-
* Development-mode validations and warnings
|
|
8
|
-
*/
|
|
9
5
|
export const DevTools = {
|
|
10
|
-
/**
|
|
11
|
-
* Validates factory uniqueness in development
|
|
12
|
-
*/
|
|
13
6
|
validateFactoryUniqueness(factory, key) {
|
|
14
7
|
if (!dev)
|
|
15
8
|
return;
|
|
16
9
|
if (seenFactories.has(factory)) {
|
|
17
10
|
console.warn(`[edges-svelte] Factory collision detected for key "${key}". ` +
|
|
18
|
-
`This might cause unexpected behavior
|
|
19
|
-
`
|
|
11
|
+
`This might cause unexpected behavior.` +
|
|
12
|
+
`Set a unique __storeKey__ property on your factory function.`);
|
|
20
13
|
}
|
|
21
14
|
seenFactories.add(factory);
|
|
22
15
|
},
|
|
23
|
-
/**
|
|
24
|
-
* Get cached size of a value (optimized to avoid repeated JSON.stringify)
|
|
25
|
-
*/
|
|
26
16
|
getSize(value) {
|
|
27
17
|
if (typeof value === 'object' && value !== null) {
|
|
28
18
|
if (sizeCache.has(value)) {
|
|
@@ -34,9 +24,6 @@ export const DevTools = {
|
|
|
34
24
|
}
|
|
35
25
|
return JSON.stringify(value).length;
|
|
36
26
|
},
|
|
37
|
-
/**
|
|
38
|
-
* Warns about large state objects (optimized with memoization)
|
|
39
|
-
*/
|
|
40
27
|
warnOnLargeState(key, value) {
|
|
41
28
|
if (!dev)
|
|
42
29
|
return;
|
|
@@ -45,7 +32,6 @@ export const DevTools = {
|
|
|
45
32
|
try {
|
|
46
33
|
const size = this.getSize(value);
|
|
47
34
|
if (size > 50000) {
|
|
48
|
-
// 50KB threshold
|
|
49
35
|
largeStateWarnings.add(key);
|
|
50
36
|
console.warn(`[edges-svelte] Large state detected for key "${key}" (${Math.round(size / 1024)}KB). ` +
|
|
51
37
|
`Consider splitting into smaller stores or enabling compression.`);
|
|
@@ -55,21 +41,14 @@ export const DevTools = {
|
|
|
55
41
|
// Ignore serialization errors in dev warnings
|
|
56
42
|
}
|
|
57
43
|
},
|
|
58
|
-
/**
|
|
59
|
-
* Validates state mutations
|
|
60
|
-
*/
|
|
61
44
|
checkStateMutation(key, oldValue, newValue) {
|
|
62
45
|
if (!dev || !browser)
|
|
63
46
|
return;
|
|
64
|
-
// Check for direct object mutations
|
|
65
47
|
if (typeof oldValue === 'object' && oldValue !== null && oldValue === newValue && !Array.isArray(oldValue)) {
|
|
66
48
|
console.error(`[edges-svelte] Direct mutation detected for key "${key}". ` +
|
|
67
49
|
`State should be immutable. Use spread operator or Object.assign() to create new objects.`);
|
|
68
50
|
}
|
|
69
51
|
},
|
|
70
|
-
/**
|
|
71
|
-
* Performance metrics collection
|
|
72
|
-
*/
|
|
73
52
|
measurePerformance(name, fn) {
|
|
74
53
|
if (!dev)
|
|
75
54
|
return fn();
|
|
@@ -77,14 +56,10 @@ export const DevTools = {
|
|
|
77
56
|
const result = fn();
|
|
78
57
|
const duration = performance.now() - start;
|
|
79
58
|
if (duration > 16) {
|
|
80
|
-
// Longer than a frame
|
|
81
59
|
console.warn(`[edges-svelte] Slow operation "${name}" took ${duration.toFixed(2)}ms. ` + `Consider optimizing for better performance.`);
|
|
82
60
|
}
|
|
83
61
|
return result;
|
|
84
62
|
},
|
|
85
|
-
/**
|
|
86
|
-
* Debug state tree visualization
|
|
87
|
-
*/
|
|
88
63
|
visualizeStateTree(stateMap) {
|
|
89
64
|
if (!dev || !browser)
|
|
90
65
|
return;
|
|
@@ -105,7 +80,6 @@ export const DevTools = {
|
|
|
105
80
|
console.groupEnd();
|
|
106
81
|
}
|
|
107
82
|
};
|
|
108
|
-
// Export for browser devtools integration
|
|
109
83
|
if (browser && dev) {
|
|
110
84
|
window.__EDGES_DEVTOOLS__ = {
|
|
111
85
|
version: '1.3.0',
|
|
@@ -127,7 +101,6 @@ if (browser && dev) {
|
|
|
127
101
|
const sizes = {};
|
|
128
102
|
for (const [key, value] of stateMap) {
|
|
129
103
|
try {
|
|
130
|
-
// Use memoized getSize for better performance
|
|
131
104
|
const size = DevTools.getSize(value);
|
|
132
105
|
sizes[key] = size;
|
|
133
106
|
totalSize += size;
|
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Universal browser detection that works in both SvelteKit and non-SvelteKit environments
|
|
3
|
-
* This fixes SSR issues when the package is used in different contexts
|
|
4
|
-
*/
|
|
5
1
|
export const browser = !import.meta.env.SSR && typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
6
2
|
export const dev = import.meta.env.DEV;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edges-svelte",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Pixel1917",
|
|
6
6
|
"description": "A blazing-fast, extremely lightweight and SSR-friendly store for Svelte",
|
|
@@ -107,9 +107,6 @@
|
|
|
107
107
|
"state management",
|
|
108
108
|
"ssr store"
|
|
109
109
|
],
|
|
110
|
-
"dependencies": {
|
|
111
|
-
"devalue": "^5.1.1"
|
|
112
|
-
},
|
|
113
110
|
"scripts": {
|
|
114
111
|
"dev": "vite dev",
|
|
115
112
|
"build": "vite build && npm run prepack",
|