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 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 // Only direct children, not entire subtree
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', // Run before SvelteKit
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
- // FNV-1a hash algorithm - better distribution than simple sum hash
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; // FNV prime
59
- hash = hash >>> 0; // Convert to unsigned 32-bit integer
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 = {
@@ -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>;
@@ -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(() => {
@@ -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. Consider using createNamedStore() ` +
19
- `or setting a unique __storeKey__ property on your factory function.`);
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 declare const browser: boolean;
6
2
  export declare const dev: boolean;
@@ -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.0",
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",