@terreno/rtk 0.13.3 → 0.14.0
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/dist/authSlice.test.js +2 -10
- package/dist/authSlice.test.js.map +1 -1
- package/dist/authSliceNative.test.js +11 -3
- package/dist/authSliceNative.test.js.map +1 -1
- package/dist/betterAuthSlice.test.js +1 -25
- package/dist/betterAuthSlice.test.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -3
- package/dist/constants.js.map +1 -1
- package/dist/constants.test.js +44 -2
- package/dist/constants.test.js.map +1 -1
- package/dist/emptyApi.d.ts +2 -0
- package/dist/emptyApi.d.ts.map +1 -1
- package/dist/emptyApi.js +5 -0
- package/dist/emptyApi.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/isolated/constants.isolated.js +7 -1
- package/dist/isolated/constants.isolated.js.map +1 -1
- package/dist/isolated/offlineMiddleware.isolated.d.ts +2 -0
- package/dist/isolated/offlineMiddleware.isolated.d.ts.map +1 -0
- package/dist/isolated/offlineMiddleware.isolated.js +516 -0
- package/dist/isolated/offlineMiddleware.isolated.js.map +1 -0
- package/dist/isolated/offlineMiddleware.native.isolated.d.ts +2 -0
- package/dist/isolated/offlineMiddleware.native.isolated.d.ts.map +1 -0
- package/dist/isolated/offlineMiddleware.native.isolated.js +70 -0
- package/dist/isolated/offlineMiddleware.native.isolated.js.map +1 -0
- package/dist/isolated/useUpgradeCheck.mobile.isolated.d.ts +2 -0
- package/dist/isolated/useUpgradeCheck.mobile.isolated.d.ts.map +1 -0
- package/dist/{useUpgradeCheck.mobile.test.js → isolated/useUpgradeCheck.mobile.isolated.js} +13 -5
- package/dist/isolated/useUpgradeCheck.mobile.isolated.js.map +1 -0
- package/dist/isolated/useUpgradeCheck.web.isolated.d.ts +2 -0
- package/dist/isolated/useUpgradeCheck.web.isolated.d.ts.map +1 -0
- package/dist/{useUpgradeCheck.test.js → isolated/useUpgradeCheck.web.isolated.js} +37 -7
- package/dist/isolated/useUpgradeCheck.web.isolated.js.map +1 -0
- package/dist/mongooseSlice.test.js +1 -1
- package/dist/mongooseSlice.test.js.map +1 -1
- package/dist/offlineGate.d.ts +8 -0
- package/dist/offlineGate.d.ts.map +1 -0
- package/dist/offlineGate.js +20 -0
- package/dist/offlineGate.js.map +1 -0
- package/dist/offlineGate.test.d.ts +2 -0
- package/dist/offlineGate.test.d.ts.map +1 -0
- package/dist/offlineGate.test.js +46 -0
- package/dist/offlineGate.test.js.map +1 -0
- package/dist/offlineMiddleware.d.ts +46 -0
- package/dist/offlineMiddleware.d.ts.map +1 -0
- package/dist/offlineMiddleware.js +591 -0
- package/dist/offlineMiddleware.js.map +1 -0
- package/dist/offlineMiddleware.test.d.ts +2 -0
- package/dist/offlineMiddleware.test.d.ts.map +1 -0
- package/dist/offlineMiddleware.test.js +145 -0
- package/dist/offlineMiddleware.test.js.map +1 -0
- package/dist/offlineSlice.d.ts +232 -0
- package/dist/offlineSlice.d.ts.map +1 -0
- package/dist/offlineSlice.js +65 -0
- package/dist/offlineSlice.js.map +1 -0
- package/dist/offlineSlice.test.d.ts +2 -0
- package/dist/offlineSlice.test.d.ts.map +1 -0
- package/dist/offlineSlice.test.js +153 -0
- package/dist/offlineSlice.test.js.map +1 -0
- package/dist/realtime.d.ts +123 -0
- package/dist/realtime.d.ts.map +1 -0
- package/dist/realtime.js +299 -0
- package/dist/realtime.js.map +1 -0
- package/dist/realtime.test.d.ts +2 -0
- package/dist/realtime.test.d.ts.map +1 -0
- package/dist/realtime.test.js +339 -0
- package/dist/realtime.test.js.map +1 -0
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +29 -28
- package/dist/socket.js.map +1 -1
- package/dist/sync.d.ts +35 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +205 -0
- package/dist/sync.js.map +1 -0
- package/dist/tagGenerator.d.ts +20 -1
- package/dist/tagGenerator.d.ts.map +1 -1
- package/dist/tagGenerator.js +1 -2
- package/dist/tagGenerator.js.map +1 -1
- package/dist/useOfflineStatus.d.ts +33 -0
- package/dist/useOfflineStatus.d.ts.map +1 -0
- package/dist/useOfflineStatus.js +43 -0
- package/dist/useOfflineStatus.js.map +1 -0
- package/dist/useOfflineStatus.test.d.ts +2 -0
- package/dist/useOfflineStatus.test.d.ts.map +1 -0
- package/dist/useOfflineStatus.test.js +119 -0
- package/dist/useOfflineStatus.test.js.map +1 -0
- package/dist/useRealtimeDebug.d.ts +6 -0
- package/dist/useRealtimeDebug.d.ts.map +1 -0
- package/dist/useRealtimeDebug.js +41 -0
- package/dist/useRealtimeDebug.js.map +1 -0
- package/dist/useServerStatus.d.ts +29 -0
- package/dist/useServerStatus.d.ts.map +1 -0
- package/dist/useServerStatus.js +89 -0
- package/dist/useServerStatus.js.map +1 -0
- package/dist/useUpgradeCheck.d.ts +7 -3
- package/dist/useUpgradeCheck.d.ts.map +1 -1
- package/dist/useUpgradeCheck.js +27 -13
- package/dist/useUpgradeCheck.js.map +1 -1
- package/package.json +2 -2
- package/src/authSlice.test.ts +2 -11
- package/src/authSliceNative.test.ts +11 -4
- package/src/betterAuthSlice.test.ts +1 -25
- package/src/constants.test.ts +54 -1
- package/src/constants.ts +14 -3
- package/src/emptyApi.ts +8 -0
- package/src/index.ts +8 -0
- package/src/isolated/constants.isolated.ts +7 -3
- package/src/isolated/offlineMiddleware.isolated.ts +696 -0
- package/src/isolated/offlineMiddleware.native.isolated.ts +80 -0
- package/src/{useUpgradeCheck.mobile.test.ts → isolated/useUpgradeCheck.mobile.isolated.ts} +13 -4
- package/src/{useUpgradeCheck.test.ts → isolated/useUpgradeCheck.web.isolated.ts} +48 -6
- package/src/mongooseSlice.test.ts +1 -1
- package/src/offlineGate.test.ts +55 -0
- package/src/offlineGate.ts +25 -0
- package/src/offlineMiddleware.test.ts +187 -0
- package/src/offlineMiddleware.ts +718 -0
- package/src/offlineSlice.test.ts +204 -0
- package/src/offlineSlice.ts +130 -0
- package/src/realtime.test.ts +404 -0
- package/src/realtime.ts +408 -0
- package/src/socket.ts +33 -32
- package/src/sync.ts +310 -0
- package/src/tagGenerator.ts +29 -11
- package/src/useOfflineStatus.test.ts +157 -0
- package/src/useOfflineStatus.ts +82 -0
- package/src/useRealtimeDebug.ts +51 -0
- package/src/useServerStatus.ts +122 -0
- package/src/useUpgradeCheck.ts +35 -13
- package/dist/isolated/useUpgradeCheck.isolated.d.ts +0 -2
- package/dist/isolated/useUpgradeCheck.isolated.d.ts.map +0 -1
- package/dist/isolated/useUpgradeCheck.isolated.js +0 -153
- package/dist/isolated/useUpgradeCheck.isolated.js.map +0 -1
- package/dist/useUpgradeCheck.mobile.test.d.ts +0 -2
- package/dist/useUpgradeCheck.mobile.test.d.ts.map +0 -1
- package/dist/useUpgradeCheck.mobile.test.js.map +0 -1
- package/dist/useUpgradeCheck.test.d.ts +0 -2
- package/dist/useUpgradeCheck.test.d.ts.map +0 -1
- package/dist/useUpgradeCheck.test.js.map +0 -1
- package/src/isolated/useUpgradeCheck.isolated.ts +0 -201
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
3
|
+
import { buildOptimisticCreateItem, createOfflineMiddleware, isNetworkFetchError, shouldReplayQueuedMutation, } from "./offlineMiddleware";
|
|
4
|
+
import { selectOfflineQueue } from "./offlineSlice";
|
|
5
|
+
const LIST_QUERY_ARGS = {};
|
|
6
|
+
const LIST_UPDATED_AT = "2026-05-23T21:00:00.123Z";
|
|
7
|
+
const initialTestApiState = {
|
|
8
|
+
queries: {
|
|
9
|
+
"getTodos({})": {
|
|
10
|
+
data: {
|
|
11
|
+
data: [
|
|
12
|
+
{
|
|
13
|
+
_id: "todo-1",
|
|
14
|
+
title: "Original",
|
|
15
|
+
updated: LIST_UPDATED_AT,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
originalArgs: LIST_QUERY_ARGS,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const testApiReducer = (state = initialTestApiState) => state;
|
|
24
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test double covers only fields used by offline middleware.
|
|
25
|
+
const createTestApi = () => {
|
|
26
|
+
return {
|
|
27
|
+
endpoints: {
|
|
28
|
+
getTodos: {
|
|
29
|
+
select: (queryArg) => {
|
|
30
|
+
return (state) => {
|
|
31
|
+
return Object.values(state.testApi.queries).find((entry) => entry.originalArgs === queryArg);
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
reducerPath: "testApi",
|
|
37
|
+
util: {
|
|
38
|
+
invalidateTags: () => ({ type: "testApi/invalidateTags" }),
|
|
39
|
+
updateQueryData: () => ({ type: "testApi/updateQueryData" }),
|
|
40
|
+
},
|
|
41
|
+
// biome-ignore lint/suspicious/noExplicitAny: Test double covers only fields used by offline middleware.
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
describe("isNetworkFetchError", () => {
|
|
45
|
+
it("returns true for TypeError error name", () => {
|
|
46
|
+
expect(isNetworkFetchError({ error: { name: "TypeError" } })).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it("returns true for network error messages", () => {
|
|
49
|
+
expect(isNetworkFetchError({ error: { message: "Failed to fetch" } })).toBe(true);
|
|
50
|
+
expect(isNetworkFetchError({ error: { message: "fetch failed" } })).toBe(true);
|
|
51
|
+
expect(isNetworkFetchError({ error: { message: "Network Error" } })).toBe(true);
|
|
52
|
+
expect(isNetworkFetchError({ error: { message: "Network unavailable" } })).toBe(true);
|
|
53
|
+
expect(isNetworkFetchError({ error: { message: "Load failed" } })).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it("returns true for string error fields", () => {
|
|
56
|
+
expect(isNetworkFetchError({ error: "fetch failed" })).toBe(true);
|
|
57
|
+
expect(isNetworkFetchError({ payload: { error: "network error" } })).toBe(true);
|
|
58
|
+
expect(isNetworkFetchError({ error: "Failed to fetch", status: "FETCH_ERROR" })).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("returns false for non-network errors", () => {
|
|
61
|
+
expect(isNetworkFetchError({ error: "Unauthorized", status: "FETCH_ERROR" })).toBe(false);
|
|
62
|
+
expect(isNetworkFetchError({ error: { message: "Unauthorized" } })).toBe(false);
|
|
63
|
+
expect(isNetworkFetchError(null)).toBe(false);
|
|
64
|
+
expect(isNetworkFetchError(undefined)).toBe(false);
|
|
65
|
+
expect(isNetworkFetchError({})).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe("shouldReplayQueuedMutation", () => {
|
|
69
|
+
const baseMutation = {
|
|
70
|
+
args: { body: { title: "Test" } },
|
|
71
|
+
endpointName: "postTodos",
|
|
72
|
+
id: "m1",
|
|
73
|
+
timestamp: "2026-04-15T10:00:00.000Z",
|
|
74
|
+
type: "create",
|
|
75
|
+
userId: "user-a",
|
|
76
|
+
};
|
|
77
|
+
it("replays only mutations owned by the current user", () => {
|
|
78
|
+
expect(shouldReplayQueuedMutation(baseMutation, "user-a")).toBe(true);
|
|
79
|
+
expect(shouldReplayQueuedMutation(baseMutation, "user-b")).toBe(false);
|
|
80
|
+
expect(shouldReplayQueuedMutation(baseMutation, undefined)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
it("does not replay legacy mutations without userId", () => {
|
|
83
|
+
const legacy = { ...baseMutation, userId: undefined };
|
|
84
|
+
expect(shouldReplayQueuedMutation(legacy, "user-a")).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("buildOptimisticCreateItem", () => {
|
|
88
|
+
const mutation = {
|
|
89
|
+
args: { body: { title: "New" } },
|
|
90
|
+
endpointName: "postTodos",
|
|
91
|
+
id: "queue-1",
|
|
92
|
+
timestamp: "2026-04-15T10:00:00.000Z",
|
|
93
|
+
type: "create",
|
|
94
|
+
};
|
|
95
|
+
it("applies temp ids after body spread so client ids cannot win", () => {
|
|
96
|
+
const item = buildOptimisticCreateItem(mutation, {
|
|
97
|
+
_id: "client-id",
|
|
98
|
+
id: "client-id",
|
|
99
|
+
title: "New",
|
|
100
|
+
});
|
|
101
|
+
expect(item._id).toBe("temp-queue-1");
|
|
102
|
+
expect(item.id).toBe("temp-queue-1");
|
|
103
|
+
expect(item.title).toBe("New");
|
|
104
|
+
expect(item.created).toBe(mutation.timestamp);
|
|
105
|
+
expect(item.updated).toBe(mutation.timestamp);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("offlineMiddleware", () => {
|
|
109
|
+
it("uses list-cache updated timestamp for queued update conflict headers", async () => {
|
|
110
|
+
const api = createTestApi();
|
|
111
|
+
const offline = createOfflineMiddleware({
|
|
112
|
+
api,
|
|
113
|
+
endpoints: ["patchTodosById"],
|
|
114
|
+
});
|
|
115
|
+
const store = configureStore({
|
|
116
|
+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }).concat(offline.middleware),
|
|
117
|
+
reducer: {
|
|
118
|
+
auth: (state = { userId: "user-a" }) => state,
|
|
119
|
+
offline: offline.offlineReducer,
|
|
120
|
+
testApi: testApiReducer,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const rejectedMutation = {
|
|
124
|
+
error: { message: "Network unavailable" },
|
|
125
|
+
meta: {
|
|
126
|
+
arg: {
|
|
127
|
+
endpointName: "patchTodosById",
|
|
128
|
+
originalArgs: {
|
|
129
|
+
body: { title: "Queued title" },
|
|
130
|
+
id: "todo-1",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
type: "testApi/executeMutation/rejected",
|
|
135
|
+
};
|
|
136
|
+
store.dispatch(rejectedMutation);
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
138
|
+
const queue = selectOfflineQueue(store.getState());
|
|
139
|
+
expect(queue).toHaveLength(1);
|
|
140
|
+
expect(queue[0].baseUpdatedAt).toBe(LIST_UPDATED_AT);
|
|
141
|
+
expect(queue[0].timestamp).toBe(LIST_UPDATED_AT);
|
|
142
|
+
expect(queue[0].userId).toBe("user-a");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=offlineMiddleware.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offlineMiddleware.test.js","sourceRoot":"","sources":["../src/offlineMiddleware.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { type PayloadAction } from "@reduxjs/toolkit";
|
|
2
|
+
export interface QueuedMutation {
|
|
3
|
+
/** Unique identifier for this queued mutation */
|
|
4
|
+
id: string;
|
|
5
|
+
/** RTK Query endpoint name, e.g. "patchTodosById" */
|
|
6
|
+
endpointName: string;
|
|
7
|
+
/** Original mutation arguments */
|
|
8
|
+
args: unknown;
|
|
9
|
+
/** ISO timestamp of when the mutation was queued */
|
|
10
|
+
timestamp: string;
|
|
11
|
+
/** Document `_updatedAt` at queue time (used for If-Unmodified-Since on replay) */
|
|
12
|
+
baseUpdatedAt?: string;
|
|
13
|
+
/** The type of CRUD operation */
|
|
14
|
+
type: "create" | "update" | "delete";
|
|
15
|
+
/** Auth user ID when the mutation was queued; replay is skipped if it does not match the current user */
|
|
16
|
+
userId?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ConflictRecord {
|
|
19
|
+
/** Unique identifier for this conflict */
|
|
20
|
+
id: string;
|
|
21
|
+
/** RTK Query endpoint name that caused the conflict */
|
|
22
|
+
endpointName: string;
|
|
23
|
+
/** Original mutation arguments that were rejected */
|
|
24
|
+
args: unknown;
|
|
25
|
+
/** The current server version of the document */
|
|
26
|
+
serverDocument: unknown;
|
|
27
|
+
/** ISO timestamp of when the conflict was detected */
|
|
28
|
+
timestamp: string;
|
|
29
|
+
/** Whether the user has dismissed this conflict notification */
|
|
30
|
+
dismissed: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface OfflineState {
|
|
33
|
+
isOnline: boolean;
|
|
34
|
+
queue: QueuedMutation[];
|
|
35
|
+
conflicts: ConflictRecord[];
|
|
36
|
+
isSyncing: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare const offlineSlice: import("@reduxjs/toolkit").Slice<OfflineState, {
|
|
39
|
+
addConflict(state: {
|
|
40
|
+
isOnline: boolean;
|
|
41
|
+
queue: {
|
|
42
|
+
id: string;
|
|
43
|
+
endpointName: string;
|
|
44
|
+
args: unknown;
|
|
45
|
+
timestamp: string;
|
|
46
|
+
baseUpdatedAt?: string | undefined;
|
|
47
|
+
type: "create" | "update" | "delete";
|
|
48
|
+
userId?: string | undefined;
|
|
49
|
+
}[];
|
|
50
|
+
conflicts: {
|
|
51
|
+
id: string;
|
|
52
|
+
endpointName: string;
|
|
53
|
+
args: unknown;
|
|
54
|
+
serverDocument: unknown;
|
|
55
|
+
timestamp: string;
|
|
56
|
+
dismissed: boolean;
|
|
57
|
+
}[];
|
|
58
|
+
isSyncing: boolean;
|
|
59
|
+
}, action: PayloadAction<ConflictRecord>): void;
|
|
60
|
+
clearConflicts(state: {
|
|
61
|
+
isOnline: boolean;
|
|
62
|
+
queue: {
|
|
63
|
+
id: string;
|
|
64
|
+
endpointName: string;
|
|
65
|
+
args: unknown;
|
|
66
|
+
timestamp: string;
|
|
67
|
+
baseUpdatedAt?: string | undefined;
|
|
68
|
+
type: "create" | "update" | "delete";
|
|
69
|
+
userId?: string | undefined;
|
|
70
|
+
}[];
|
|
71
|
+
conflicts: {
|
|
72
|
+
id: string;
|
|
73
|
+
endpointName: string;
|
|
74
|
+
args: unknown;
|
|
75
|
+
serverDocument: unknown;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
dismissed: boolean;
|
|
78
|
+
}[];
|
|
79
|
+
isSyncing: boolean;
|
|
80
|
+
}): void;
|
|
81
|
+
clearQueue(state: {
|
|
82
|
+
isOnline: boolean;
|
|
83
|
+
queue: {
|
|
84
|
+
id: string;
|
|
85
|
+
endpointName: string;
|
|
86
|
+
args: unknown;
|
|
87
|
+
timestamp: string;
|
|
88
|
+
baseUpdatedAt?: string | undefined;
|
|
89
|
+
type: "create" | "update" | "delete";
|
|
90
|
+
userId?: string | undefined;
|
|
91
|
+
}[];
|
|
92
|
+
conflicts: {
|
|
93
|
+
id: string;
|
|
94
|
+
endpointName: string;
|
|
95
|
+
args: unknown;
|
|
96
|
+
serverDocument: unknown;
|
|
97
|
+
timestamp: string;
|
|
98
|
+
dismissed: boolean;
|
|
99
|
+
}[];
|
|
100
|
+
isSyncing: boolean;
|
|
101
|
+
}): void;
|
|
102
|
+
dequeue(state: {
|
|
103
|
+
isOnline: boolean;
|
|
104
|
+
queue: {
|
|
105
|
+
id: string;
|
|
106
|
+
endpointName: string;
|
|
107
|
+
args: unknown;
|
|
108
|
+
timestamp: string;
|
|
109
|
+
baseUpdatedAt?: string | undefined;
|
|
110
|
+
type: "create" | "update" | "delete";
|
|
111
|
+
userId?: string | undefined;
|
|
112
|
+
}[];
|
|
113
|
+
conflicts: {
|
|
114
|
+
id: string;
|
|
115
|
+
endpointName: string;
|
|
116
|
+
args: unknown;
|
|
117
|
+
serverDocument: unknown;
|
|
118
|
+
timestamp: string;
|
|
119
|
+
dismissed: boolean;
|
|
120
|
+
}[];
|
|
121
|
+
isSyncing: boolean;
|
|
122
|
+
}, action: PayloadAction<string>): void;
|
|
123
|
+
dismissConflict(state: {
|
|
124
|
+
isOnline: boolean;
|
|
125
|
+
queue: {
|
|
126
|
+
id: string;
|
|
127
|
+
endpointName: string;
|
|
128
|
+
args: unknown;
|
|
129
|
+
timestamp: string;
|
|
130
|
+
baseUpdatedAt?: string | undefined;
|
|
131
|
+
type: "create" | "update" | "delete";
|
|
132
|
+
userId?: string | undefined;
|
|
133
|
+
}[];
|
|
134
|
+
conflicts: {
|
|
135
|
+
id: string;
|
|
136
|
+
endpointName: string;
|
|
137
|
+
args: unknown;
|
|
138
|
+
serverDocument: unknown;
|
|
139
|
+
timestamp: string;
|
|
140
|
+
dismissed: boolean;
|
|
141
|
+
}[];
|
|
142
|
+
isSyncing: boolean;
|
|
143
|
+
}, action: PayloadAction<string>): void;
|
|
144
|
+
enqueue(state: {
|
|
145
|
+
isOnline: boolean;
|
|
146
|
+
queue: {
|
|
147
|
+
id: string;
|
|
148
|
+
endpointName: string;
|
|
149
|
+
args: unknown;
|
|
150
|
+
timestamp: string;
|
|
151
|
+
baseUpdatedAt?: string | undefined;
|
|
152
|
+
type: "create" | "update" | "delete";
|
|
153
|
+
userId?: string | undefined;
|
|
154
|
+
}[];
|
|
155
|
+
conflicts: {
|
|
156
|
+
id: string;
|
|
157
|
+
endpointName: string;
|
|
158
|
+
args: unknown;
|
|
159
|
+
serverDocument: unknown;
|
|
160
|
+
timestamp: string;
|
|
161
|
+
dismissed: boolean;
|
|
162
|
+
}[];
|
|
163
|
+
isSyncing: boolean;
|
|
164
|
+
}, action: PayloadAction<QueuedMutation>): void;
|
|
165
|
+
setOnlineStatus(state: {
|
|
166
|
+
isOnline: boolean;
|
|
167
|
+
queue: {
|
|
168
|
+
id: string;
|
|
169
|
+
endpointName: string;
|
|
170
|
+
args: unknown;
|
|
171
|
+
timestamp: string;
|
|
172
|
+
baseUpdatedAt?: string | undefined;
|
|
173
|
+
type: "create" | "update" | "delete";
|
|
174
|
+
userId?: string | undefined;
|
|
175
|
+
}[];
|
|
176
|
+
conflicts: {
|
|
177
|
+
id: string;
|
|
178
|
+
endpointName: string;
|
|
179
|
+
args: unknown;
|
|
180
|
+
serverDocument: unknown;
|
|
181
|
+
timestamp: string;
|
|
182
|
+
dismissed: boolean;
|
|
183
|
+
}[];
|
|
184
|
+
isSyncing: boolean;
|
|
185
|
+
}, action: PayloadAction<boolean>): void;
|
|
186
|
+
setSyncing(state: {
|
|
187
|
+
isOnline: boolean;
|
|
188
|
+
queue: {
|
|
189
|
+
id: string;
|
|
190
|
+
endpointName: string;
|
|
191
|
+
args: unknown;
|
|
192
|
+
timestamp: string;
|
|
193
|
+
baseUpdatedAt?: string | undefined;
|
|
194
|
+
type: "create" | "update" | "delete";
|
|
195
|
+
userId?: string | undefined;
|
|
196
|
+
}[];
|
|
197
|
+
conflicts: {
|
|
198
|
+
id: string;
|
|
199
|
+
endpointName: string;
|
|
200
|
+
args: unknown;
|
|
201
|
+
serverDocument: unknown;
|
|
202
|
+
timestamp: string;
|
|
203
|
+
dismissed: boolean;
|
|
204
|
+
}[];
|
|
205
|
+
isSyncing: boolean;
|
|
206
|
+
}, action: PayloadAction<boolean>): void;
|
|
207
|
+
}, "offline", "offline", import("@reduxjs/toolkit").SliceSelectors<OfflineState>>;
|
|
208
|
+
export declare const setOnlineStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "offline/setOnlineStatus">, enqueue: import("@reduxjs/toolkit").ActionCreatorWithPayload<QueuedMutation, "offline/enqueue">, dequeue: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "offline/dequeue">, clearQueue: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"offline/clearQueue">, addConflict: import("@reduxjs/toolkit").ActionCreatorWithPayload<ConflictRecord, "offline/addConflict">, dismissConflict: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "offline/dismissConflict">, clearConflicts: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"offline/clearConflicts">, setSyncing: import("@reduxjs/toolkit").ActionCreatorWithPayload<boolean, "offline/setSyncing">;
|
|
209
|
+
export declare const offlineReducer: import("@reduxjs/toolkit").Reducer<OfflineState>;
|
|
210
|
+
export declare const selectIsOnline: (state: {
|
|
211
|
+
offline: OfflineState;
|
|
212
|
+
}) => boolean;
|
|
213
|
+
/** Safe online check when the offline reducer may not be mounted (defaults to online). */
|
|
214
|
+
export declare const selectIsOnlineSafe: (state: {
|
|
215
|
+
offline?: OfflineState;
|
|
216
|
+
}) => boolean;
|
|
217
|
+
export declare const selectOfflineQueue: (state: {
|
|
218
|
+
offline: OfflineState;
|
|
219
|
+
}) => QueuedMutation[];
|
|
220
|
+
export declare const selectQueueLength: (state: {
|
|
221
|
+
offline: OfflineState;
|
|
222
|
+
}) => number;
|
|
223
|
+
export declare const selectConflicts: (state: {
|
|
224
|
+
offline: OfflineState;
|
|
225
|
+
}) => ConflictRecord[];
|
|
226
|
+
export declare const selectUndismissedConflicts: (state: {
|
|
227
|
+
offline: OfflineState;
|
|
228
|
+
}) => ConflictRecord[];
|
|
229
|
+
export declare const selectIsSyncing: (state: {
|
|
230
|
+
offline: OfflineState;
|
|
231
|
+
}) => boolean;
|
|
232
|
+
//# sourceMappingURL=offlineSlice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offlineSlice.d.ts","sourceRoot":"","sources":["../src/offlineSlice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createSlice } from "@reduxjs/toolkit";
|
|
2
|
+
import { REHYDRATE } from "redux-persist";
|
|
3
|
+
import { IsWeb } from "./platform";
|
|
4
|
+
const getInitialOnlineStatus = () => {
|
|
5
|
+
if (IsWeb && typeof navigator !== "undefined" && typeof navigator.onLine === "boolean") {
|
|
6
|
+
return navigator.onLine;
|
|
7
|
+
}
|
|
8
|
+
return true;
|
|
9
|
+
};
|
|
10
|
+
const initialState = {
|
|
11
|
+
conflicts: [],
|
|
12
|
+
isOnline: getInitialOnlineStatus(),
|
|
13
|
+
isSyncing: false,
|
|
14
|
+
queue: [],
|
|
15
|
+
};
|
|
16
|
+
export const offlineSlice = createSlice({
|
|
17
|
+
extraReducers: (builder) => {
|
|
18
|
+
builder.addCase(REHYDRATE, (state) => {
|
|
19
|
+
state.isSyncing = false;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
initialState,
|
|
23
|
+
name: "offline",
|
|
24
|
+
reducers: {
|
|
25
|
+
addConflict(state, action) {
|
|
26
|
+
state.conflicts.push(action.payload);
|
|
27
|
+
},
|
|
28
|
+
clearConflicts(state) {
|
|
29
|
+
state.conflicts = [];
|
|
30
|
+
},
|
|
31
|
+
clearQueue(state) {
|
|
32
|
+
state.queue = [];
|
|
33
|
+
},
|
|
34
|
+
dequeue(state, action) {
|
|
35
|
+
state.queue = state.queue.filter((m) => m.id !== action.payload);
|
|
36
|
+
},
|
|
37
|
+
dismissConflict(state, action) {
|
|
38
|
+
const conflict = state.conflicts.find((c) => c.id === action.payload);
|
|
39
|
+
if (conflict) {
|
|
40
|
+
conflict.dismissed = true;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
enqueue(state, action) {
|
|
44
|
+
state.queue.push(action.payload);
|
|
45
|
+
},
|
|
46
|
+
setOnlineStatus(state, action) {
|
|
47
|
+
state.isOnline = action.payload;
|
|
48
|
+
},
|
|
49
|
+
setSyncing(state, action) {
|
|
50
|
+
state.isSyncing = action.payload;
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
export const { setOnlineStatus, enqueue, dequeue, clearQueue, addConflict, dismissConflict, clearConflicts, setSyncing, } = offlineSlice.actions;
|
|
55
|
+
export const offlineReducer = offlineSlice.reducer;
|
|
56
|
+
// Selectors
|
|
57
|
+
export const selectIsOnline = (state) => state.offline.isOnline;
|
|
58
|
+
/** Safe online check when the offline reducer may not be mounted (defaults to online). */
|
|
59
|
+
export const selectIsOnlineSafe = (state) => state.offline?.isOnline ?? true;
|
|
60
|
+
export const selectOfflineQueue = (state) => state.offline.queue;
|
|
61
|
+
export const selectQueueLength = (state) => state.offline.queue.length;
|
|
62
|
+
export const selectConflicts = (state) => state.offline.conflicts;
|
|
63
|
+
export const selectUndismissedConflicts = (state) => state.offline.conflicts.filter((c) => !c.dismissed);
|
|
64
|
+
export const selectIsSyncing = (state) => state.offline.isSyncing;
|
|
65
|
+
//# sourceMappingURL=offlineSlice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offlineSlice.js","sourceRoot":"","sources":["../src/offlineSlice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offlineSlice.test.d.ts","sourceRoot":"","sources":["../src/offlineSlice.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { configureStore } from "@reduxjs/toolkit";
|
|
3
|
+
import { REHYDRATE } from "redux-persist";
|
|
4
|
+
import { addConflict, clearConflicts, clearQueue, dequeue, dismissConflict, enqueue, offlineReducer, selectConflicts, selectIsOnline, selectIsOnlineSafe, selectIsSyncing, selectOfflineQueue, selectQueueLength, selectUndismissedConflicts, setOnlineStatus, setSyncing, } from "./offlineSlice";
|
|
5
|
+
const createTestStore = () => configureStore({
|
|
6
|
+
reducer: { offline: offlineReducer },
|
|
7
|
+
});
|
|
8
|
+
describe("offlineSlice", () => {
|
|
9
|
+
let store;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
store = createTestStore();
|
|
12
|
+
});
|
|
13
|
+
describe("setOnlineStatus", () => {
|
|
14
|
+
it("sets online status to false", () => {
|
|
15
|
+
store.dispatch(setOnlineStatus(false));
|
|
16
|
+
expect(selectIsOnline(store.getState())).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it("sets online status to true", () => {
|
|
19
|
+
store.dispatch(setOnlineStatus(false));
|
|
20
|
+
store.dispatch(setOnlineStatus(true));
|
|
21
|
+
expect(selectIsOnline(store.getState())).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("defaults to online", () => {
|
|
24
|
+
expect(selectIsOnline(store.getState())).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("mutation queue", () => {
|
|
28
|
+
const mutation1 = {
|
|
29
|
+
args: { body: { title: "Updated" }, id: "123" },
|
|
30
|
+
endpointName: "patchTodosById",
|
|
31
|
+
id: "m1",
|
|
32
|
+
timestamp: "2026-04-15T10:00:00.000Z",
|
|
33
|
+
type: "update",
|
|
34
|
+
};
|
|
35
|
+
const mutation2 = {
|
|
36
|
+
args: { body: { title: "New Todo" } },
|
|
37
|
+
endpointName: "postTodos",
|
|
38
|
+
id: "m2",
|
|
39
|
+
timestamp: "2026-04-15T10:01:00.000Z",
|
|
40
|
+
type: "create",
|
|
41
|
+
};
|
|
42
|
+
it("enqueues mutations in FIFO order", () => {
|
|
43
|
+
store.dispatch(enqueue(mutation1));
|
|
44
|
+
store.dispatch(enqueue(mutation2));
|
|
45
|
+
const queue = selectOfflineQueue(store.getState());
|
|
46
|
+
expect(queue).toHaveLength(2);
|
|
47
|
+
expect(queue[0].id).toBe("m1");
|
|
48
|
+
expect(queue[1].id).toBe("m2");
|
|
49
|
+
});
|
|
50
|
+
it("dequeues by id", () => {
|
|
51
|
+
store.dispatch(enqueue(mutation1));
|
|
52
|
+
store.dispatch(enqueue(mutation2));
|
|
53
|
+
store.dispatch(dequeue("m1"));
|
|
54
|
+
const queue = selectOfflineQueue(store.getState());
|
|
55
|
+
expect(queue).toHaveLength(1);
|
|
56
|
+
expect(queue[0].id).toBe("m2");
|
|
57
|
+
});
|
|
58
|
+
it("clears entire queue", () => {
|
|
59
|
+
store.dispatch(enqueue(mutation1));
|
|
60
|
+
store.dispatch(enqueue(mutation2));
|
|
61
|
+
store.dispatch(clearQueue());
|
|
62
|
+
expect(selectOfflineQueue(store.getState())).toHaveLength(0);
|
|
63
|
+
});
|
|
64
|
+
it("reports queue length", () => {
|
|
65
|
+
expect(selectQueueLength(store.getState())).toBe(0);
|
|
66
|
+
store.dispatch(enqueue(mutation1));
|
|
67
|
+
expect(selectQueueLength(store.getState())).toBe(1);
|
|
68
|
+
store.dispatch(enqueue(mutation2));
|
|
69
|
+
expect(selectQueueLength(store.getState())).toBe(2);
|
|
70
|
+
});
|
|
71
|
+
it("preserves mutation args", () => {
|
|
72
|
+
store.dispatch(enqueue(mutation1));
|
|
73
|
+
const queued = selectOfflineQueue(store.getState())[0];
|
|
74
|
+
expect(queued.args).toEqual({ body: { title: "Updated" }, id: "123" });
|
|
75
|
+
expect(queued.endpointName).toBe("patchTodosById");
|
|
76
|
+
expect(queued.type).toBe("update");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("conflicts", () => {
|
|
80
|
+
const conflict1 = {
|
|
81
|
+
args: { body: { title: "Offline" }, id: "123" },
|
|
82
|
+
dismissed: false,
|
|
83
|
+
endpointName: "patchTodosById",
|
|
84
|
+
id: "c1",
|
|
85
|
+
serverDocument: { _id: "123", title: "Server Version", updated: "2026-04-15T11:00:00.000Z" },
|
|
86
|
+
timestamp: "2026-04-15T10:30:00.000Z",
|
|
87
|
+
};
|
|
88
|
+
const conflict2 = {
|
|
89
|
+
args: { body: { completed: true }, id: "456" },
|
|
90
|
+
dismissed: false,
|
|
91
|
+
endpointName: "patchTodosById",
|
|
92
|
+
id: "c2",
|
|
93
|
+
serverDocument: { _id: "456", completed: false, title: "Other" },
|
|
94
|
+
timestamp: "2026-04-15T10:31:00.000Z",
|
|
95
|
+
};
|
|
96
|
+
it("adds conflict records", () => {
|
|
97
|
+
store.dispatch(addConflict(conflict1));
|
|
98
|
+
expect(selectConflicts(store.getState())).toHaveLength(1);
|
|
99
|
+
expect(selectConflicts(store.getState())[0].id).toBe("c1");
|
|
100
|
+
});
|
|
101
|
+
it("dismisses a conflict", () => {
|
|
102
|
+
store.dispatch(addConflict(conflict1));
|
|
103
|
+
store.dispatch(addConflict(conflict2));
|
|
104
|
+
store.dispatch(dismissConflict("c1"));
|
|
105
|
+
const conflicts = selectConflicts(store.getState());
|
|
106
|
+
expect(conflicts[0].dismissed).toBe(true);
|
|
107
|
+
expect(conflicts[1].dismissed).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it("filters undismissed conflicts", () => {
|
|
110
|
+
store.dispatch(addConflict(conflict1));
|
|
111
|
+
store.dispatch(addConflict(conflict2));
|
|
112
|
+
store.dispatch(dismissConflict("c1"));
|
|
113
|
+
const undismissed = selectUndismissedConflicts(store.getState());
|
|
114
|
+
expect(undismissed).toHaveLength(1);
|
|
115
|
+
expect(undismissed[0].id).toBe("c2");
|
|
116
|
+
});
|
|
117
|
+
it("clears all conflicts", () => {
|
|
118
|
+
store.dispatch(addConflict(conflict1));
|
|
119
|
+
store.dispatch(addConflict(conflict2));
|
|
120
|
+
store.dispatch(clearConflicts());
|
|
121
|
+
expect(selectConflicts(store.getState())).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe("selectIsOnlineSafe", () => {
|
|
125
|
+
it("defaults to online when offline reducer is absent", () => {
|
|
126
|
+
expect(selectIsOnlineSafe({})).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
it("reflects offline state when reducer is mounted", () => {
|
|
129
|
+
store.dispatch(setOnlineStatus(false));
|
|
130
|
+
expect(selectIsOnlineSafe(store.getState())).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
describe("syncing", () => {
|
|
134
|
+
it("defaults to not syncing", () => {
|
|
135
|
+
expect(selectIsSyncing(store.getState())).toBe(false);
|
|
136
|
+
});
|
|
137
|
+
it("sets syncing state", () => {
|
|
138
|
+
store.dispatch(setSyncing(true));
|
|
139
|
+
expect(selectIsSyncing(store.getState())).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
it("clears syncing state", () => {
|
|
142
|
+
store.dispatch(setSyncing(true));
|
|
143
|
+
store.dispatch(setSyncing(false));
|
|
144
|
+
expect(selectIsSyncing(store.getState())).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
it("resets syncing on redux-persist rehydrate", () => {
|
|
147
|
+
store.dispatch(setSyncing(true));
|
|
148
|
+
store.dispatch({ payload: undefined, type: REHYDRATE });
|
|
149
|
+
expect(selectIsSyncing(store.getState())).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=offlineSlice.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offlineSlice.test.js","sourceRoot":"","sources":["../src/offlineSlice.test.ts"],"names":[],"mappings":"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"}
|