native-document 1.0.166 → 1.0.168
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/.vitepress/config.js +166 -0
- package/CHANGELOG.md +153 -0
- package/components.js +2 -1
- package/dist/native-document.components.min.js +495 -228
- package/dist/native-document.dev.js +7 -0
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/advanced-components.md +213 -608
- package/docs/anchor.md +173 -312
- package/docs/cache.md +95 -803
- package/docs/cli.md +179 -0
- package/docs/components/accordion.md +172 -0
- package/docs/components/alert.md +99 -0
- package/docs/components/avatar.md +160 -0
- package/docs/components/badge.md +102 -0
- package/docs/components/breadcrumb.md +89 -0
- package/docs/components/button.md +183 -0
- package/docs/components/card.md +69 -0
- package/docs/components/context-menu.md +118 -0
- package/docs/components/data-table.md +345 -0
- package/docs/components/dropdown.md +214 -0
- package/docs/components/form/autocomplete-field.md +81 -0
- package/docs/components/form/checkbox-field.md +41 -0
- package/docs/components/form/checkbox-group-field.md +54 -0
- package/docs/components/form/color-field.md +64 -0
- package/docs/components/form/date-field.md +92 -0
- package/docs/components/form/field-collection.md +63 -0
- package/docs/components/form/file-field.md +203 -0
- package/docs/components/form/form-control.md +87 -0
- package/docs/components/form/image-field.md +90 -0
- package/docs/components/form/index.md +115 -0
- package/docs/components/form/number-field.md +65 -0
- package/docs/components/form/radio-field.md +51 -0
- package/docs/components/form/select-field.md +123 -0
- package/docs/components/form/slider.md +136 -0
- package/docs/components/form/string-field.md +134 -0
- package/docs/components/form/textarea-field.md +65 -0
- package/docs/components/form-fields.md +372 -0
- package/docs/components/getting-started.md +264 -0
- package/docs/components/index.md +337 -0
- package/docs/components/layout.md +279 -0
- package/docs/components/list.md +73 -0
- package/docs/components/menu.md +215 -0
- package/docs/components/modal.md +156 -0
- package/docs/components/pagination.md +95 -0
- package/docs/components/popover.md +131 -0
- package/docs/components/progress.md +111 -0
- package/docs/components/shortcut-manager.md +221 -0
- package/docs/components/simple-table.md +107 -0
- package/docs/components/skeleton.md +155 -0
- package/docs/components/spinner.md +100 -0
- package/docs/components/splitter.md +133 -0
- package/docs/components/stepper.md +163 -0
- package/docs/components/switch.md +113 -0
- package/docs/components/tabs.md +153 -0
- package/docs/components/toast.md +119 -0
- package/docs/components/tooltip.md +151 -0
- package/docs/components/traits.md +261 -0
- package/docs/conditional-rendering.md +170 -588
- package/docs/contributing.md +300 -25
- package/docs/core-concepts.md +205 -374
- package/docs/elements.md +251 -367
- package/docs/extending-native-document-element.md +192 -207
- package/docs/filters.md +153 -1122
- package/docs/getting-started.md +193 -267
- package/docs/i18n.md +241 -0
- package/docs/index.md +76 -0
- package/docs/lifecycle-events.md +143 -75
- package/docs/list-rendering.md +227 -852
- package/docs/memory-management.md +134 -47
- package/docs/native-document-element.md +337 -186
- package/docs/native-fetch.md +99 -630
- package/docs/observable-resource.md +364 -0
- package/docs/observables.md +592 -526
- package/docs/routing.md +244 -653
- package/docs/state-management.md +134 -241
- package/docs/svg-elements.md +231 -0
- package/docs/theming.md +409 -0
- package/docs/tutorials/.gitkeep +0 -0
- package/docs/validation.md +95 -97
- package/docs/vitepress-conventions.md +219 -0
- package/package.json +34 -13
- package/readme.md +269 -89
- package/src/components/card/Card.js +93 -39
- package/src/components/card/index.js +1 -1
- package/src/components/list/HasListItem.js +171 -0
- package/src/components/list/List.js +41 -107
- package/src/components/list/ListDivider.js +39 -0
- package/src/components/list/ListGroup.js +76 -59
- package/src/components/list/ListItem.js +117 -69
- package/src/components/list/index.js +3 -1
- package/src/components/list/types/ListItem.d.ts +45 -34
- package/src/components/spacer/Spacer.js +1 -1
- package/src/core/data/ObservableResource.js +5 -0
- package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
- package/src/ui/components/card/CardRender.js +133 -0
- package/src/ui/components/card/card.css +169 -0
- package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
- package/src/ui/components/list/ListRender.js +18 -0
- package/src/ui/components/list/divider/ListDividerRender.js +10 -0
- package/src/ui/components/list/divider/list-divider.css +12 -0
- package/src/ui/components/list/group/ListGroupRender.js +61 -0
- package/src/ui/components/list/group/list-group.css +62 -0
- package/src/ui/components/list/item/ListItemRender.js +238 -0
- package/src/ui/components/list/item/list-item.css +191 -0
- package/src/ui/components/list/list.css +24 -0
- package/src/ui/components/spacer/SpacerRender.js +10 -0
- package/src/ui/index.js +8 -0
package/docs/cache.md
CHANGED
|
@@ -1,888 +1,180 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Cache
|
|
3
|
+
description: Lazy initialization, singleton patterns, and key-based memoization utilities
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# Cache
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
NativeDocument provides three caching utilities for optimizing function execution: lazy initialization, eager singletons, and key-based memoization.
|
|
6
9
|
|
|
7
|
-
The Cache utilities include:
|
|
8
|
-
- **`Cache.once(fn)`** - Lazy execution (via `autoOnce`) - executes on first property access
|
|
9
|
-
- **`Cache.singleton(fn)`** - Eager singleton (via `once`) - executes immediately on first call
|
|
10
|
-
- **`Cache.memoize(fn)`** - Lazy memoization (via `autoMemoize`) - proxy-based caching
|
|
11
|
-
|
|
12
|
-
## Import
|
|
13
10
|
```javascript
|
|
14
|
-
import {
|
|
11
|
+
import { utils } from 'native-document';
|
|
12
|
+
const { Cache } = utils;
|
|
15
13
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
const singleton = Cache.singleton(() => { /* ... */ });
|
|
19
|
-
const memoized = Cache.memoize((key) => { /* ... */ });
|
|
14
|
+
// Or
|
|
15
|
+
import { Cache } from 'native-document/utils';
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
---
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
## `Cache.once(fn)` - Lazy Initialization
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
```javascript
|
|
28
|
-
import { Cache } from 'native-document/utils';
|
|
22
|
+
The function is **not executed immediately**. It runs only when you access a property on the returned Proxy object. The result is cached after the first access.
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
```javascript
|
|
25
|
+
const Config = Cache.once(() => {
|
|
31
26
|
console.log('Loading config...');
|
|
32
27
|
return {
|
|
33
|
-
apiUrl:
|
|
34
|
-
timeout:
|
|
28
|
+
apiUrl: 'https://api.example.com',
|
|
29
|
+
timeout: 5000,
|
|
35
30
|
maxRetries: 3
|
|
36
31
|
};
|
|
37
32
|
});
|
|
38
33
|
|
|
39
34
|
// Function not executed yet
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// First property access triggers execution
|
|
43
|
-
console.log(LazyConfig.apiUrl);
|
|
44
|
-
// Logs: "Loading config..."
|
|
45
|
-
// Returns: "https://api.example.com"
|
|
46
|
-
|
|
47
|
-
// Subsequent accesses use cached result
|
|
48
|
-
console.log(LazyConfig.timeout); // No log, returns 5000
|
|
49
|
-
console.log(LazyConfig.maxRetries); // No log, returns 3
|
|
35
|
+
Config.apiUrl; // "Loading config..." -> "https://api.example.com"
|
|
36
|
+
Config.timeout; // no log -> 5000 (cached)
|
|
50
37
|
```
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
```javascript
|
|
54
|
-
import { Cache } from 'native-document/utils';
|
|
55
|
-
|
|
56
|
-
const Utils = Cache.once(() => {
|
|
57
|
-
console.log('Initializing utils...');
|
|
58
|
-
return {
|
|
59
|
-
formatDate: (date) => new Date(date).toLocaleDateString(),
|
|
60
|
-
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
|
|
61
|
-
truncate: (str, len) => str.length > len ? str.slice(0, len) + '...' : str,
|
|
62
|
-
slugify: (str) => str.toLowerCase().replace(/\s+/g, '-')
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Utils not initialized yet
|
|
67
|
-
console.log('App started');
|
|
68
|
-
|
|
69
|
-
// First use initializes the module
|
|
70
|
-
const formatted = Utils.formatDate(new Date());
|
|
71
|
-
// Logs: "Initializing utils..."
|
|
39
|
+
Use for optional features or heavy modules that may not always be needed:
|
|
72
40
|
|
|
73
|
-
// Already initialized - no log
|
|
74
|
-
const capitalized = Utils.capitalize('hello');
|
|
75
|
-
const slug = Utils.slugify('Hello World');
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Lazy API Client
|
|
79
41
|
```javascript
|
|
80
|
-
import { Cache } from 'native-document/utils';
|
|
81
|
-
import { NativeFetch } from 'native-document/utils';
|
|
82
|
-
|
|
83
42
|
const API = Cache.once(() => {
|
|
84
|
-
console.log('Setting up API client...');
|
|
85
43
|
const client = new NativeFetch('https://api.example.com');
|
|
86
|
-
|
|
87
|
-
client.interceptors.request((config) => {
|
|
44
|
+
client.interceptors.request(config => {
|
|
88
45
|
config.headers['Authorization'] = `Bearer ${getToken()}`;
|
|
89
46
|
return config;
|
|
90
47
|
});
|
|
91
|
-
|
|
92
48
|
return {
|
|
93
49
|
users: {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
posts: {
|
|
98
|
-
get: (id) => client.get(`/posts/${id}`),
|
|
99
|
-
create: (data) => client.post('/posts', data)
|
|
50
|
+
list: () => client.get('/users'),
|
|
51
|
+
get: (id) => client.get(`/users/${id}`)
|
|
100
52
|
}
|
|
101
53
|
};
|
|
102
54
|
});
|
|
103
55
|
|
|
104
|
-
// Client
|
|
105
|
-
console.log('Starting app...');
|
|
106
|
-
|
|
107
|
-
// Client created on first API call
|
|
56
|
+
// Client created only on first use
|
|
108
57
|
const users = await API.users.list();
|
|
109
|
-
// Logs: "Setting up API client..."
|
|
110
|
-
|
|
111
|
-
// Client already created
|
|
112
|
-
const user = await API.users.get('123');
|
|
113
58
|
```
|
|
114
59
|
|
|
115
|
-
|
|
60
|
+
---
|
|
116
61
|
|
|
117
|
-
|
|
62
|
+
## `Cache.singleton(fn)` - Eager Singleton
|
|
118
63
|
|
|
119
|
-
|
|
120
|
-
```javascript
|
|
121
|
-
import { Cache } from 'native-document/utils';
|
|
64
|
+
The function executes on the **first call** and caches the result. All subsequent calls return the same instance.
|
|
122
65
|
|
|
66
|
+
```javascript
|
|
123
67
|
const getLogger = Cache.singleton(() => {
|
|
124
|
-
console.log('Creating logger
|
|
68
|
+
console.log('Creating logger...');
|
|
125
69
|
return {
|
|
126
|
-
log:
|
|
127
|
-
error:
|
|
128
|
-
warn: (msg) => console.warn(`[WARN] ${msg}`),
|
|
129
|
-
info: (msg) => console.info(`[INFO] ${msg}`)
|
|
70
|
+
log: msg => console.log(`[LOG] ${msg}`),
|
|
71
|
+
error: msg => console.error(`[ERROR] ${msg}`)
|
|
130
72
|
};
|
|
131
73
|
});
|
|
132
74
|
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
logger.log('Application started');
|
|
138
|
-
|
|
139
|
-
// Subsequent calls return cached instance
|
|
140
|
-
const logger2 = getLogger();
|
|
141
|
-
// No log - returns cached instance
|
|
142
|
-
|
|
143
|
-
console.log(logger === logger2); // true - same reference
|
|
75
|
+
const logger = getLogger(); // "Creating logger..."
|
|
76
|
+
const logger2 = getLogger(); // no log
|
|
77
|
+
console.log(logger === logger2); // true
|
|
144
78
|
```
|
|
145
79
|
|
|
146
|
-
|
|
147
|
-
```javascript
|
|
148
|
-
import { Cache } from 'native-document/utils';
|
|
149
|
-
|
|
150
|
-
const getConfig = Cache.singleton(() => {
|
|
151
|
-
console.log('Loading configuration...');
|
|
152
|
-
return {
|
|
153
|
-
apiUrl: import.meta.env.VITE_API_URL || 'https://api.example.com',
|
|
154
|
-
debug: import.meta.env.DEV,
|
|
155
|
-
version: '1.0.0',
|
|
156
|
-
features: {
|
|
157
|
-
analytics: true,
|
|
158
|
-
darkMode: true
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// First call loads config
|
|
164
|
-
const config = getConfig();
|
|
165
|
-
// Logs: "Loading configuration..."
|
|
166
|
-
|
|
167
|
-
console.log(config.apiUrl);
|
|
80
|
+
Use for core services that must exist exactly once:
|
|
168
81
|
|
|
169
|
-
// Returns same config instance
|
|
170
|
-
const config2 = getConfig();
|
|
171
|
-
console.log(config === config2); // true
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Event Bus Singleton
|
|
175
82
|
```javascript
|
|
176
|
-
|
|
83
|
+
const getConfig = Cache.singleton(() => ({
|
|
84
|
+
apiUrl: import.meta.env.VITE_API_URL,
|
|
85
|
+
debug: import.meta.env.DEV,
|
|
86
|
+
version: '1.0.0'
|
|
87
|
+
}));
|
|
177
88
|
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
listeners.set(event, []);
|
|
186
|
-
}
|
|
187
|
-
listeners.get(event).push(callback);
|
|
188
|
-
},
|
|
189
|
-
off: (event, callback) => {
|
|
190
|
-
const eventListeners = listeners.get(event);
|
|
191
|
-
if (eventListeners) {
|
|
192
|
-
const index = eventListeners.indexOf(callback);
|
|
193
|
-
if (index > -1) {
|
|
194
|
-
eventListeners.splice(index, 1);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
emit: (event, data) => {
|
|
199
|
-
const eventListeners = listeners.get(event);
|
|
200
|
-
if (eventListeners) {
|
|
201
|
-
eventListeners.forEach(callback => callback(data));
|
|
202
|
-
}
|
|
89
|
+
const getAPI = Cache.singleton(() => {
|
|
90
|
+
const config = getConfig();
|
|
91
|
+
const client = new NativeFetch(config.apiUrl);
|
|
92
|
+
client.interceptors.request(req => {
|
|
93
|
+
const token = localStorage.getItem('token');
|
|
94
|
+
if (token) {
|
|
95
|
+
req.headers['Authorization'] = `Bearer ${token}`;
|
|
203
96
|
}
|
|
204
|
-
|
|
97
|
+
return req;
|
|
98
|
+
});
|
|
99
|
+
return client;
|
|
205
100
|
});
|
|
206
|
-
|
|
207
|
-
// Create event bus
|
|
208
|
-
const bus = getEventBus();
|
|
209
|
-
// Logs: "Creating event bus..."
|
|
210
|
-
|
|
211
|
-
bus.on('user:login', (user) => console.log('User logged in:', user));
|
|
212
|
-
bus.emit('user:login', { id: 1, name: 'John' });
|
|
213
|
-
|
|
214
|
-
// Same event bus everywhere
|
|
215
|
-
const bus2 = getEventBus();
|
|
216
|
-
console.log(bus === bus2); // true
|
|
217
101
|
```
|
|
218
102
|
|
|
219
|
-
|
|
103
|
+
---
|
|
220
104
|
|
|
221
|
-
|
|
105
|
+
## `Cache.memoize(fn)` - Key-Based Memoization
|
|
222
106
|
|
|
223
|
-
|
|
224
|
-
```javascript
|
|
225
|
-
import { Cache } from 'native-document/utils';
|
|
107
|
+
Each property access creates and caches a **separate instance** using the property name as the key argument.
|
|
226
108
|
|
|
227
|
-
const API = Cache.memoize((key) => {
|
|
228
|
-
console.log(`Creating API for: ${key}`);
|
|
229
|
-
return {
|
|
230
|
-
async list() {
|
|
231
|
-
return await fetch('/' + key);
|
|
232
|
-
},
|
|
233
|
-
async get(id) {
|
|
234
|
-
return await fetch('/' + key + '/' + id);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// First access to 'users' - executes function with key='users'
|
|
240
|
-
await API.users.list();
|
|
241
|
-
// Logs: "Creating API for: users"
|
|
242
|
-
// Fetch: '/users'
|
|
243
|
-
|
|
244
|
-
// Second access to 'users' - returns cached instance
|
|
245
|
-
await API.users.get('123');
|
|
246
|
-
// No log - cached instance
|
|
247
|
-
// Fetch: '/users/123'
|
|
248
|
-
|
|
249
|
-
// First access to 'posts' - executes function with key='posts'
|
|
250
|
-
await API.posts.list();
|
|
251
|
-
// Logs: "Creating API for: posts"
|
|
252
|
-
// Fetch: '/posts'
|
|
253
|
-
|
|
254
|
-
// Cached instance for 'posts'
|
|
255
|
-
await API.posts.get('456');
|
|
256
|
-
// No log - cached instance
|
|
257
|
-
// Fetch: '/posts/456'
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Key Concepts
|
|
261
|
-
|
|
262
|
-
1. **Property name becomes the key**: `API.users` -> `key = 'users'`
|
|
263
|
-
2. **Function executed per unique key**: First access creates instance
|
|
264
|
-
3. **Results cached by key**: Subsequent accesses return same instance
|
|
265
|
-
4. **Each key has its own instance**: `API.users` ≠ `API.posts`
|
|
266
|
-
|
|
267
|
-
### Basic Resource Providers
|
|
268
109
|
```javascript
|
|
269
|
-
import { Cache } from 'native-document/utils';
|
|
270
|
-
|
|
271
|
-
const Resources = Cache.memoize((resource) => {
|
|
272
|
-
console.log(`Loading ${resource}...`);
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
data: `${resource} data`,
|
|
276
|
-
load: () => console.log(`Reloading ${resource}`),
|
|
277
|
-
save: (content) => console.log(`Saving to ${resource}:`, content)
|
|
278
|
-
};
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// First access - creates 'icons' instance
|
|
282
|
-
Resources.icons.load();
|
|
283
|
-
// Logs: "Loading icons..."
|
|
284
|
-
// Logs: "Reloading icons"
|
|
285
|
-
|
|
286
|
-
// Cached instance
|
|
287
|
-
Resources.icons.save('new-icon.svg');
|
|
288
|
-
// Logs: "Saving to icons: new-icon.svg"
|
|
289
|
-
|
|
290
|
-
// Different key - creates 'fonts' instance
|
|
291
|
-
Resources.fonts.load();
|
|
292
|
-
// Logs: "Loading fonts..."
|
|
293
|
-
// Logs: "Reloading fonts"
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### API Endpoint Collections
|
|
297
|
-
```javascript
|
|
298
|
-
import { Cache } from 'native-document/utils';
|
|
299
|
-
import { NativeFetch } from 'native-document/utils';
|
|
300
|
-
|
|
301
|
-
const api = new NativeFetch('https://api.example.com');
|
|
302
|
-
|
|
303
110
|
const Endpoints = Cache.memoize((resource) => {
|
|
304
|
-
console.log(`Creating endpoint
|
|
305
|
-
|
|
111
|
+
console.log(`Creating endpoint: ${resource}`);
|
|
112
|
+
const api = getAPI();
|
|
306
113
|
return {
|
|
307
|
-
list:
|
|
308
|
-
get:
|
|
309
|
-
create: (data)
|
|
310
|
-
update: (id, data)
|
|
311
|
-
delete: (id)
|
|
114
|
+
list: (params = {}) => api.get(`/${resource}`, params),
|
|
115
|
+
get: (id) => api.get(`/${resource}/${id}`),
|
|
116
|
+
create: (data) => api.post(`/${resource}`, data),
|
|
117
|
+
update: (id, data) => api.put(`/${resource}/${id}`, data),
|
|
118
|
+
delete: (id) => api.delete(`/${resource}/${id}`)
|
|
312
119
|
};
|
|
313
120
|
});
|
|
314
121
|
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
//
|
|
318
|
-
// GET: /users
|
|
319
|
-
|
|
320
|
-
const user = await Endpoints.users.get('123');
|
|
321
|
-
// GET: /users/123 (cached instance)
|
|
322
|
-
|
|
323
|
-
// Create 'posts' endpoint instance
|
|
324
|
-
const posts = await Endpoints.posts.list();
|
|
325
|
-
// Logs: "Creating endpoint for: posts"
|
|
326
|
-
// GET: /posts
|
|
327
|
-
|
|
328
|
-
await Endpoints.posts.create({ title: 'New Post' });
|
|
329
|
-
// POST: /posts (cached instance)
|
|
122
|
+
await Endpoints.users.list(); // "Creating endpoint: users"
|
|
123
|
+
await Endpoints.users.get('1'); // no log - cached
|
|
124
|
+
await Endpoints.posts.list(); // "Creating endpoint: posts"
|
|
330
125
|
```
|
|
331
126
|
|
|
332
|
-
|
|
333
|
-
```javascript
|
|
334
|
-
import { Cache } from 'native-document/utils';
|
|
335
|
-
import { Store } from 'native-document';
|
|
127
|
+
Use for resources that share the same structure but need separate instances per key:
|
|
336
128
|
|
|
337
|
-
const Stores = Cache.memoize((storeName) => {
|
|
338
|
-
console.log(`Creating store: ${storeName}`);
|
|
339
|
-
|
|
340
|
-
// Create store if it doesn't exist
|
|
341
|
-
if (!Store.get(storeName)) {
|
|
342
|
-
Store.create(storeName, {
|
|
343
|
-
items: [],
|
|
344
|
-
loading: false,
|
|
345
|
-
error: null
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return {
|
|
350
|
-
get: () => Store.use(storeName),
|
|
351
|
-
setLoading: (value) => {
|
|
352
|
-
/*...*/
|
|
353
|
-
},
|
|
354
|
-
addItem: (items) => {
|
|
355
|
-
/*...*/
|
|
356
|
-
},
|
|
357
|
-
setItems: (items) => {
|
|
358
|
-
/*...*/
|
|
359
|
-
},
|
|
360
|
-
setError: (error) => {
|
|
361
|
-
/*...*/
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// Create 'products' store
|
|
367
|
-
const productsStore = Stores.products.get();
|
|
368
|
-
// Logs: "Creating store: products"
|
|
369
|
-
|
|
370
|
-
Stores.products.setLoading(true);
|
|
371
|
-
Stores.products.addItem({ id: 1, name: 'Product 1' });
|
|
372
|
-
|
|
373
|
-
// Create 'users' store
|
|
374
|
-
const usersStore = Stores.users.get();
|
|
375
|
-
// Logs: "Creating store: users"
|
|
376
|
-
|
|
377
|
-
Stores.users.setItems([{ id: 1, name: 'Alice' }]);
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### LocalStorage Namespaces
|
|
381
129
|
```javascript
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
console.log(`Creating storage for namespace: ${namespace}`);
|
|
130
|
+
// Namespaced localStorage
|
|
131
|
+
const Storage = Cache.memoize((namespace) => ({
|
|
132
|
+
get: (key) => JSON.parse(localStorage.getItem(`${namespace}:${key}`)),
|
|
386
133
|
|
|
387
|
-
|
|
134
|
+
set: (key, val) => localStorage.setItem(`${namespace}:${key}`, JSON.stringify(val)),
|
|
388
135
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
try {
|
|
392
|
-
const item = localStorage.getItem(prefix + key);
|
|
393
|
-
return item ? JSON.parse(item) : null;
|
|
394
|
-
} catch {
|
|
395
|
-
return null;
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
set: (key, value) => {
|
|
399
|
-
try {
|
|
400
|
-
localStorage.setItem(prefix + key, JSON.stringify(value));
|
|
401
|
-
return true;
|
|
402
|
-
} catch {
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
remove: (key) => {
|
|
407
|
-
localStorage.removeItem(prefix + key);
|
|
408
|
-
},
|
|
409
|
-
clear: () => {
|
|
410
|
-
// Clear all keys with this namespace
|
|
411
|
-
Object.keys(localStorage)
|
|
412
|
-
.filter(key => key.startsWith(prefix))
|
|
413
|
-
.forEach(key => localStorage.removeItem(key));
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
});
|
|
136
|
+
remove: (key) => localStorage.removeItem(`${namespace}:${key}`)
|
|
137
|
+
}));
|
|
417
138
|
|
|
418
|
-
// User preferences namespace
|
|
419
139
|
Storage.user.set('theme', 'dark');
|
|
420
|
-
// Logs: "Creating storage for namespace: user"
|
|
421
|
-
// localStorage: "user:theme" = "dark"
|
|
422
|
-
|
|
423
|
-
Storage.user.set('language', 'en');
|
|
424
|
-
// localStorage: "user:language" = "en"
|
|
425
|
-
|
|
426
|
-
const theme = Storage.user.get('theme');
|
|
427
|
-
// Returns: "dark"
|
|
428
|
-
|
|
429
|
-
// App settings namespace (different instance)
|
|
430
140
|
Storage.app.set('version', '1.0.0');
|
|
431
|
-
// Logs: "Creating storage for namespace: app"
|
|
432
|
-
// localStorage: "app:version" = "1.0.0"
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
### Event Handlers by Type
|
|
436
|
-
```javascript
|
|
437
|
-
import { Cache } from 'native-document/utils';
|
|
438
|
-
|
|
439
|
-
const EventHandlers = Cache.memoize((eventType) => {
|
|
440
|
-
console.log(`Creating handler for: ${eventType}`);
|
|
441
|
-
const listeners = [];
|
|
442
|
-
|
|
443
|
-
return {
|
|
444
|
-
add: (callback) => {
|
|
445
|
-
listeners.push(callback);
|
|
446
|
-
},
|
|
447
|
-
remove: (callback) => {
|
|
448
|
-
const index = listeners.indexOf(callback);
|
|
449
|
-
if (index > -1) {
|
|
450
|
-
listeners.splice(index, 1);
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
emit: (data) => {
|
|
454
|
-
listeners.forEach(callback => callback(data));
|
|
455
|
-
},
|
|
456
|
-
count: () => listeners.length
|
|
457
|
-
};
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// Create 'click' event handler
|
|
461
|
-
EventHandlers.click.add((data) => console.log('Clicked:', data));
|
|
462
|
-
// Logs: "Creating handler for: click"
|
|
463
|
-
|
|
464
|
-
EventHandlers.click.emit({ x: 100, y: 200 });
|
|
465
|
-
// Logs: "Clicked: { x: 100, y: 200 }"
|
|
466
|
-
|
|
467
|
-
// Create 'scroll' event handler (different instance)
|
|
468
|
-
EventHandlers.scroll.add((data) => console.log('Scrolled:', data));
|
|
469
|
-
// Logs: "Creating handler for: scroll"
|
|
470
|
-
|
|
471
|
-
EventHandlers.scroll.emit({ top: 500 });
|
|
472
|
-
// Logs: "Scrolled: { top: 500 }"
|
|
473
|
-
|
|
474
|
-
console.log(EventHandlers.click.count()); // 1
|
|
475
|
-
console.log(EventHandlers.scroll.count()); // 1
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
### Validation Rules by Type
|
|
479
|
-
```javascript
|
|
480
|
-
import { Cache } from 'native-document/utils';
|
|
481
141
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
required: (value) => !!value || 'Email is required',
|
|
488
|
-
format: (value) => /\S+@\S+\.\S+/.test(value) || 'Invalid email format'
|
|
489
|
-
},
|
|
490
|
-
password: {
|
|
491
|
-
required: (value) => !!value || 'Password is required',
|
|
492
|
-
minLength: (value) => value.length >= 8 || 'Password must be at least 8 characters',
|
|
493
|
-
hasNumber: (value) => /\d/.test(value) || 'Password must contain a number'
|
|
494
|
-
},
|
|
495
|
-
phone: {
|
|
496
|
-
required: (value) => !!value || 'Phone is required',
|
|
497
|
-
format: (value) => /^\d{10}$/.test(value) || 'Phone must be 10 digits'
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
return {
|
|
502
|
-
validate: (value) => {
|
|
503
|
-
const fieldRules = rules[fieldType];
|
|
504
|
-
if (!fieldRules) return [];
|
|
505
|
-
|
|
506
|
-
const errors = [];
|
|
507
|
-
for (const [ruleName, rule] of Object.entries(fieldRules)) {
|
|
508
|
-
const result = rule(value);
|
|
509
|
-
if (result !== true) {
|
|
510
|
-
errors.push(result);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return errors;
|
|
514
|
-
},
|
|
515
|
-
isValid: (value) => {
|
|
516
|
-
return Validators[fieldType].validate(value).length === 0;
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// Validate email
|
|
522
|
-
const emailErrors = Validators.email.validate('invalid-email');
|
|
523
|
-
// Logs: "Creating validators for: email"
|
|
524
|
-
// Returns: ['Invalid email format']
|
|
525
|
-
|
|
526
|
-
const isEmailValid = Validators.email.isValid('test@example.com');
|
|
527
|
-
// Returns: true (cached instance)
|
|
528
|
-
|
|
529
|
-
// Validate password
|
|
530
|
-
const passwordErrors = Validators.password.validate('weak');
|
|
531
|
-
// Logs: "Creating validators for: password"
|
|
532
|
-
// Returns: ['Password must be at least 8 characters', 'Password must contain a number']
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### Chart Instances by Type
|
|
536
|
-
```javascript
|
|
537
|
-
import { Cache } from 'native-document/utils';
|
|
538
|
-
|
|
539
|
-
const Charts = Cache.memoize((chartType) => {
|
|
540
|
-
console.log(`Creating ${chartType} chart instance...`);
|
|
541
|
-
|
|
542
|
-
return {
|
|
543
|
-
render: (container, data, options = {}) => {
|
|
544
|
-
console.log(`Rendering ${chartType} chart`);
|
|
545
|
-
|
|
546
|
-
// Simplified chart rendering
|
|
547
|
-
const canvas = document.createElement('canvas');
|
|
548
|
-
container.appendChild(canvas);
|
|
549
|
-
|
|
550
|
-
// Chart-specific rendering logic
|
|
551
|
-
switch (chartType) {
|
|
552
|
-
case 'bar':
|
|
553
|
-
renderBarChart(canvas, data, options);
|
|
554
|
-
break;
|
|
555
|
-
case 'line':
|
|
556
|
-
renderLineChart(canvas, data, options);
|
|
557
|
-
break;
|
|
558
|
-
case 'pie':
|
|
559
|
-
renderPieChart(canvas, data, options);
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return canvas;
|
|
564
|
-
},
|
|
565
|
-
update: (canvas, data) => {
|
|
566
|
-
console.log(`Updating ${chartType} chart`);
|
|
567
|
-
// Update logic
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// Create bar chart instance
|
|
573
|
-
const barContainer = document.querySelector('#bar-chart');
|
|
574
|
-
Charts.bar.render(barContainer, [10, 20, 30]);
|
|
575
|
-
// Logs: "Creating bar chart instance..."
|
|
576
|
-
// Logs: "Rendering bar chart"
|
|
577
|
-
|
|
578
|
-
// Create line chart instance (different from bar)
|
|
579
|
-
const lineContainer = document.querySelector('#line-chart');
|
|
580
|
-
Charts.line.render(lineContainer, [5, 15, 25]);
|
|
581
|
-
// Logs: "Creating line chart instance..."
|
|
582
|
-
// Logs: "Rendering line chart"
|
|
583
|
-
|
|
584
|
-
// Reuse bar chart instance
|
|
585
|
-
Charts.bar.update(barCanvas, [15, 25, 35]);
|
|
586
|
-
// Logs: "Updating bar chart"
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
### Form Field Managers
|
|
590
|
-
```javascript
|
|
591
|
-
import { Cache } from 'native-document/utils';
|
|
592
|
-
import { Observable } from 'native-document';
|
|
593
|
-
|
|
594
|
-
const FormFields = Cache.memoize((fieldName) => {
|
|
595
|
-
console.log(`Creating field manager for: ${fieldName}`);
|
|
596
|
-
|
|
597
|
-
const value = Observable('');
|
|
598
|
-
const errors = Observable([]);
|
|
599
|
-
const touched = Observable(false);
|
|
600
|
-
|
|
601
|
-
return {
|
|
602
|
-
value,
|
|
603
|
-
errors,
|
|
604
|
-
touched,
|
|
605
|
-
setValue: (newValue) => {
|
|
606
|
-
value.set(newValue);
|
|
607
|
-
touched.set(true);
|
|
608
|
-
},
|
|
609
|
-
setErrors: (newErrors) => {
|
|
610
|
-
errors.set(newErrors);
|
|
611
|
-
},
|
|
612
|
-
reset: () => {
|
|
613
|
-
value.set('');
|
|
614
|
-
errors.set([]);
|
|
615
|
-
touched.set(false);
|
|
616
|
-
},
|
|
617
|
-
isValid: () => errors.val().length === 0
|
|
618
|
-
};
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
// Create email field
|
|
622
|
-
FormFields.email.setValue('test@example.com');
|
|
623
|
-
// Logs: "Creating field manager for: email"
|
|
624
|
-
|
|
625
|
-
FormFields.email.setErrors([]);
|
|
626
|
-
console.log(FormFields.email.isValid()); // true
|
|
627
|
-
|
|
628
|
-
// Create password field (different instance)
|
|
629
|
-
FormFields.password.setValue('weak');
|
|
630
|
-
// Logs: "Creating field manager for: password"
|
|
631
|
-
|
|
632
|
-
FormFields.password.setErrors(['Too short']);
|
|
633
|
-
console.log(FormFields.password.isValid()); // false
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
## Comparison: once vs singleton vs memoize
|
|
637
|
-
|
|
638
|
-
| Feature | `Cache.once()` | `Cache.singleton()` | `Cache.memoize()` |
|
|
639
|
-
|---------|------------------|------------------------|---------------------|
|
|
640
|
-
| **Execution** | Lazy (on property access) | Eager (on first call) | Lazy (per key on property access) |
|
|
641
|
-
| **Implementation** | `autoOnce` | `once` | `autoMemoize` |
|
|
642
|
-
| **Access Pattern** | `obj.property` | `fn()` | `obj[key].method()` |
|
|
643
|
-
| **Instances Created** | 1 (single instance) | 1 (single instance) | N (one per key) |
|
|
644
|
-
| **Cache Strategy** | Properties from result | Entire result | Result per property key |
|
|
645
|
-
| **Use Case** | Lazy modules | Eager singletons | Multiple instances by key |
|
|
646
|
-
|
|
647
|
-
### Visual Comparison
|
|
648
|
-
```javascript
|
|
649
|
-
import { Cache } from 'native-document/utils';
|
|
650
|
-
|
|
651
|
-
// Cache.once() - Single lazy instance
|
|
652
|
-
const LazyUtils = Cache.once(() => {
|
|
653
|
-
console.log('Init utils');
|
|
654
|
-
return {
|
|
655
|
-
format: () => 'formatted',
|
|
656
|
-
parse: () => 'parsed'
|
|
657
|
-
};
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
LazyUtils.format(); // Logs: "Init utils"
|
|
661
|
-
LazyUtils.parse(); // No log - same instance
|
|
662
|
-
|
|
663
|
-
// Cache.singleton() - Single eager instance
|
|
664
|
-
const getConfig = Cache.singleton(() => {
|
|
665
|
-
console.log('Init config');
|
|
666
|
-
return { api: 'url', debug: true };
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
const config = getConfig(); // Logs: "Init config"
|
|
670
|
-
const config2 = getConfig(); // No log - same instance
|
|
671
|
-
|
|
672
|
-
// Cache.memoize() - Multiple instances by key
|
|
673
|
-
const API = Cache.memoize((resource) => {
|
|
674
|
-
console.log(`Init ${resource}`);
|
|
675
|
-
return {
|
|
676
|
-
list: () => `List ${resource}`,
|
|
677
|
-
get: (id) => `Get ${resource}/${id}`
|
|
678
|
-
};
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
API.users.list(); // Logs: "Init users"
|
|
682
|
-
API.users.get(1); // No log - cached 'users' instance
|
|
683
|
-
API.posts.list(); // Logs: "Init posts" - new key, new instance
|
|
684
|
-
API.posts.get(2); // No log - cached 'posts' instance
|
|
685
|
-
```
|
|
686
|
-
|
|
687
|
-
## When to Use Cache.memoize()
|
|
688
|
-
|
|
689
|
-
### ✅ Good Use Cases
|
|
690
|
-
```javascript
|
|
691
|
-
import { Cache } from 'native-document/utils';
|
|
692
|
-
|
|
693
|
-
// Multiple API endpoints with same structure
|
|
694
|
-
const API = Cache.memoize((resource) => ({
|
|
695
|
-
list: () => fetch(`/${resource}`),
|
|
696
|
-
get: (id) => fetch(`/${resource}/${id}`)
|
|
697
|
-
}));
|
|
698
|
-
|
|
699
|
-
// Multiple storage namespaces
|
|
700
|
-
const Storage = Cache.memoize((namespace) => ({
|
|
701
|
-
get: (key) => localStorage.getItem(`${namespace}:${key}`),
|
|
702
|
-
set: (key, val) => localStorage.setItem(`${namespace}:${key}`, val)
|
|
142
|
+
// Form fields with observables
|
|
143
|
+
const Fields = Cache.memoize(name => ({
|
|
144
|
+
value: Observable(''),
|
|
145
|
+
error: Observable(null),
|
|
146
|
+
reset: () => { Fields[name].value.set(''); Fields[name].error.set(null); }
|
|
703
147
|
}));
|
|
704
148
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const listeners = [];
|
|
708
|
-
return {
|
|
709
|
-
on: (cb) => listeners.push(cb),
|
|
710
|
-
emit: (data) => listeners.forEach(cb => cb(data))
|
|
711
|
-
};
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
// Multiple form fields with same structure
|
|
715
|
-
const Fields = Cache.memoize((name) => ({
|
|
716
|
-
value: Observable(''),
|
|
717
|
-
error: Observable(null),
|
|
718
|
-
validate: () => { /* ... */ }
|
|
719
|
-
}));
|
|
149
|
+
Fields.email.value.set('alice@example.com');
|
|
150
|
+
Fields.password.value.set('secret');
|
|
720
151
|
```
|
|
721
152
|
|
|
722
|
-
|
|
723
|
-
```javascript
|
|
724
|
-
import { Cache } from 'native-document/utils';
|
|
153
|
+
---
|
|
725
154
|
|
|
726
|
-
|
|
727
|
-
const Config = Cache.memoize(() => loadConfig());
|
|
728
|
-
// Use Cache.singleton() instead
|
|
155
|
+
## Comparison
|
|
729
156
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
157
|
+
| | `Cache.once()` | `Cache.singleton()` | `Cache.memoize()` |
|
|
158
|
+
|---|---|---|---|
|
|
159
|
+
| **Execution** | Lazy - on property access | Eager - on first call | Lazy - per key on property access |
|
|
160
|
+
| **Access** | `obj.property` | `fn()` | `obj.key.method()` |
|
|
161
|
+
| **Instances** | 1 | 1 | 1 per key |
|
|
162
|
+
| **Best for** | Optional modules | Core services | Resources by type |
|
|
733
163
|
|
|
734
|
-
|
|
735
|
-
const Constants = Cache.memoize(() => ({
|
|
736
|
-
PI: 3.14159,
|
|
737
|
-
E: 2.71828
|
|
738
|
-
}));
|
|
739
|
-
// Use Cache.once() instead
|
|
740
|
-
```
|
|
164
|
+
---
|
|
741
165
|
|
|
742
166
|
## Best Practices
|
|
743
167
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
const Endpoints = Cache.memoize((resource) => createAPI(resource));
|
|
750
|
-
Endpoints.users.list();
|
|
751
|
-
Endpoints.posts.list();
|
|
752
|
-
|
|
753
|
-
// ❌ Bad: Dynamic, unpredictable keys
|
|
754
|
-
const DynamicAPI = Cache.memoize((timestamp) => createAPI(timestamp));
|
|
755
|
-
DynamicAPI[Date.now()].list(); // New instance every time
|
|
756
|
-
```
|
|
757
|
-
|
|
758
|
-
### 2. Document Key-Based Behavior
|
|
759
|
-
```javascript
|
|
760
|
-
import { Cache } from 'native-document/utils';
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* API endpoints by resource type
|
|
764
|
-
* @memoized Each resource type gets its own instance
|
|
765
|
-
* @param {string} resource - Resource name (e.g., 'users', 'posts')
|
|
766
|
-
*/
|
|
767
|
-
const API = Cache.memoize((resource) => ({
|
|
768
|
-
list: () => fetch(`/${resource}`),
|
|
769
|
-
get: (id) => fetch(`/${resource}/${id}`)
|
|
770
|
-
}));
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
### 3. Keep Keys Simple
|
|
774
|
-
```javascript
|
|
775
|
-
import { Cache } from 'native-document/utils';
|
|
776
|
-
|
|
777
|
-
// ✅ Good: Simple string keys
|
|
778
|
-
const Stores = Cache.memoize((name) => Store.create(name, {}));
|
|
779
|
-
Stores.user;
|
|
780
|
-
Stores.settings;
|
|
781
|
-
|
|
782
|
-
// ❌ Bad: Complex keys (won't work as expected)
|
|
783
|
-
const BadCache = Cache.memoize((config) => createThing(config));
|
|
784
|
-
BadCache[{ type: 'user' }]; // Object as key - problematic
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
### 4. Combine with Other Patterns
|
|
788
|
-
```javascript
|
|
789
|
-
import { Cache } from 'native-document/utils';
|
|
790
|
-
|
|
791
|
-
// Eager config + Memoized resources
|
|
792
|
-
const getConfig = Cache.singleton(() => loadConfig());
|
|
793
|
-
|
|
794
|
-
const Resources = Cache.memoize((type) => {
|
|
795
|
-
const config = getConfig(); // Config already loaded
|
|
796
|
-
return createResource(type, config);
|
|
797
|
-
});
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
## Performance Considerations
|
|
801
|
-
|
|
802
|
-
### Memory Usage
|
|
803
|
-
```javascript
|
|
804
|
-
import { Cache } from 'native-document/utils';
|
|
805
|
-
|
|
806
|
-
// ⚠️ Each key creates a new cached instance
|
|
807
|
-
const API = Cache.memoize((resource) => createAPI(resource));
|
|
808
|
-
|
|
809
|
-
API.users; // Instance 1
|
|
810
|
-
API.posts; // Instance 2
|
|
811
|
-
API.comments; // Instance 3
|
|
812
|
-
// All instances stay in memory
|
|
813
|
-
|
|
814
|
-
// Consider: Do you need separate instances per key?
|
|
815
|
-
```
|
|
816
|
-
|
|
817
|
-
### Cache Growth
|
|
818
|
-
```javascript
|
|
819
|
-
import { Cache } from 'native-document/utils';
|
|
820
|
-
|
|
821
|
-
// ⚠️ Warning: Unbounded cache growth
|
|
822
|
-
const DynamicCache = Cache.memoize((id) => createInstance(id));
|
|
823
|
-
|
|
824
|
-
// If IDs keep changing, cache grows indefinitely
|
|
825
|
-
for (let i = 0; i < 1000; i++) {
|
|
826
|
-
DynamicCache[`user-${i}`]; // 1000 cached instances
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// ✅ Better: Limited, predictable keys
|
|
830
|
-
const ResourceCache = Cache.memoize((type) => createResource(type));
|
|
831
|
-
ResourceCache.users; // Only a few known types
|
|
832
|
-
ResourceCache.posts;
|
|
833
|
-
ResourceCache.comments;
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
## Summary
|
|
837
|
-
|
|
838
|
-
### Quick Reference
|
|
839
|
-
|
|
840
|
-
| Pattern | Syntax | When to Use |
|
|
841
|
-
|---------|--------|-------------|
|
|
842
|
-
| **Lazy Module** | `Cache.once(() => {...})` | Optional features, heavy resources |
|
|
843
|
-
| **Eager Singleton** | `Cache.singleton(() => {...})` | Core services, configuration |
|
|
844
|
-
| **Multi-Instance** | `Cache.memoize((key) => {...})` | Resources by type, namespaced data |
|
|
845
|
-
|
|
846
|
-
### Key Differences
|
|
847
|
-
```javascript
|
|
848
|
-
import { Cache } from 'native-document/utils';
|
|
849
|
-
|
|
850
|
-
// Cache.once() - Single lazy instance via autoOnce
|
|
851
|
-
const Lazy = Cache.once(() => ({ value: 1 }));
|
|
852
|
-
Lazy.value; // Creates instance on property access
|
|
853
|
-
|
|
854
|
-
// Cache.singleton() - Single eager instance via once
|
|
855
|
-
const Eager = Cache.singleton(() => ({ value: 1 }));
|
|
856
|
-
Eager(); // Creates instance on function call
|
|
857
|
-
|
|
858
|
-
// Cache.memoize() - Multiple instances by key via autoMemoize
|
|
859
|
-
const Multi = Cache.memoize((key) => ({ value: key }));
|
|
860
|
-
Multi.a; // Creates instance for key 'a'
|
|
861
|
-
Multi.b; // Creates instance for key 'b'
|
|
862
|
-
```
|
|
863
|
-
|
|
864
|
-
## Next Steps
|
|
168
|
+
1. Use `Cache.singleton()` for core services (API client, config, logger)
|
|
169
|
+
2. Use `Cache.once()` for optional or heavy modules that may not be needed
|
|
170
|
+
3. Use `Cache.memoize()` for resources that share structure but need separate instances
|
|
171
|
+
4. Keep memoize keys simple and predictable - avoid dynamic or timestamp-based keys
|
|
172
|
+
5. Be aware that `Cache.memoize()` instances stay in memory - use only for a finite set of keys
|
|
865
173
|
|
|
866
|
-
|
|
174
|
+
---
|
|
867
175
|
|
|
868
176
|
## Next Steps
|
|
869
177
|
|
|
870
|
-
- **[
|
|
871
|
-
- **[
|
|
872
|
-
- **[Observables](observables.md)** - Reactive state management
|
|
873
|
-
- **[Elements](elements.md)** - Creating and composing UI
|
|
874
|
-
- **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
|
|
875
|
-
- **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
|
|
876
|
-
- **[Routing](routing.md)** - Navigation and URL management
|
|
877
|
-
- **[State Management](state-management.md)** - Global state patterns
|
|
878
|
-
- **[NDElement](native-document-element.md)** - Native Document Element
|
|
879
|
-
- **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
|
|
880
|
-
- **[Advanced Components](advanced-components.md)** - Template caching and singleton views
|
|
881
|
-
- **[Args Validation](validation.md)** - Function Argument Validation
|
|
882
|
-
- **[Memory Management](memory-management.md)** - Memory management
|
|
883
|
-
|
|
884
|
-
## Utilities
|
|
885
|
-
|
|
886
|
-
- **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
|
|
887
|
-
- **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
|
|
888
|
-
- **[Filters](docs/utils/filters.md)** - Data filtering helpers
|
|
178
|
+
- **[NativeFetch](./native-fetch.md)** - HTTP client with interceptors
|
|
179
|
+
- **[State Management](./state-management.md)** - Global state with Store
|
|
180
|
+
- **[Observables](./observables.md)** - Reactive state management
|