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.
Files changed (108) hide show
  1. package/.vitepress/config.js +166 -0
  2. package/CHANGELOG.md +153 -0
  3. package/components.js +2 -1
  4. package/dist/native-document.components.min.js +495 -228
  5. package/dist/native-document.dev.js +7 -0
  6. package/dist/native-document.dev.js.map +1 -1
  7. package/dist/native-document.min.js +1 -1
  8. package/docs/advanced-components.md +213 -608
  9. package/docs/anchor.md +173 -312
  10. package/docs/cache.md +95 -803
  11. package/docs/cli.md +179 -0
  12. package/docs/components/accordion.md +172 -0
  13. package/docs/components/alert.md +99 -0
  14. package/docs/components/avatar.md +160 -0
  15. package/docs/components/badge.md +102 -0
  16. package/docs/components/breadcrumb.md +89 -0
  17. package/docs/components/button.md +183 -0
  18. package/docs/components/card.md +69 -0
  19. package/docs/components/context-menu.md +118 -0
  20. package/docs/components/data-table.md +345 -0
  21. package/docs/components/dropdown.md +214 -0
  22. package/docs/components/form/autocomplete-field.md +81 -0
  23. package/docs/components/form/checkbox-field.md +41 -0
  24. package/docs/components/form/checkbox-group-field.md +54 -0
  25. package/docs/components/form/color-field.md +64 -0
  26. package/docs/components/form/date-field.md +92 -0
  27. package/docs/components/form/field-collection.md +63 -0
  28. package/docs/components/form/file-field.md +203 -0
  29. package/docs/components/form/form-control.md +87 -0
  30. package/docs/components/form/image-field.md +90 -0
  31. package/docs/components/form/index.md +115 -0
  32. package/docs/components/form/number-field.md +65 -0
  33. package/docs/components/form/radio-field.md +51 -0
  34. package/docs/components/form/select-field.md +123 -0
  35. package/docs/components/form/slider.md +136 -0
  36. package/docs/components/form/string-field.md +134 -0
  37. package/docs/components/form/textarea-field.md +65 -0
  38. package/docs/components/form-fields.md +372 -0
  39. package/docs/components/getting-started.md +264 -0
  40. package/docs/components/index.md +337 -0
  41. package/docs/components/layout.md +279 -0
  42. package/docs/components/list.md +73 -0
  43. package/docs/components/menu.md +215 -0
  44. package/docs/components/modal.md +156 -0
  45. package/docs/components/pagination.md +95 -0
  46. package/docs/components/popover.md +131 -0
  47. package/docs/components/progress.md +111 -0
  48. package/docs/components/shortcut-manager.md +221 -0
  49. package/docs/components/simple-table.md +107 -0
  50. package/docs/components/skeleton.md +155 -0
  51. package/docs/components/spinner.md +100 -0
  52. package/docs/components/splitter.md +133 -0
  53. package/docs/components/stepper.md +163 -0
  54. package/docs/components/switch.md +113 -0
  55. package/docs/components/tabs.md +153 -0
  56. package/docs/components/toast.md +119 -0
  57. package/docs/components/tooltip.md +151 -0
  58. package/docs/components/traits.md +261 -0
  59. package/docs/conditional-rendering.md +170 -588
  60. package/docs/contributing.md +300 -25
  61. package/docs/core-concepts.md +205 -374
  62. package/docs/elements.md +251 -367
  63. package/docs/extending-native-document-element.md +192 -207
  64. package/docs/filters.md +153 -1122
  65. package/docs/getting-started.md +193 -267
  66. package/docs/i18n.md +241 -0
  67. package/docs/index.md +76 -0
  68. package/docs/lifecycle-events.md +143 -75
  69. package/docs/list-rendering.md +227 -852
  70. package/docs/memory-management.md +134 -47
  71. package/docs/native-document-element.md +337 -186
  72. package/docs/native-fetch.md +99 -630
  73. package/docs/observable-resource.md +364 -0
  74. package/docs/observables.md +592 -526
  75. package/docs/routing.md +244 -653
  76. package/docs/state-management.md +134 -241
  77. package/docs/svg-elements.md +231 -0
  78. package/docs/theming.md +409 -0
  79. package/docs/tutorials/.gitkeep +0 -0
  80. package/docs/validation.md +95 -97
  81. package/docs/vitepress-conventions.md +219 -0
  82. package/package.json +34 -13
  83. package/readme.md +269 -89
  84. package/src/components/card/Card.js +93 -39
  85. package/src/components/card/index.js +1 -1
  86. package/src/components/list/HasListItem.js +171 -0
  87. package/src/components/list/List.js +41 -107
  88. package/src/components/list/ListDivider.js +39 -0
  89. package/src/components/list/ListGroup.js +76 -59
  90. package/src/components/list/ListItem.js +117 -69
  91. package/src/components/list/index.js +3 -1
  92. package/src/components/list/types/ListItem.d.ts +45 -34
  93. package/src/components/spacer/Spacer.js +1 -1
  94. package/src/core/data/ObservableResource.js +5 -0
  95. package/src/core/data/observable-helpers/observable.prototypes.js +2 -0
  96. package/src/ui/components/card/CardRender.js +133 -0
  97. package/src/ui/components/card/card.css +169 -0
  98. package/src/ui/components/contextmenu/ContextmenuRender.js +1 -1
  99. package/src/ui/components/list/ListRender.js +18 -0
  100. package/src/ui/components/list/divider/ListDividerRender.js +10 -0
  101. package/src/ui/components/list/divider/list-divider.css +12 -0
  102. package/src/ui/components/list/group/ListGroupRender.js +61 -0
  103. package/src/ui/components/list/group/list-group.css +62 -0
  104. package/src/ui/components/list/item/ListItemRender.js +238 -0
  105. package/src/ui/components/list/item/list-item.css +191 -0
  106. package/src/ui/components/list/list.css +24 -0
  107. package/src/ui/components/spacer/SpacerRender.js +10 -0
  108. package/src/ui/index.js +8 -0
@@ -1,103 +1,66 @@
1
- # NativeFetch
2
-
3
- NativeFetch is a powerful HTTP client built on top of the native Fetch API, providing a clean interface with interceptors, automatic request/response handling, and advanced features for modern web applications.
1
+ ---
2
+ title: NativeFetch
3
+ description: HTTP client built on the native Fetch API with request and response interceptors
4
+ ---
4
5
 
5
- ## Overview
6
+ # NativeFetch
6
7
 
7
- NativeFetch enhances the standard Fetch API with:
8
- - **Interceptors** - Request and response transformation
9
- - **Base URL** - Centralized endpoint configuration
10
- - **Automatic JSON handling** - Parse and stringify automatically
11
- - **Error handling** - Consistent error responses
12
- - **Request cancellation** - AbortController integration
13
- - **TypeScript-friendly** - Clean, predictable API
8
+ NativeFetch is an HTTP client built on top of the native Fetch API. It adds interceptors, a base URL, and automatic JSON handling.
14
9
 
15
- ## Import
16
10
  ```javascript
17
- import { NativeFetch } from 'native-document/utils';
18
- ```
19
-
20
- ## Basic Usage
11
+ import { utils } from 'native-document';
12
+ const { NativeFetch } = utils;
21
13
 
22
- ### Creating an Instance
23
- ```javascript
14
+ // Or
24
15
  import { NativeFetch } from 'native-document/utils';
16
+ ```
25
17
 
26
- // Create client with base URL
27
- const api = new NativeFetch('https://api.example.com');
18
+ ---
28
19
 
29
- // Make requests
30
- const users = await api.get('/users');
31
- const user = await api.get('/users/123');
32
- const newUser = await api.post('/users', { name: 'Alice', email: 'alice@example.com' });
33
- ```
20
+ ## Creating an Instance
34
21
 
35
- ### HTTP Methods
36
22
  ```javascript
37
- import { NativeFetch } from 'native-document/utils';
38
-
39
23
  const api = new NativeFetch('https://api.example.com');
24
+ ```
40
25
 
41
- // GET request
42
- const data = await api.get('/endpoint');
43
- const dataWithParams = await api.get('/endpoint', { page: 1, limit: 10 });
26
+ ---
44
27
 
45
- // POST request
46
- const created = await api.post('/endpoint', {
47
- name: 'New Item',
48
- description: 'Description'
49
- });
28
+ ## HTTP Methods
50
29
 
51
- // PUT request
52
- const updated = await api.put('/endpoint/123', {
53
- name: 'Updated Item'
54
- });
55
-
56
- // PATCH request
57
- const patched = await api.patch('/endpoint/123', {
58
- status: 'active'
59
- });
30
+ ```javascript
31
+ // GET
32
+ const users = await api.get('/users');
33
+ const user = await api.get('/users/123');
60
34
 
61
- // DELETE request
62
- const deleted = await api.delete('/endpoint/123');
63
- ```
35
+ // GET with query parameters
36
+ const page = await api.get('/users', { page: 1, limit: 20, sort: 'name' });
37
+ // -> GET /users?page=1&limit=20&sort=name
64
38
 
65
- ### Query Parameters
66
- ```javascript
67
- import { NativeFetch } from 'native-document/utils';
39
+ // POST
40
+ const newUser = await api.post('/users', { name: 'Alice', email: 'alice@example.com' });
68
41
 
69
- const api = new NativeFetch('https://api.example.com');
42
+ // PUT
43
+ const updated = await api.put('/users/123', { name: 'Alice Updated' });
70
44
 
71
- // Pass query parameters as object
72
- const users = await api.get('/users', {
73
- page: 1,
74
- limit: 20,
75
- sort: 'name',
76
- filter: 'active'
77
- });
78
- // Request: GET /users?page=1&limit=20&sort=name&filter=active
45
+ // PATCH
46
+ const patched = await api.patch('/users/123', { status: 'active' });
79
47
 
80
- // Array parameters
81
- const posts = await api.get('/posts', {
82
- tags: ['javascript', 'tutorial'],
83
- categories: ['tech', 'programming']
84
- });
85
- // Request: GET /posts?tags=javascript,tutorial&categories=tech,programming
48
+ // DELETE
49
+ await api.delete('/users/123');
86
50
  ```
87
51
 
52
+ ---
53
+
88
54
  ## Interceptors
89
55
 
90
- Interceptors allow you to transform requests before they're sent and responses before they're returned.
56
+ ### Request interceptors
91
57
 
92
- ### Request Interceptors
93
- ```javascript
94
- import { NativeFetch } from 'native-document/utils';
58
+ Run before the request is sent:
95
59
 
96
- const api = new NativeFetch('https://api.example.com');
97
-
98
- // Add authentication header
99
- api.interceptors.request((config) => {
100
- const token = localStorage.getItem('auth_token');
60
+ ```javascript
61
+ // Add authentication
62
+ api.interceptors.request(config => {
63
+ const token = localStorage.getItem('token');
101
64
  if (token) {
102
65
  config.headers['Authorization'] = `Bearer ${token}`;
103
66
  }
@@ -105,640 +68,146 @@ api.interceptors.request((config) => {
105
68
  });
106
69
 
107
70
  // Add custom headers
108
- api.interceptors.request((config) => {
71
+ api.interceptors.request(config => {
109
72
  config.headers['X-App-Version'] = '1.0.0';
110
- config.headers['X-Request-ID'] = generateRequestId();
111
- return config;
112
- });
113
-
114
- // Log requests
115
- api.interceptors.request((config) => {
116
- console.log(`[${config.method}] ${config.url}`);
117
73
  return config;
118
74
  });
119
75
  ```
120
76
 
121
- ### Response Interceptors
122
- ```javascript
123
- import { NativeFetch } from 'native-document/utils';
77
+ ### Response interceptors
124
78
 
125
- const api = new NativeFetch('https://api.example.com');
79
+ Run after the response is received:
126
80
 
127
- // Handle authentication errors
128
- api.interceptors.response((response) => {
81
+ ```javascript
82
+ // Handle 401
83
+ api.interceptors.response(response => {
129
84
  if (response.status === 401) {
130
- // Redirect to login
85
+ Store.get('auth').set({ user: null, token: null, isLoggedIn: false });
131
86
  window.location.href = '/login';
132
- throw new Error('Unauthorized');
133
87
  }
134
88
  return response;
135
89
  });
136
90
 
137
- // Transform response data
138
- api.interceptors.response((response) => {
139
- // Unwrap data from envelope
140
- if (response.data && response.data.result) {
141
- return {
142
- ...response,
143
- data: response.data.result
144
- };
145
- }
146
- return response;
147
- });
148
-
149
- // Log responses
150
- api.interceptors.response((response) => {
151
- console.log(`[${response.status}] ${response.url}`);
152
- return response;
153
- });
154
-
155
- // Handle errors globally
156
- api.interceptors.response((response) => {
157
- if (!response.ok) {
158
- console.error(`Error: ${response.status} - ${response.statusText}`);
91
+ // Unwrap data envelope
92
+ api.interceptors.response(response => {
93
+ if (response.data?.result) {
94
+ return { ...response, data: response.data.result };
159
95
  }
160
96
  return response;
161
97
  });
162
98
  ```
163
99
 
164
- ### Multiple Interceptors
165
- ```javascript
166
- import { NativeFetch } from 'native-document/utils';
167
-
168
- const api = new NativeFetch('https://api.example.com');
169
-
170
- // Interceptors are executed in order
171
- api.interceptors.request((config) => {
172
- console.log('Interceptor 1');
173
- return config;
174
- });
175
-
176
- api.interceptors.request((config) => {
177
- console.log('Interceptor 2');
178
- return config;
179
- });
180
-
181
- // When making a request:
182
- await api.get('/users');
183
- // Logs: "Interceptor 1"
184
- // Logs: "Interceptor 2"
185
- ```
186
-
187
- ## Advanced Features
188
-
189
- ### Custom Headers
190
- ```javascript
191
- import { NativeFetch } from 'native-document/utils';
192
-
193
- const api = new NativeFetch('https://api.example.com');
194
-
195
- // Set default headers
196
- api.interceptors.request((config) => {
197
- config.headers['Content-Type'] = 'application/json';
198
- config.headers['Accept'] = 'application/json';
199
- return config;
200
- });
201
-
202
- // Per-request headers
203
- const data = await api.get('/endpoint', {}, {
204
- headers: {
205
- 'X-Custom-Header': 'value'
206
- }
207
- });
208
- ```
209
-
210
- ### Retry Logic
211
- ```javascript
212
- import { NativeFetch } from 'native-document/utils';
100
+ Multiple interceptors run in the order they were registered.
213
101
 
214
- const api = new NativeFetch('https://api.example.com');
102
+ ---
215
103
 
216
- // Add retry logic
217
- api.interceptors.response(async (response) => {
218
- if (response.status === 429) {
219
- // Rate limited - wait and retry
220
- const retryAfter = response.headers.get('Retry-After') || 1;
221
- await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
222
-
223
- // Retry the request
224
- return fetch(response.url, response.config);
225
- }
226
- return response;
227
- });
228
- ```
104
+ ## Practical Example - API Service Layer
229
105
 
230
- ## Practical Examples
106
+ Combine with `Cache` for a clean service pattern:
231
107
 
232
- ### Authentication Flow
233
108
  ```javascript
234
- import { NativeFetch } from 'native-document/utils';
109
+ import { Cache, NativeFetch } from 'native-document/utils';
235
110
  import { Store } from 'native-document';
236
111
 
237
- const api = new NativeFetch('https://api.example.com');
238
-
239
- // Create auth store
240
- Store.create('auth', {
241
- user: null,
242
- token: null,
243
- isAuthenticated: false
244
- });
245
-
246
- // Add token to requests
247
- api.interceptors.request((config) => {
248
- const auth = Store.get('auth').val();
249
- if (auth.token) {
250
- config.headers['Authorization'] = `Bearer ${auth.token}`;
251
- }
252
- return config;
253
- });
254
-
255
- // Handle unauthorized
256
- api.interceptors.response((response) => {
257
- if (response.status === 401) {
258
- const auth = Store.get('auth');
259
- auth.set({
260
- user: null,
261
- token: null,
262
- isAuthenticated: false
263
- });
264
- window.location.href = '/login';
265
- }
266
- return response;
267
- });
268
-
269
- // Login function
270
- async function login(email, password) {
271
- const { data } = await api.post('/auth/login', { email, password });
272
-
273
- const auth = Store.get('auth');
274
- auth.set({
275
- user: data.user,
276
- token: data.token,
277
- isAuthenticated: true
278
- });
279
-
280
- return data;
281
- }
282
-
283
- // Logout function
284
- async function logout() {
285
- await api.post('/auth/logout');
286
-
287
- const auth = Store.get('auth');
288
- auth.set({
289
- user: null,
290
- token: null,
291
- isAuthenticated: false
292
- });
293
- }
294
- ```
295
-
296
- ### API Service Layer
297
- ```javascript
298
- import { NativeFetch } from 'native-document/utils';
299
- import { Cache } from 'native-document/utils';
300
-
301
- // Create API client singleton
112
+ // Singleton API client
302
113
  const getAPI = Cache.singleton(() => {
303
114
  const client = new NativeFetch('https://api.example.com');
304
-
305
- // Add interceptors
306
- client.interceptors.request((config) => {
307
- config.headers['Content-Type'] = 'application/json';
308
- const token = localStorage.getItem('token');
115
+
116
+ client.interceptors.request(config => {
117
+ const token = Store.get('auth').$value?.token;
309
118
  if (token) {
310
119
  config.headers['Authorization'] = `Bearer ${token}`;
311
120
  }
312
121
  return config;
313
122
  });
314
-
123
+
124
+ client.interceptors.response(response => {
125
+ if (response.status === 401) {
126
+ Store.reset('auth');
127
+ Router.push({ name: 'login' });
128
+ }
129
+ return response;
130
+ });
131
+
315
132
  return client;
316
133
  });
317
134
 
318
- // API endpoints by resource
319
- export const API = Cache.memoize((resource) => {
135
+ // Resource endpoints via memoize
136
+ const API = Cache.memoize(resource => {
320
137
  const client = getAPI();
321
-
322
138
  return {
323
139
  list: (params = {}) => client.get(`/${resource}`, params),
324
140
  get: (id) => client.get(`/${resource}/${id}`),
325
141
  create: (data) => client.post(`/${resource}`, data),
326
142
  update: (id, data) => client.put(`/${resource}/${id}`, data),
327
- patch: (id, data) => client.patch(`/${resource}/${id}`, data),
328
143
  delete: (id) => client.delete(`/${resource}/${id}`)
329
144
  };
330
145
  });
331
146
 
332
147
  // Usage
333
- const users = await API.users.list({ page: 1, limit: 10 });
148
+ const users = await API.users.list({ page: 1 });
334
149
  const user = await API.users.get('123');
335
150
  const newUser = await API.users.create({ name: 'Alice' });
336
- await API.users.update('123', { name: 'Alice Updated' });
337
151
  await API.users.delete('123');
338
152
  ```
339
153
 
340
- ### Loading States
341
- ```javascript
342
- import { NativeFetch } from 'native-document/utils';
343
- import { Observable } from 'native-document';
154
+ ---
344
155
 
345
- const api = new NativeFetch('https://api.example.com');
156
+ ## Loading State with Observables
346
157
 
158
+ ```javascript
347
159
  const isLoading = Observable(false);
348
- const loadingCount = Observable(0);
160
+ const requestCount = Observable(0);
349
161
 
350
- // Track loading state
351
- api.interceptors.request((config) => {
352
- loadingCount.set(loadingCount.val() + 1);
162
+ api.interceptors.request(config => {
163
+ requestCount.set(requestCount.val() + 1);
353
164
  isLoading.set(true);
354
165
  return config;
355
166
  });
356
167
 
357
- api.interceptors.response((response) => {
358
- const count = loadingCount.val() - 1;
359
- loadingCount.set(count);
168
+ api.interceptors.response(response => {
169
+ const count = requestCount.val() - 1;
170
+ requestCount.set(count);
360
171
  if (count === 0) {
361
172
  isLoading.set(false);
362
173
  }
363
174
  return response;
364
175
  });
365
176
 
366
- // Use in UI
367
- import { Div } from 'native-document/src/elements';
368
- import { ShowIf } from 'native-document';
369
-
370
- const LoadingIndicator = Div({ class: 'loading-indicator' }, [
371
- ShowIf(isLoading, () =>
372
- Div({ class: 'spinner' }, 'Loading...')
373
- )
374
- ]);
177
+ ShowIf(isLoading.isTruthy(), Div({ class: 'spinner' }, 'Loading...'))
375
178
  ```
376
179
 
377
- ### Error Handling
378
- ```javascript
379
- import { NativeFetch } from 'native-document/utils';
380
- import { Observable } from 'native-document';
381
-
382
- const api = new NativeFetch('https://api.example.com');
383
- const globalError = Observable(null);
384
-
385
- // Global error handler
386
- api.interceptors.response((response) => {
387
- if (!response.ok) {
388
- const error = {
389
- status: response.status,
390
- message: response.statusText,
391
- url: response.url
392
- };
393
-
394
- globalError.set(error);
395
-
396
- // Auto-clear after 5 seconds
397
- setTimeout(() => globalError.set(null), 5000);
398
- }
399
- return response;
400
- });
401
-
402
- // Error display component
403
- import { Div } from 'native-document/src/elements';
404
- import { ShowIf } from 'native-document';
405
-
406
- const ErrorNotification = Div([
407
- ShowIf(globalError, () =>
408
- Div({
409
- class: 'error-notification',
410
- style: {
411
- position: 'fixed',
412
- top: '20px',
413
- right: '20px',
414
- background: '#ff4444',
415
- color: 'white',
416
- padding: '16px',
417
- borderRadius: '8px'
418
- }
419
- }, [
420
- globalError.check(err => err ? err.message : '')
421
- ])
422
- )
423
- ]);
424
- ```
425
-
426
- ### Request Logging
427
- ```javascript
428
- import { NativeFetch } from 'native-document/utils';
429
-
430
- const api = new NativeFetch('https://api.example.com');
431
-
432
- // Log all requests
433
- api.interceptors.request((config) => {
434
- console.group(`🔵 ${config.method} ${config.url}`);
435
- console.log('Headers:', config.headers);
436
- console.log('Body:', config.body);
437
- console.log('Timestamp:', new Date().toISOString());
438
- console.groupEnd();
439
- return config;
440
- });
441
-
442
- // Log all responses
443
- api.interceptors.response((response) => {
444
- const color = response.ok ? '🟢' : '🔴';
445
- console.group(`${color} ${response.status} ${response.url}`);
446
- console.log('Status:', response.statusText);
447
- console.log('Data:', response.data);
448
- console.log('Duration:', /* calculate duration */);
449
- console.groupEnd();
450
- return response;
451
- });
452
- ```
453
-
454
- ### Analytics Tracking
455
- ```javascript
456
- import { NativeFetch } from 'native-document/utils';
457
-
458
- const api = new NativeFetch('https://api.example.com');
459
-
460
- // Track API calls
461
- api.interceptors.request((config) => {
462
- config._startTime = Date.now();
463
- return config;
464
- });
465
-
466
- api.interceptors.response((response) => {
467
- const duration = Date.now() - response.config._startTime;
468
-
469
- // Send to analytics
470
- if (window.analytics) {
471
- window.analytics.track('api_request', {
472
- method: response.config.method,
473
- endpoint: response.url,
474
- status: response.status,
475
- duration: duration,
476
- success: response.ok
477
- });
478
- }
479
-
480
- return response;
481
- });
482
- ```
180
+ ---
483
181
 
484
182
  ## Response Format
485
183
 
486
- NativeFetch automatically parses JSON responses and provides a consistent response object:
487
- ```javascript
488
- {
489
- ok: boolean, // true if status 200-299
490
- status: number, // HTTP status code
491
- statusText: string, // Status message
492
- headers: Headers, // Response headers
493
- data: any, // Parsed JSON data
494
- url: string, // Request URL
495
- config: object // Original request config
496
- }
497
- ```
184
+ NativeFetch returns a consistent response object:
498
185
 
499
- ### Handling Different Response Types
500
186
  ```javascript
501
- import { NativeFetch } from 'native-document/utils';
502
-
503
- const api = new NativeFetch('https://api.example.com');
504
-
505
- // JSON response (default)
506
- const json = await api.get('/data');
507
- console.log(json.data);
508
-
509
- // Text response
510
- const response = await fetch('https://api.example.com/text');
511
- const text = await response.text();
512
-
513
- // Blob response (files, images)
514
- const blobResponse = await fetch('https://api.example.com/image.png');
515
- const blob = await blobResponse.blob();
516
- const imageUrl = URL.createObjectURL(blob);
517
- ```
518
-
519
- ## Error Handling
520
-
521
- ### Try-Catch Pattern
522
- ```javascript
523
- import { NativeFetch } from 'native-document/utils';
524
-
525
- const api = new NativeFetch('https://api.example.com');
526
-
527
- try {
528
- const data = await api.get('/endpoint');
529
- console.log('Success:', data);
530
- } catch (error) {
531
- if (error.response) {
532
- // Server responded with error
533
- console.error('Server error:', error.response.status);
534
- console.error('Message:', error.response.data);
535
- } else if (error.request) {
536
- // Request made but no response
537
- console.error('Network error:', error.message);
538
- } else {
539
- // Other errors
540
- console.error('Error:', error.message);
541
- }
187
+ {
188
+ ok: boolean, // true if status 200-299
189
+ status: number, // HTTP status code
190
+ statusText: string, // status message
191
+ headers: Headers, // response headers
192
+ data: any, // parsed JSON body
193
+ url: string // request URL
542
194
  }
543
195
  ```
544
196
 
545
- ### Async Error Boundaries
546
- ```javascript
547
- import { NativeFetch } from 'native-document/utils';
548
- import { Observable } from 'native-document';
549
-
550
- const api = new NativeFetch('https://api.example.com');
551
-
552
- async function fetchData() {
553
- const data = Observable(null);
554
- const error = Observable(null);
555
- const loading = Observable(true);
556
-
557
- try {
558
- const response = await api.get('/data');
559
- data.set(response.data);
560
- } catch (err) {
561
- error.set(err.message);
562
- } finally {
563
- loading.set(false);
564
- }
565
-
566
- return { data, error, loading };
567
- }
568
- ```
197
+ ---
569
198
 
570
199
  ## Best Practices
571
200
 
572
- ### 1. Create Singleton API Client
573
- ```javascript
574
- import { Cache } from 'native-document/utils';
575
- import { NativeFetch } from 'native-document/utils';
576
-
577
- // ✅ Good: Single instance with interceptors
578
- const getAPI = Cache.singleton(() => {
579
- const client = new NativeFetch('https://api.example.com');
580
-
581
- client.interceptors.request((config) => {
582
- // Add auth header
583
- return config;
584
- });
585
-
586
- return client;
587
- });
588
-
589
- // ❌ Bad: New instance every time
590
- function makeRequest() {
591
- const api = new NativeFetch('https://api.example.com');
592
- return api.get('/data');
593
- }
594
- ```
595
-
596
- ### 2. Use Resource-Based Endpoints
597
- ```javascript
598
- import { Cache } from 'native-document/utils';
599
-
600
- // ✅ Good: Organized by resource
601
- const API = Cache.memoize((resource) => ({
602
- list: () => api.get(`/${resource}`),
603
- get: (id) => api.get(`/${resource}/${id}`),
604
- create: (data) => api.post(`/${resource}`, data)
605
- }));
606
-
607
- // ❌ Bad: Scattered API calls
608
- async function getUsers() {
609
- return api.get('/users');
610
- }
611
- async function getUser(id) {
612
- return api.get('/users/' + id);
613
- }
614
- ```
615
-
616
- ### 3. Handle Errors Consistently
617
- ```javascript
618
- import { NativeFetch } from 'native-document/utils';
619
-
620
- const api = new NativeFetch('https://api.example.com');
621
-
622
- // ✅ Good: Centralized error handling
623
- api.interceptors.response((response) => {
624
- if (!response.ok) {
625
- handleError(response);
626
- }
627
- return response;
628
- });
629
-
630
- // ❌ Bad: Error handling in every request
631
- try {
632
- await api.get('/endpoint1');
633
- } catch (error) {
634
- showError(error);
635
- }
636
-
637
- try {
638
- await api.get('/endpoint2');
639
- } catch (error) {
640
- showError(error);
641
- }
642
- ```
643
-
644
- ### 4. Use Query Parameters Object
645
- ```javascript
646
- import { NativeFetch } from 'native-document/utils';
647
-
648
- const api = new NativeFetch('https://api.example.com');
649
-
650
- // ✅ Good: Clean object syntax
651
- const users = await api.get('/users', {
652
- page: 1,
653
- limit: 20,
654
- sort: 'name'
655
- });
656
-
657
- // ❌ Bad: Manual query string
658
- const users = await api.get('/users?page=1&limit=20&sort=name');
659
- ```
660
-
661
- ### 5. Type Your Responses
662
- ```javascript
663
- import { NativeFetch } from 'native-document/utils';
664
-
665
- const api = new NativeFetch('https://api.example.com');
666
-
667
- // ✅ Good: Document expected response
668
- /**
669
- * @returns {Promise<{data: User[]}>}
670
- */
671
- async function getUsers() {
672
- return await api.get('/users');
673
- }
674
-
675
- // Use with confidence
676
- const { data } = await getUsers();
677
- data.forEach(user => console.log(user.name));
678
- ```
679
-
680
- ## Performance Tips
681
-
682
- ### 1. Reuse Client Instances
683
- ```javascript
684
- import { Cache } from 'native-document/utils';
685
-
686
- // ✅ Singleton - created once
687
- const getAPI = Cache.singleton(() => new NativeFetch('https://api.example.com'));
688
- ```
689
-
690
- ### 2. Cancel Unnecessary Requests
691
- ```javascript
692
- let controller = new AbortController();
693
-
694
- async function search(query) {
695
- // Cancel previous request
696
- controller.abort();
697
- controller = new AbortController();
698
-
699
- return await api.get('/search', { q: query }, {
700
- signal: controller.signal
701
- });
702
- }
703
- ```
704
-
705
- ### 3. Batch Related Requests
706
- ```javascript
707
- // ✅ Good: Batch requests
708
- const [users, posts, comments] = await Promise.all([
709
- api.get('/users'),
710
- api.get('/posts'),
711
- api.get('/comments')
712
- ]);
713
-
714
- // ❌ Bad: Sequential requests
715
- const users = await api.get('/users');
716
- const posts = await api.get('/posts');
717
- const comments = await api.get('/comments');
718
- ```
719
-
720
- ## Next Steps
201
+ 1. Create a single instance via `Cache.singleton()` - not a new instance per request
202
+ 2. Use `Cache.memoize()` for resource-based endpoint collections
203
+ 3. Handle authentication and errors in interceptors - not in each call
204
+ 4. Use `Promise.all()` for parallel requests instead of sequential awaits
721
205
 
722
- Explore related utilities and concepts:
206
+ ---
723
207
 
724
208
  ## Next Steps
725
209
 
726
- - **[Getting Started](getting-started.md)** - Installation and first steps
727
- - **[Core Concepts](core-concepts.md)** - Understanding the fundamentals
728
- - **[Observables](observables.md)** - Reactive state management
729
- - **[Elements](elements.md)** - Creating and composing UI
730
- - **[Conditional Rendering](conditional-rendering.md)** - Dynamic content
731
- - **[List Rendering](list-rendering.md)** - (ForEach | ForEachArray) and dynamic lists
732
- - **[Routing](routing.md)** - Navigation and URL management
733
- - **[State Management](state-management.md)** - Global state patterns
734
- - **[NDElement](native-document-element.md)** - Native Document Element
735
- - **[Extending NDElement](extending-native-document-element.md)** - Custom Methods Guide
736
- - **[Advanced Components](advanced-components.md)** - Template caching and singleton views
737
- - **[Args Validation](validation.md)** - Function Argument Validation
738
- - **[Memory Management](memory-management.md)** - Memory management
739
-
740
- ## Utilities
741
-
742
- - **[Cache](docs/utils/cache.md)** - Lazy initialization and singleton patterns
743
- - **[NativeFetch](docs/utils/native-fetch.md)** - HTTP client with interceptors
744
- - **[Filters](docs/utils/filters.md)** - Data filtering helpers
210
+ - **[Cache](./cache.md)** - Lazy initialization and singleton patterns
211
+ - **[Filters](./filters.md)** - Data filtering helpers
212
+ - **[State Management](./state-management.md)** - Global state with Store
213
+ - **[Observable Resource](./observable-resource.md)** - Async data with built-in states