@jayethian/axiom 0.1.0 â 0.1.2
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 +356 -1
- package/dist/index.d.mts +85 -13
- package/dist/index.d.ts +85 -13
- package/dist/index.js +92 -34
- package/dist/index.mjs +92 -34
- package/package.json +26 -5
- package/.editorconfig +0 -8
- package/.gitattributes +0 -4
- package/src/adapters/index.ts +0 -15
- package/src/adapters/memory.ts +0 -22
- package/src/engine/fetcher.ts +0 -131
- package/src/engine/sync.ts +0 -96
- package/src/index.ts +0 -6
- package/src/react/AxiomProvider.tsx +0 -57
- package/src/react/useAxiomQueue.ts +0 -12
- package/src/types.ts +0 -20
- package/tsconfig.json +0 -16
package/README.md
CHANGED
|
@@ -1 +1,356 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jayethian/axiom/main/assets/logo.png" alt="axiom logo" width="400" />
|
|
3
|
+
<h1>Axiom</h1>
|
|
4
|
+
|
|
5
|
+
<h3>Resilient, offline-first networking for modern React, Next.js, and React Native apps.</h3>
|
|
6
|
+
|
|
7
|
+
<p>
|
|
8
|
+
<a href="https://www.npmjs.com/package/@jayethian/axiom">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/@jayethian/axiom.svg?style=flat-square" alt="npm version" />
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@jayethian/axiom">
|
|
12
|
+
<img src="https://img.shields.io/npm/dm/@jayethian/axiom.svg?style=flat-square" alt="npm downloads" />
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://opensource.org/licenses/MIT">
|
|
15
|
+
<img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License: MIT" />
|
|
16
|
+
</a>
|
|
17
|
+
<img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg?style=flat-square" alt="TypeScript" />
|
|
18
|
+
</p>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<br />
|
|
22
|
+
|
|
23
|
+
## The Problem
|
|
24
|
+
Standard HTTP clients like **Axios** or **Fetch** assume a stable connection. When a user submits data in a dead zone (elevators, basements, rural areas), the request simply fails. Without a complex, manual retry system written from scratch, **that data is gone forever.**
|
|
25
|
+
|
|
26
|
+
## The Axiom Way
|
|
27
|
+
Axiom intercepts network failures and timeouts. Instead of throwing an error, it serializes the request and safely moves it to a persistent local queue. When the connection returns, Axiom flushes the queue automatically, in the background.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Standard Fetch: Fails and loses data when offline.
|
|
31
|
+
await fetch('/api/orders', { method: 'POST', body: data });
|
|
32
|
+
|
|
33
|
+
// Axiom: Intercepts drop, queues safely, and syncs when back online.
|
|
34
|
+
await axiom.post('/api/orders', data);
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Table of Contents
|
|
41
|
+
|
|
42
|
+
1. [Features](https://www.google.com/search?q=%23-features)
|
|
43
|
+
2. [Installation](https://www.google.com/search?q=%23-installation)
|
|
44
|
+
3. [React / Next.js Setup](https://www.google.com/search?q=%23-react--nextjs-setup-zero-config)
|
|
45
|
+
4. [React Native Setup](https://www.google.com/search?q=%23-react-native-setup)
|
|
46
|
+
5. [Vanilla JS / Node Setup](https://www.google.com/search?q=%23-vanilla-js--node-setup)
|
|
47
|
+
6. [Core Hooks (`useAxiomQueue`)](https://www.google.com/search?q=%23-core-hooks)
|
|
48
|
+
7. [Advanced Architecture](https://www.google.com/search?q=%23-advanced-architecture)
|
|
49
|
+
* [Global Interceptors](https://www.google.com/search?q=%23global-interceptors)
|
|
50
|
+
* [Priority Lanes](https://www.google.com/search?q=%23priority-lanes)
|
|
51
|
+
* [Queue Inspection & "Outbox" UI](https://www.google.com/search?q=%23queue-inspection--outbox-ui)
|
|
52
|
+
* [Storage Adapters (MMKV, IndexedDB)](https://www.google.com/search?q=%23storage-adapters)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
8. [API Reference](https://www.google.com/search?q=%23-api-reference)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
* **ðą Mobile-First Resilience:** Specifically tuned to handle spotty connectivity, aggressive timeouts, and background execution.
|
|
62
|
+
* **ð§ Smart Fallback Storage:** Automatically detects your environment and falls back to the safest storage (`IndexedDB` for Web, `Memory` for SSR/React Native) without crashing.
|
|
63
|
+
* **ð Autonomous Background Sync:** Replays the queue the moment a signal is detected.
|
|
64
|
+
* **⥠Priority Lanes:** Ensure critical data (e.g., payments) jumps to the front of the queue ahead of background tasks (e.g., analytics).
|
|
65
|
+
* **ðĄïļ Just-In-Time Headers:** Refresh Auth Tokens immediately before syncing to prevent `401 Unauthorized` errors on delayed requests.
|
|
66
|
+
* **ð Global Interceptors:** Catch success and error events globally, even when requests resolve in the background hours later.
|
|
67
|
+
* **ðŠĶ Dead Letter Queues:** Protects your app from infinite loops by isolating permanently failing requests and exposing them to the UI for user intervention.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install @jayethian/axiom
|
|
75
|
+
# or
|
|
76
|
+
yarn add @jayethian/axiom
|
|
77
|
+
# or
|
|
78
|
+
pnpm add @jayethian/axiom
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## React & Next.js Setup (Zero-Config)
|
|
85
|
+
Axiom includes a built-in event listener that automatically binds to the browser's `window.addEventListener('online')` APIs. For Next.js and standard React Web apps, setup requires zero boilerplate.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// App.tsx or layout.tsx
|
|
89
|
+
import { AxiomProvider } from '@jayethian/axiom';
|
|
90
|
+
|
|
91
|
+
export default function App({ children }) {
|
|
92
|
+
return (
|
|
93
|
+
<AxiomProvider
|
|
94
|
+
config={{
|
|
95
|
+
baseURL: '[https://api.myapp.com](https://api.myapp.com)',
|
|
96
|
+
timeout: 8000
|
|
97
|
+
}}
|
|
98
|
+
// Automatically uses IndexedDB, falls back to LocalStorage if in Private Browsing
|
|
99
|
+
fallbackAdapter="indexeddb"
|
|
100
|
+
>
|
|
101
|
+
{children}
|
|
102
|
+
</AxiomProvider>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## React Native Setup
|
|
111
|
+
|
|
112
|
+
React Native does not have a native DOM `window`, so you must provide a network listener (like `@react-native-community/netinfo`) and a persistent storage adapter (like `react-native-mmkv` or `AsyncStorage`).
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { AxiomProvider } from '@jayethian/axiom';
|
|
116
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
117
|
+
import { MMKVAdapter } from './my-adapters'; // See Storage Adapters below
|
|
118
|
+
|
|
119
|
+
export default function App({ children }) {
|
|
120
|
+
return (
|
|
121
|
+
<AxiomProvider
|
|
122
|
+
config={{ baseURL: '[https://api.myapp.com](https://api.myapp.com)' }}
|
|
123
|
+
storageAdapter={new MMKVAdapter()}
|
|
124
|
+
networkListener={(callback) => {
|
|
125
|
+
return NetInfo.addEventListener(state => callback(!!state.isConnected));
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
{children}
|
|
129
|
+
</AxiomProvider>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Vanilla JS / Node Setup
|
|
138
|
+
|
|
139
|
+
You do not need React to use Axiom. You can instantiate the engine directly and use our built-in **Event Emitter** to listen for background syncs.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { axiom } from '@jayethian/axiom';
|
|
143
|
+
|
|
144
|
+
// 1. Initialize
|
|
145
|
+
axiom.create({ baseURL: '[https://api.myapp.com](https://api.myapp.com)' });
|
|
146
|
+
|
|
147
|
+
// 2. Listen to Background Events
|
|
148
|
+
axiom.on('syncSuccess', (data, req) => {
|
|
149
|
+
console.log(`Background sync finished for ${req.url}`);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
axiom.on('deadLetter', (req) => {
|
|
153
|
+
console.error(`Request permanently failed after 3 retries:`, req);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// 3. Make Requests
|
|
157
|
+
const response = await axiom.post('/users', { name: 'John' });
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Core Hooks
|
|
164
|
+
|
|
165
|
+
The `useAxiomQueue` hook gives your UI complete visibility into the background engine. Keep your users informed when they are working offline.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { axiom, useAxiomQueue } from '@jayethian/axiom';
|
|
169
|
+
|
|
170
|
+
export function CheckoutButton() {
|
|
171
|
+
const { isOnline, deadLetters, clearDeadLetters } = useAxiomQueue();
|
|
172
|
+
|
|
173
|
+
const onSave = async (data) => {
|
|
174
|
+
// If offline, returns a 202 and flags isQueued: true
|
|
175
|
+
const res = await axiom.post('/checkout', data, { priority: 'urgent' });
|
|
176
|
+
|
|
177
|
+
if (res.isQueued) {
|
|
178
|
+
alert("Working offline. Your order will sync automatically!");
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<div>
|
|
184
|
+
{!isOnline && <Banner>You are offline. Actions will be saved.</Banner>}
|
|
185
|
+
{deadLetters.length > 0 && <ErrorBanner>Some actions failed to save.</ErrorBanner>}
|
|
186
|
+
|
|
187
|
+
<button onClick={onSave}>Checkout</button>
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Advanced Architecture
|
|
197
|
+
|
|
198
|
+
### Global Interceptors
|
|
199
|
+
|
|
200
|
+
Axiom allows you to intercept requests exactly like Axios, but it applies these rules to **background syncs** as well.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
<AxiomProvider
|
|
204
|
+
config={{
|
|
205
|
+
// Triggered globally whenever a request hard-fails (e.g., 500, 401)
|
|
206
|
+
onError: (status, error, request) => {
|
|
207
|
+
if (status === 401) {
|
|
208
|
+
AuthService.logout(); // Global logout on token expiration
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
// Triggered globally whenever ANY request succeeds (immediate or background)
|
|
212
|
+
onResponse: (data, status, request) => {
|
|
213
|
+
if (request.url.includes('/payment')) {
|
|
214
|
+
Analytics.track('Payment Successful');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Priority Lanes
|
|
223
|
+
|
|
224
|
+
By default, Axiom queues requests First-In-First-Out (FIFO). However, you can force critical requests to jump to the front of the line when the network returns.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// This stays at the back of the line (queued in the background)
|
|
228
|
+
axiom.post('/analytics', logData, { priority: 'background' });
|
|
229
|
+
|
|
230
|
+
// This jumps to the front and syncs first when the connection returns
|
|
231
|
+
axiom.post('/chat/send', message, { priority: 'urgent' });
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Queue Inspection & "Outbox" UI
|
|
236
|
+
|
|
237
|
+
Give your users the ability to see what is waiting to sync, and let them cancel actions before the network returns.
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { useAxiomQueue } from '@jayethian/axiom';
|
|
241
|
+
|
|
242
|
+
export function Outbox() {
|
|
243
|
+
const { inspectQueue, cancelRequest } = useAxiomQueue();
|
|
244
|
+
const [pending, setPending] = useState([]);
|
|
245
|
+
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
inspectQueue().then(setPending);
|
|
248
|
+
}, []);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<ul>
|
|
252
|
+
{pending.map(req => (
|
|
253
|
+
<li key={req.id}>
|
|
254
|
+
Pending: {req.method} {req.url}
|
|
255
|
+
<button onClick={() => cancelRequest(req.id)}>Cancel</button>
|
|
256
|
+
</li>
|
|
257
|
+
))}
|
|
258
|
+
</ul>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Just-In-Time Headers
|
|
265
|
+
|
|
266
|
+
If a user is offline for 4 hours, their JWT will likely expire. If Axiom attempts to sync the old request, the server will reject it. `onBeforeSync` allows you to inject fresh tokens *milliseconds* before the queue flushes.
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
<AxiomProvider
|
|
270
|
+
config={{
|
|
271
|
+
onBeforeSync: async (request) => {
|
|
272
|
+
const freshToken = await getValidAuthToken(); // Your logic
|
|
273
|
+
return {
|
|
274
|
+
...request,
|
|
275
|
+
headers: { ...request.headers, Authorization: `Bearer ${freshToken}` }
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Storage Adapters
|
|
284
|
+
|
|
285
|
+
Axiom comes with `IndexedDB`, `LocalStorage`, and `Memory` adapters out of the box. For React Native, building a custom adapter using a high-performance library like MMKV is incredibly simple.
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { MMKV } from 'react-native-mmkv';
|
|
289
|
+
import { AxiomStorageAdapter, QueuedRequest } from '@jayethian/axiom';
|
|
290
|
+
|
|
291
|
+
const mmkv = new MMKV();
|
|
292
|
+
|
|
293
|
+
export class MMKVAdapter implements AxiomStorageAdapter {
|
|
294
|
+
private key = 'axiom_queue';
|
|
295
|
+
|
|
296
|
+
private getQ(): QueuedRequest[] {
|
|
297
|
+
const data = mmkv.getString(this.key);
|
|
298
|
+
return data ? JSON.parse(data) : [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async save(req: QueuedRequest) {
|
|
302
|
+
const q = this.getQ();
|
|
303
|
+
q.push(req);
|
|
304
|
+
mmkv.set(this.key, JSON.stringify(q));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async getAll() { return this.getQ(); }
|
|
308
|
+
|
|
309
|
+
async remove(id: string) {
|
|
310
|
+
const q = this.getQ().filter(r => r.id !== id);
|
|
311
|
+
mmkv.set(this.key, JSON.stringify(q));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async clearAll() { mmkv.delete(this.key); }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Pass it to the provider:
|
|
318
|
+
<AxiomProvider storageAdapter={new MMKVAdapter()} {...props} />
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## API Reference
|
|
325
|
+
|
|
326
|
+
### `AxiomConfig`
|
|
327
|
+
|
|
328
|
+
| Property | Type | Default | Description |
|
|
329
|
+
| --- | --- | --- | --- |
|
|
330
|
+
| `baseURL` | `string` | `undefined` | Prepend this to all request URLs. |
|
|
331
|
+
| `defaultHeaders` | `Record<string, string>` | `{}` | Global headers applied to all requests. |
|
|
332
|
+
| `timeout` | `number` | `8000` | MS before a request is aborted and moved to the offline queue. |
|
|
333
|
+
| `maxRetries` | `number` | `3` | Attempts before a background sync fails permanently. |
|
|
334
|
+
| `fallbackAdapter` | `'indexeddb' | 'localstorage' | 'memory'` | `'memory'` | The internal adapter to use if `storageAdapter` is omitted. |
|
|
335
|
+
| `debug` | `boolean` | `false` | Prints verbose engine logs to the console. |
|
|
336
|
+
|
|
337
|
+
### `AxiomRequestOptions`
|
|
338
|
+
|
|
339
|
+
Passed as the third parameter to `axiom.post`, `axiom.get`, etc.
|
|
340
|
+
|
|
341
|
+
| Property | Type | Description |
|
|
342
|
+
| --- | --- | --- |
|
|
343
|
+
| `priority` | `'urgent' | 'background'` | Determines sort order when the queue flushes. |
|
|
344
|
+
| `timeout` | `number` | Overrides the global timeout for this specific request. |
|
|
345
|
+
| `headers` | `Record<string, string>` | Append or overwrite global headers for this request. |
|
|
346
|
+
| `metadata` | `any` | Attach custom UI data to the request. Survives serialization. |
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Contributing
|
|
351
|
+
|
|
352
|
+
Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://www.google.com/search?q=https://github.com/jayethian/axiom/issues).
|
|
353
|
+
|
|
354
|
+
## ð License
|
|
355
|
+
|
|
356
|
+
This project is [MIT](https://opensource.org/licenses/MIT) licensed. Built with âĄïļ by [Jayetheus](https://www.google.com/search?q=https://github.com/jayethian).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/** * Defines how the request should be treated when the queue flushes.
|
|
4
|
+
* Urgent requests bypass the background queue entirely if the network is active.
|
|
5
|
+
*/
|
|
3
6
|
type RequestPriority = 'urgent' | 'background';
|
|
7
|
+
/** * Represents a serialized HTTP request frozen in offline storage.
|
|
8
|
+
*/
|
|
4
9
|
interface QueuedRequest {
|
|
5
10
|
id: string;
|
|
6
11
|
timestamp: number;
|
|
@@ -11,13 +16,32 @@ interface QueuedRequest {
|
|
|
11
16
|
priority: RequestPriority;
|
|
12
17
|
retryCount: number;
|
|
13
18
|
}
|
|
19
|
+
/** * Global configuration for the Axiom engine initialized on startup.
|
|
20
|
+
*/
|
|
14
21
|
interface AxiomConfig {
|
|
22
|
+
/** The base URL prepended to all request paths. */
|
|
15
23
|
baseURL?: string;
|
|
24
|
+
/** Global headers applied to every request (e.g., Auth tokens). */
|
|
16
25
|
defaultHeaders?: Record<string, string>;
|
|
26
|
+
/** The maximum number of times a queued request will attempt to sync before failing permanently. */
|
|
17
27
|
maxRetries?: number;
|
|
28
|
+
/** Global timeout in milliseconds before a request is aborted and queued. */
|
|
29
|
+
timeout?: number;
|
|
30
|
+
/** Middleware hook triggered immediately before a queued request is synced. Ideal for refreshing Auth tokens. */
|
|
18
31
|
onBeforeSync?: (request: QueuedRequest) => Promise<QueuedRequest>;
|
|
32
|
+
/** Callback triggered when a request exceeds maxRetries and is permanently removed from the queue. */
|
|
19
33
|
onDeadLetter?: (request: QueuedRequest, error: Error) => void;
|
|
20
34
|
}
|
|
35
|
+
/** * Per-request configuration options that override the global configuration.
|
|
36
|
+
*/
|
|
37
|
+
interface AxiomRequestOptions {
|
|
38
|
+
/** Overrides the queue sorting behavior for this specific request. */
|
|
39
|
+
priority?: RequestPriority;
|
|
40
|
+
/** Overrides the global timeout limit for this specific request. */
|
|
41
|
+
timeout?: number;
|
|
42
|
+
/** Specific headers to append to this single request (e.g., custom Content-Type). */
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
}
|
|
21
45
|
|
|
22
46
|
interface AxiomStorageAdapter {
|
|
23
47
|
/** Saves a request to the persistent queue */
|
|
@@ -43,40 +67,88 @@ declare class AxiomEngine {
|
|
|
43
67
|
private storage;
|
|
44
68
|
private syncManager;
|
|
45
69
|
/**
|
|
46
|
-
* Initializes the Axiom engine with
|
|
70
|
+
* Initializes the Axiom engine with global configuration and a storage adapter.
|
|
71
|
+
* This must be called before making any requests to enable persistence.
|
|
72
|
+
* * @param config - Global configuration (baseURL, timeouts, custom headers, etc.)
|
|
73
|
+
* @param storageAdapter - Optional custom adapter (e.g., MMKV). Defaults to in-memory storage.
|
|
47
74
|
*/
|
|
48
75
|
create(config: AxiomConfig, storageAdapter?: AxiomStorageAdapter): void;
|
|
76
|
+
/**
|
|
77
|
+
* Manually triggers the background sync manager to flush all pending queued requests.
|
|
78
|
+
* Note: This is automatically handled by `AxiomProvider` when the network reconnects.
|
|
79
|
+
*/
|
|
49
80
|
forceSync(): Promise<void>;
|
|
50
81
|
/**
|
|
51
|
-
* Generates a unique ID for queued requests.
|
|
82
|
+
* Generates a unique collision-resistant ID for queued requests.
|
|
52
83
|
*/
|
|
53
84
|
private generateId;
|
|
54
85
|
/**
|
|
55
|
-
*
|
|
86
|
+
* Executes an HTTP GET request.
|
|
87
|
+
* If the network is unavailable or times out, the request is safely queued.
|
|
88
|
+
* * @param url - The endpoint URL (appended to baseURL if configured).
|
|
89
|
+
* @param options - Request-specific options (priority lanes, timeout overrides).
|
|
90
|
+
* @returns A promise resolving to the response data, status code, and queue state.
|
|
91
|
+
*/
|
|
92
|
+
get<T>(url: string, options?: AxiomRequestOptions): Promise<{
|
|
93
|
+
data?: T;
|
|
94
|
+
status: number;
|
|
95
|
+
isQueued: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Executes an HTTP POST request.
|
|
99
|
+
* If the network is unavailable or times out, the payload is safely queued.
|
|
100
|
+
* * @param url - The endpoint URL.
|
|
101
|
+
* @param data - The payload object to be serialized and sent.
|
|
102
|
+
* @param options - Request-specific options.
|
|
103
|
+
*/
|
|
104
|
+
post<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
105
|
+
data?: T;
|
|
106
|
+
status: number;
|
|
107
|
+
isQueued: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* Executes an HTTP PUT request to entirely replace a resource.
|
|
111
|
+
* * @param url - The endpoint URL.
|
|
112
|
+
* @param data - The payload object to be serialized and sent.
|
|
113
|
+
* @param options - Request-specific options.
|
|
114
|
+
*/
|
|
115
|
+
put<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
116
|
+
data?: T;
|
|
117
|
+
status: number;
|
|
118
|
+
isQueued: boolean;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Executes an HTTP PATCH request to partially update a resource.
|
|
122
|
+
* * @param url - The endpoint URL.
|
|
123
|
+
* @param data - The partial payload object to be serialized and sent.
|
|
124
|
+
* @param options - Request-specific options.
|
|
56
125
|
*/
|
|
57
|
-
|
|
58
|
-
priority?: RequestPriority;
|
|
59
|
-
}): Promise<{
|
|
126
|
+
patch<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
60
127
|
data?: T;
|
|
61
128
|
status: number;
|
|
62
129
|
isQueued: boolean;
|
|
63
130
|
}>;
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
131
|
+
/**
|
|
132
|
+
* Executes an HTTP DELETE request.
|
|
133
|
+
* * @param url - The endpoint URL.
|
|
134
|
+
* @param options - Request-specific options.
|
|
66
135
|
*/
|
|
67
|
-
|
|
68
|
-
priority?: RequestPriority;
|
|
69
|
-
}): Promise<{
|
|
136
|
+
delete<T>(url: string, options?: AxiomRequestOptions): Promise<{
|
|
70
137
|
data?: T;
|
|
71
138
|
status: number;
|
|
72
139
|
isQueued: boolean;
|
|
73
140
|
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Internal helper to consolidate request preparation and keep the engine DRY.
|
|
143
|
+
*/
|
|
144
|
+
private prepareRequest;
|
|
74
145
|
/**
|
|
75
146
|
* Internal logic to fire the request or catch the network drop.
|
|
147
|
+
* Handles timeout cancellations via AbortController.
|
|
76
148
|
*/
|
|
77
149
|
private attemptFetch;
|
|
78
150
|
/**
|
|
79
|
-
* Saves the request to
|
|
151
|
+
* Saves the request to the configured storage adapter.
|
|
80
152
|
*/
|
|
81
153
|
private enqueueRequest;
|
|
82
154
|
}
|
|
@@ -99,4 +171,4 @@ declare function useAxiomQueue(): {
|
|
|
99
171
|
forceSync: () => Promise<void>;
|
|
100
172
|
};
|
|
101
173
|
|
|
102
|
-
export { type AxiomConfig, AxiomEngine, AxiomProvider, type AxiomStorageAdapter, MemoryStorageAdapter, type QueuedRequest, type RequestPriority, axiom, useAxiomQueue };
|
|
174
|
+
export { type AxiomConfig, AxiomEngine, AxiomProvider, type AxiomRequestOptions, type AxiomStorageAdapter, MemoryStorageAdapter, type QueuedRequest, type RequestPriority, axiom, useAxiomQueue };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/** * Defines how the request should be treated when the queue flushes.
|
|
4
|
+
* Urgent requests bypass the background queue entirely if the network is active.
|
|
5
|
+
*/
|
|
3
6
|
type RequestPriority = 'urgent' | 'background';
|
|
7
|
+
/** * Represents a serialized HTTP request frozen in offline storage.
|
|
8
|
+
*/
|
|
4
9
|
interface QueuedRequest {
|
|
5
10
|
id: string;
|
|
6
11
|
timestamp: number;
|
|
@@ -11,13 +16,32 @@ interface QueuedRequest {
|
|
|
11
16
|
priority: RequestPriority;
|
|
12
17
|
retryCount: number;
|
|
13
18
|
}
|
|
19
|
+
/** * Global configuration for the Axiom engine initialized on startup.
|
|
20
|
+
*/
|
|
14
21
|
interface AxiomConfig {
|
|
22
|
+
/** The base URL prepended to all request paths. */
|
|
15
23
|
baseURL?: string;
|
|
24
|
+
/** Global headers applied to every request (e.g., Auth tokens). */
|
|
16
25
|
defaultHeaders?: Record<string, string>;
|
|
26
|
+
/** The maximum number of times a queued request will attempt to sync before failing permanently. */
|
|
17
27
|
maxRetries?: number;
|
|
28
|
+
/** Global timeout in milliseconds before a request is aborted and queued. */
|
|
29
|
+
timeout?: number;
|
|
30
|
+
/** Middleware hook triggered immediately before a queued request is synced. Ideal for refreshing Auth tokens. */
|
|
18
31
|
onBeforeSync?: (request: QueuedRequest) => Promise<QueuedRequest>;
|
|
32
|
+
/** Callback triggered when a request exceeds maxRetries and is permanently removed from the queue. */
|
|
19
33
|
onDeadLetter?: (request: QueuedRequest, error: Error) => void;
|
|
20
34
|
}
|
|
35
|
+
/** * Per-request configuration options that override the global configuration.
|
|
36
|
+
*/
|
|
37
|
+
interface AxiomRequestOptions {
|
|
38
|
+
/** Overrides the queue sorting behavior for this specific request. */
|
|
39
|
+
priority?: RequestPriority;
|
|
40
|
+
/** Overrides the global timeout limit for this specific request. */
|
|
41
|
+
timeout?: number;
|
|
42
|
+
/** Specific headers to append to this single request (e.g., custom Content-Type). */
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
}
|
|
21
45
|
|
|
22
46
|
interface AxiomStorageAdapter {
|
|
23
47
|
/** Saves a request to the persistent queue */
|
|
@@ -43,40 +67,88 @@ declare class AxiomEngine {
|
|
|
43
67
|
private storage;
|
|
44
68
|
private syncManager;
|
|
45
69
|
/**
|
|
46
|
-
* Initializes the Axiom engine with
|
|
70
|
+
* Initializes the Axiom engine with global configuration and a storage adapter.
|
|
71
|
+
* This must be called before making any requests to enable persistence.
|
|
72
|
+
* * @param config - Global configuration (baseURL, timeouts, custom headers, etc.)
|
|
73
|
+
* @param storageAdapter - Optional custom adapter (e.g., MMKV). Defaults to in-memory storage.
|
|
47
74
|
*/
|
|
48
75
|
create(config: AxiomConfig, storageAdapter?: AxiomStorageAdapter): void;
|
|
76
|
+
/**
|
|
77
|
+
* Manually triggers the background sync manager to flush all pending queued requests.
|
|
78
|
+
* Note: This is automatically handled by `AxiomProvider` when the network reconnects.
|
|
79
|
+
*/
|
|
49
80
|
forceSync(): Promise<void>;
|
|
50
81
|
/**
|
|
51
|
-
* Generates a unique ID for queued requests.
|
|
82
|
+
* Generates a unique collision-resistant ID for queued requests.
|
|
52
83
|
*/
|
|
53
84
|
private generateId;
|
|
54
85
|
/**
|
|
55
|
-
*
|
|
86
|
+
* Executes an HTTP GET request.
|
|
87
|
+
* If the network is unavailable or times out, the request is safely queued.
|
|
88
|
+
* * @param url - The endpoint URL (appended to baseURL if configured).
|
|
89
|
+
* @param options - Request-specific options (priority lanes, timeout overrides).
|
|
90
|
+
* @returns A promise resolving to the response data, status code, and queue state.
|
|
91
|
+
*/
|
|
92
|
+
get<T>(url: string, options?: AxiomRequestOptions): Promise<{
|
|
93
|
+
data?: T;
|
|
94
|
+
status: number;
|
|
95
|
+
isQueued: boolean;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Executes an HTTP POST request.
|
|
99
|
+
* If the network is unavailable or times out, the payload is safely queued.
|
|
100
|
+
* * @param url - The endpoint URL.
|
|
101
|
+
* @param data - The payload object to be serialized and sent.
|
|
102
|
+
* @param options - Request-specific options.
|
|
103
|
+
*/
|
|
104
|
+
post<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
105
|
+
data?: T;
|
|
106
|
+
status: number;
|
|
107
|
+
isQueued: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
/**
|
|
110
|
+
* Executes an HTTP PUT request to entirely replace a resource.
|
|
111
|
+
* * @param url - The endpoint URL.
|
|
112
|
+
* @param data - The payload object to be serialized and sent.
|
|
113
|
+
* @param options - Request-specific options.
|
|
114
|
+
*/
|
|
115
|
+
put<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
116
|
+
data?: T;
|
|
117
|
+
status: number;
|
|
118
|
+
isQueued: boolean;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Executes an HTTP PATCH request to partially update a resource.
|
|
122
|
+
* * @param url - The endpoint URL.
|
|
123
|
+
* @param data - The partial payload object to be serialized and sent.
|
|
124
|
+
* @param options - Request-specific options.
|
|
56
125
|
*/
|
|
57
|
-
|
|
58
|
-
priority?: RequestPriority;
|
|
59
|
-
}): Promise<{
|
|
126
|
+
patch<T>(url: string, data?: any, options?: AxiomRequestOptions): Promise<{
|
|
60
127
|
data?: T;
|
|
61
128
|
status: number;
|
|
62
129
|
isQueued: boolean;
|
|
63
130
|
}>;
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
131
|
+
/**
|
|
132
|
+
* Executes an HTTP DELETE request.
|
|
133
|
+
* * @param url - The endpoint URL.
|
|
134
|
+
* @param options - Request-specific options.
|
|
66
135
|
*/
|
|
67
|
-
|
|
68
|
-
priority?: RequestPriority;
|
|
69
|
-
}): Promise<{
|
|
136
|
+
delete<T>(url: string, options?: AxiomRequestOptions): Promise<{
|
|
70
137
|
data?: T;
|
|
71
138
|
status: number;
|
|
72
139
|
isQueued: boolean;
|
|
73
140
|
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Internal helper to consolidate request preparation and keep the engine DRY.
|
|
143
|
+
*/
|
|
144
|
+
private prepareRequest;
|
|
74
145
|
/**
|
|
75
146
|
* Internal logic to fire the request or catch the network drop.
|
|
147
|
+
* Handles timeout cancellations via AbortController.
|
|
76
148
|
*/
|
|
77
149
|
private attemptFetch;
|
|
78
150
|
/**
|
|
79
|
-
* Saves the request to
|
|
151
|
+
* Saves the request to the configured storage adapter.
|
|
80
152
|
*/
|
|
81
153
|
private enqueueRequest;
|
|
82
154
|
}
|
|
@@ -99,4 +171,4 @@ declare function useAxiomQueue(): {
|
|
|
99
171
|
forceSync: () => Promise<void>;
|
|
100
172
|
};
|
|
101
173
|
|
|
102
|
-
export { type AxiomConfig, AxiomEngine, AxiomProvider, type AxiomStorageAdapter, MemoryStorageAdapter, type QueuedRequest, type RequestPriority, axiom, useAxiomQueue };
|
|
174
|
+
export { type AxiomConfig, AxiomEngine, AxiomProvider, type AxiomRequestOptions, type AxiomStorageAdapter, MemoryStorageAdapter, type QueuedRequest, type RequestPriority, axiom, useAxiomQueue };
|