@carlonicora/nextjs-jsonapi 6.12.1 → 6.13.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/{ApiData-DPKNfY-9.d.mts → ApiData-Be8yxCOX.d.mts} +2 -0
- package/dist/{ApiData-DPKNfY-9.d.ts → ApiData-Be8yxCOX.d.ts} +2 -0
- package/dist/{BlockNoteEditor-N2NBYCPC.mjs → BlockNoteEditor-22Q4DJBU.mjs} +20 -8
- package/dist/BlockNoteEditor-22Q4DJBU.mjs.map +1 -0
- package/dist/{BlockNoteEditor-CBYKEXAQ.js → BlockNoteEditor-2VLPSEKS.js} +34 -22
- package/dist/BlockNoteEditor-2VLPSEKS.js.map +1 -0
- package/dist/{BlockNoteViewer-WGCDB7IJ.js → BlockNoteViewer-2XYASA5Y.js} +8 -5
- package/dist/BlockNoteViewer-2XYASA5Y.js.map +1 -0
- package/dist/{BlockNoteViewer-MTGS6YJV.mjs → BlockNoteViewer-NBW2H6LC.mjs} +7 -4
- package/dist/{BlockNoteViewer-MTGS6YJV.mjs.map → BlockNoteViewer-NBW2H6LC.mjs.map} +1 -1
- package/dist/{JsonApiRequest-A66XK5WT.mjs → JsonApiRequest-F6TMJ42G.mjs} +2 -2
- package/dist/JsonApiRequest-WISJZSDE.js +26 -0
- package/dist/{JsonApiRequest-FZGPLNUF.js.map → JsonApiRequest-WISJZSDE.js.map} +1 -1
- package/dist/billing/index.js +499 -494
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +13 -8
- package/dist/billing/index.mjs.map +1 -1
- package/dist/chunk-27LQDGNP.mjs +317 -0
- package/dist/chunk-27LQDGNP.mjs.map +1 -0
- package/dist/{chunk-6NZKWIOR.js → chunk-2H342HNN.js} +956 -929
- package/dist/chunk-2H342HNN.js.map +1 -0
- package/dist/{chunk-KSYQEKJX.js → chunk-2SV4SS6N.js} +14 -14
- package/dist/{chunk-KSYQEKJX.js.map → chunk-2SV4SS6N.js.map} +1 -1
- package/dist/{chunk-4FNGWALA.mjs → chunk-47BBY5KA.mjs} +6 -4
- package/dist/{chunk-4FNGWALA.mjs.map → chunk-47BBY5KA.mjs.map} +1 -1
- package/dist/chunk-6JUCIWDC.mjs +570 -0
- package/dist/chunk-6JUCIWDC.mjs.map +1 -0
- package/dist/chunk-7DBJTNSU.mjs +1049 -0
- package/dist/chunk-7DBJTNSU.mjs.map +1 -0
- package/dist/chunk-7JZHPMGW.mjs +704 -0
- package/dist/chunk-7JZHPMGW.mjs.map +1 -0
- package/dist/{chunk-L6PYFQTO.js → chunk-7ZHTPFFT.js} +16 -14
- package/dist/chunk-7ZHTPFFT.js.map +1 -0
- package/dist/chunk-B6JGKDP2.js +214 -0
- package/dist/chunk-B6JGKDP2.js.map +1 -0
- package/dist/{chunk-3YW35YHQ.mjs → chunk-BH6JWIKI.mjs} +5 -5
- package/dist/chunk-BZ5J2FIV.js +570 -0
- package/dist/chunk-BZ5J2FIV.js.map +1 -0
- package/dist/{chunk-V5MI7RNB.mjs → chunk-DGWOVYEC.mjs} +2 -2
- package/dist/chunk-DOCIFE6A.mjs +212 -0
- package/dist/chunk-DOCIFE6A.mjs.map +1 -0
- package/dist/chunk-GB6DBFYY.js +25 -0
- package/dist/chunk-GB6DBFYY.js.map +1 -0
- package/dist/{chunk-6XJOQFCP.mjs → chunk-H4NLDX5G.mjs} +59 -32
- package/dist/chunk-H4NLDX5G.mjs.map +1 -0
- package/dist/{chunk-EWCY6QJA.mjs → chunk-JJMMT24D.mjs} +37 -3
- package/dist/chunk-JJMMT24D.mjs.map +1 -0
- package/dist/{chunk-JTPRJNT2.js → chunk-KMW7UNL2.js} +38 -3
- package/dist/chunk-KMW7UNL2.js.map +1 -0
- package/dist/chunk-MHB36MYE.js +317 -0
- package/dist/chunk-MHB36MYE.js.map +1 -0
- package/dist/{chunk-FF4EIQ6H.js → chunk-MOGEGWXW.js} +1039 -1680
- package/dist/chunk-MOGEGWXW.js.map +1 -0
- package/dist/{chunk-AA52ORW2.mjs → chunk-P445QGOC.mjs} +61 -702
- package/dist/chunk-P445QGOC.mjs.map +1 -0
- package/dist/chunk-PV4Z5W6V.js +212 -0
- package/dist/chunk-PV4Z5W6V.js.map +1 -0
- package/dist/chunk-PXDUJ47N.js +1049 -0
- package/dist/chunk-PXDUJ47N.js.map +1 -0
- package/dist/{chunk-GHE4VT2R.js → chunk-QKNGZDSQ.js} +2 -2
- package/dist/{chunk-GHE4VT2R.js.map → chunk-QKNGZDSQ.js.map} +1 -1
- package/dist/chunk-W62MHDWM.js +704 -0
- package/dist/chunk-W62MHDWM.js.map +1 -0
- package/dist/chunk-XXP3TQHF.mjs +25 -0
- package/dist/chunk-XXP3TQHF.mjs.map +1 -0
- package/dist/chunk-YRKIFYOE.mjs +214 -0
- package/dist/chunk-YRKIFYOE.mjs.map +1 -0
- package/dist/{chunk-O3PDL2CV.js → chunk-Z4POQWFG.js} +40 -6
- package/dist/chunk-Z4POQWFG.js.map +1 -0
- package/dist/{chunk-PI56A4DG.mjs → chunk-ZLTOUZN2.mjs} +37 -2
- package/dist/chunk-ZLTOUZN2.mjs.map +1 -0
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +14 -9
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +24 -19
- package/dist/components/index.d.mts +7 -1
- package/dist/components/index.d.ts +7 -1
- package/dist/components/index.js +11 -7
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +10 -6
- package/dist/contexts/index.d.mts +1 -1
- package/dist/contexts/index.d.ts +1 -1
- package/dist/contexts/index.js +11 -7
- package/dist/contexts/index.js.map +1 -1
- package/dist/contexts/index.mjs +10 -6
- package/dist/core/index.d.mts +128 -2
- package/dist/core/index.d.ts +128 -2
- package/dist/core/index.js +17 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +22 -9
- package/dist/email-automations/index.js +101 -96
- package/dist/email-automations/index.js.map +1 -1
- package/dist/email-automations/index.mjs +13 -8
- package/dist/email-automations/index.mjs.map +1 -1
- package/dist/features/analytics/index.js +128 -123
- package/dist/features/analytics/index.js.map +1 -1
- package/dist/features/analytics/index.mjs +13 -8
- package/dist/features/analytics/index.mjs.map +1 -1
- package/dist/features/analytics/server.js +11 -8
- package/dist/features/analytics/server.js.map +1 -1
- package/dist/features/analytics/server.mjs +9 -6
- package/dist/features/analytics/server.mjs.map +1 -1
- package/dist/features/blog/index.js +158 -153
- package/dist/features/blog/index.js.map +1 -1
- package/dist/features/blog/index.mjs +14 -9
- package/dist/features/blog/index.mjs.map +1 -1
- package/dist/features/blog/server.js +30 -26
- package/dist/features/blog/server.js.map +1 -1
- package/dist/features/blog/server.mjs +10 -6
- package/dist/features/blog/server.mjs.map +1 -1
- package/dist/features/help/index.js +44 -40
- package/dist/features/help/index.js.map +1 -1
- package/dist/features/help/index.mjs +10 -6
- package/dist/features/help/index.mjs.map +1 -1
- package/dist/features/tokenusage/index.js +134 -129
- package/dist/features/tokenusage/index.js.map +1 -1
- package/dist/features/tokenusage/index.mjs +13 -8
- package/dist/features/tokenusage/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +18 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -11
- package/dist/lifecycle-4GUOTB46.js +23 -0
- package/dist/lifecycle-4GUOTB46.js.map +1 -0
- package/dist/lifecycle-LKGPRBS7.mjs +23 -0
- package/dist/local-transport-7H3SPZSA.js +15 -0
- package/dist/local-transport-7H3SPZSA.js.map +1 -0
- package/dist/local-transport-QYFKA6AN.mjs +15 -0
- package/dist/local-transport-QYFKA6AN.mjs.map +1 -0
- package/dist/offline/index.d.mts +607 -0
- package/dist/offline/index.d.ts +607 -0
- package/dist/offline/index.js +1052 -0
- package/dist/offline/index.js.map +1 -0
- package/dist/offline/index.mjs +1052 -0
- package/dist/offline/index.mjs.map +1 -0
- package/dist/offline/sqlite/device-worker.mjs +62 -0
- package/dist/offline/sqlite/index.mjs +17392 -0
- package/dist/offline/sqlite/sqlite3-opfs-async-proxy.js +881 -0
- package/dist/offline/sqlite/sqlite3.wasm +0 -0
- package/dist/request-KTBV6HHH.js +10 -0
- package/dist/request-KTBV6HHH.js.map +1 -0
- package/dist/request-SQOUNWZ4.mjs +10 -0
- package/dist/request-SQOUNWZ4.mjs.map +1 -0
- package/dist/server/cachedGet.d.mts +1 -1
- package/dist/server/cachedGet.d.ts +1 -1
- package/dist/server/cachedGet.stub.d.mts +1 -1
- package/dist/server/cachedGet.stub.d.ts +1 -1
- package/dist/server/index.d.mts +28 -2
- package/dist/server/index.d.ts +28 -2
- package/dist/server/index.js +18 -8
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +18 -8
- package/dist/{useSocket-IRHtk2xc.d.ts → useSocket-Bo9M0t0J.d.ts} +3 -1
- package/dist/{useSocket-C8fqU4od.d.mts → useSocket-J5qanQPN.d.mts} +3 -1
- package/dist/write-through-FDQ7N4YI.js +15 -0
- package/dist/write-through-FDQ7N4YI.js.map +1 -0
- package/dist/write-through-R25VQMH6.mjs +15 -0
- package/dist/write-through-R25VQMH6.mjs.map +1 -0
- package/package.json +7 -1
- package/pwa/sw.js +277 -26
- package/src/client/request.ts +37 -0
- package/src/components/containers/RoundPageContainer.tsx +8 -8
- package/src/components/editors/BlockNoteEditor.tsx +14 -0
- package/src/components/editors/__tests__/BlockNoteEditor.empty-change.spec.tsx +205 -0
- package/src/contexts/SocketContext.tsx +7 -1
- package/src/contexts/__tests__/SocketContext.session.spec.tsx +52 -5
- package/src/core/factories/RehydrationFactory.ts +28 -8
- package/src/core/factories/__tests__/RehydrationFactory.spec.ts +33 -0
- package/src/core/index.ts +3 -0
- package/src/core/interfaces/ApiData.ts +2 -0
- package/src/core/registry/ModuleRegistry.ts +3 -0
- package/src/features/auth/components/forms/Logout.tsx +8 -1
- package/src/features/auth/components/forms/__tests__/Logout.spec.tsx +62 -0
- package/src/hooks/__tests__/useSocket.pause.spec.ts +39 -0
- package/src/hooks/useSocket.ts +20 -1
- package/src/offline/__tests__/answer-builders.spec.ts +176 -0
- package/src/offline/__tests__/device-switch.spec.ts +179 -0
- package/src/offline/__tests__/generic-answers.spec.ts +380 -0
- package/src/offline/__tests__/local-transport.spec.ts +150 -0
- package/src/offline/__tests__/logout.spec.ts +122 -0
- package/src/offline/__tests__/offline-fixtures.ts +89 -0
- package/src/offline/__tests__/router.spec.ts +56 -0
- package/src/offline/__tests__/state.spec.ts +62 -0
- package/src/offline/__tests__/write-through.spec.ts +137 -0
- package/src/offline/config.ts +229 -0
- package/src/offline/configure.ts +14 -0
- package/src/offline/device-switch.ts +75 -0
- package/src/offline/generic-answers.ts +706 -0
- package/src/offline/index.ts +67 -0
- package/src/offline/local-transport.ts +189 -0
- package/src/offline/logout.ts +41 -0
- package/src/offline/router.ts +41 -0
- package/src/offline/server/__tests__/build-offline-manifest.spec.ts +76 -0
- package/src/offline/server/build-offline-manifest.ts +49 -0
- package/src/offline/state-hooks.ts +10 -0
- package/src/offline/state.ts +143 -0
- package/src/offline/store/DeviceStore.ts +112 -0
- package/src/offline/store/__tests__/device-store-lifecycle.spec.ts +343 -0
- package/src/offline/store/__tests__/shared-database.spec.ts +151 -0
- package/src/offline/store/__tests__/sql-device-store.spec.ts +399 -0
- package/src/offline/store/lifecycle.ts +315 -0
- package/src/offline/store/schema.ts +101 -0
- package/src/offline/store/shared-database.ts +321 -0
- package/src/offline/store/sql-device-store.ts +469 -0
- package/src/offline/store/worker-client.ts +71 -0
- package/src/offline/store/worker.ts +87 -0
- package/src/offline/sync/__tests__/conflicts.spec.ts +185 -0
- package/src/offline/sync/__tests__/queue-drain.spec.ts +364 -0
- package/src/offline/sync/__tests__/runtime.spec.ts +337 -0
- package/src/offline/sync/__tests__/sync-fixtures.ts +103 -0
- package/src/offline/sync/__tests__/sync-root.spec.ts +727 -0
- package/src/offline/sync/artwork-cache.ts +67 -0
- package/src/offline/sync/conflicts.ts +211 -0
- package/src/offline/sync/counts.ts +39 -0
- package/src/offline/sync/drain-queue.ts +246 -0
- package/src/offline/sync/known-roots.ts +37 -0
- package/src/offline/sync/models/OfflineChangeSet.ts +34 -0
- package/src/offline/sync/models/OfflineChangeSetInterface.ts +54 -0
- package/src/offline/sync/models/RootSync.ts +68 -0
- package/src/offline/sync/models/RootSyncInterface.ts +20 -0
- package/src/offline/sync/models/__tests__/OfflineChangeSet.spec.ts +47 -0
- package/src/offline/sync/models/__tests__/RootSync.spec.ts +54 -0
- package/src/offline/sync/models/index.ts +5 -0
- package/src/offline/sync/models/offline.modules.ts +14 -0
- package/src/offline/sync/runtime.ts +226 -0
- package/src/offline/sync/session-refresh.ts +32 -0
- package/src/offline/sync/sync-root.ts +336 -0
- package/src/offline/sync/waiting.ts +40 -0
- package/src/offline/types.ts +115 -0
- package/src/offline/write-through.ts +189 -0
- package/src/pwa/__tests__/sw.spec.ts +358 -1
- package/src/server/index.ts +5 -0
- package/src/unified/__tests__/JsonApiRequest.offline.spec.ts +243 -0
- package/dist/BlockNoteEditor-CBYKEXAQ.js.map +0 -1
- package/dist/BlockNoteEditor-N2NBYCPC.mjs.map +0 -1
- package/dist/BlockNoteViewer-WGCDB7IJ.js.map +0 -1
- package/dist/JsonApiRequest-FZGPLNUF.js +0 -26
- package/dist/chunk-6NZKWIOR.js.map +0 -1
- package/dist/chunk-6XJOQFCP.mjs.map +0 -1
- package/dist/chunk-AA52ORW2.mjs.map +0 -1
- package/dist/chunk-EWCY6QJA.mjs.map +0 -1
- package/dist/chunk-FF4EIQ6H.js.map +0 -1
- package/dist/chunk-JTPRJNT2.js.map +0 -1
- package/dist/chunk-L6PYFQTO.js.map +0 -1
- package/dist/chunk-O3PDL2CV.js.map +0 -1
- package/dist/chunk-PI56A4DG.mjs.map +0 -1
- package/dist/request-5RLK2AM3.mjs +0 -9
- package/dist/request-EUGTI3BE.js +0 -9
- package/dist/request-EUGTI3BE.js.map +0 -1
- /package/dist/{JsonApiRequest-A66XK5WT.mjs.map → JsonApiRequest-F6TMJ42G.mjs.map} +0 -0
- /package/dist/{chunk-3YW35YHQ.mjs.map → chunk-BH6JWIKI.mjs.map} +0 -0
- /package/dist/{chunk-V5MI7RNB.mjs.map → chunk-DGWOVYEC.mjs.map} +0 -0
- /package/dist/{request-5RLK2AM3.mjs.map → lifecycle-LKGPRBS7.mjs.map} +0 -0
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
knownRootIds,
|
|
4
|
+
recordKeyOf,
|
|
5
|
+
recordKeysOfEntry,
|
|
6
|
+
refreshLockedRoots,
|
|
7
|
+
rememberSyncRoots,
|
|
8
|
+
waitingRecordKeys,
|
|
9
|
+
writeThrough
|
|
10
|
+
} from "../chunk-YRKIFYOE.mjs";
|
|
11
|
+
import {
|
|
12
|
+
cacheArtworkImage,
|
|
13
|
+
deleteDeviceStore,
|
|
14
|
+
deleteImagesCaches,
|
|
15
|
+
hasUnsentChanges,
|
|
16
|
+
imageUrlOf,
|
|
17
|
+
isRootLocked,
|
|
18
|
+
onUserSignedIn,
|
|
19
|
+
openDeviceStore,
|
|
20
|
+
openMemoryDeviceStore,
|
|
21
|
+
removeArtworkImage
|
|
22
|
+
} from "../chunk-7DBJTNSU.mjs";
|
|
23
|
+
import {
|
|
24
|
+
SERVER_DEFAULT_ORDER_BY,
|
|
25
|
+
errorAnswer,
|
|
26
|
+
includedFor,
|
|
27
|
+
linkKindFor,
|
|
28
|
+
linkKindsOf,
|
|
29
|
+
linksOfKind,
|
|
30
|
+
listAnswer,
|
|
31
|
+
localTransport,
|
|
32
|
+
matchPattern,
|
|
33
|
+
matchRoute,
|
|
34
|
+
mergeResource,
|
|
35
|
+
needsConnectionAnswer,
|
|
36
|
+
notFoundAnswer,
|
|
37
|
+
orderRecords,
|
|
38
|
+
orderedListAnswer,
|
|
39
|
+
resourceFromDocument,
|
|
40
|
+
resourceListAnswer,
|
|
41
|
+
searchRecords,
|
|
42
|
+
singleAnswer
|
|
43
|
+
} from "../chunk-7JZHPMGW.mjs";
|
|
44
|
+
import {
|
|
45
|
+
useNeedsConnection,
|
|
46
|
+
useOfflineState
|
|
47
|
+
} from "../chunk-XXP3TQHF.mjs";
|
|
48
|
+
import {
|
|
49
|
+
EndpointCreator,
|
|
50
|
+
Modules,
|
|
51
|
+
ROOT_SYNC_ENDPOINT,
|
|
52
|
+
getI18nDefaultLocale,
|
|
53
|
+
getI18nSupportedLocales
|
|
54
|
+
} from "../chunk-6JUCIWDC.mjs";
|
|
55
|
+
import "../chunk-DOCIFE6A.mjs";
|
|
56
|
+
import {
|
|
57
|
+
directFetch
|
|
58
|
+
} from "../chunk-ZLTOUZN2.mjs";
|
|
59
|
+
import {
|
|
60
|
+
DEFAULT_DEVICE_WORKER_URL,
|
|
61
|
+
DEFAULT_SQLITE_MODULE_URL,
|
|
62
|
+
DEVICE_WORKER_FILE,
|
|
63
|
+
DeviceStoreUnavailableError,
|
|
64
|
+
NeedsConnectionError,
|
|
65
|
+
OFFLINE_REPLAY_HEADER,
|
|
66
|
+
OFFLINE_SQLITE_FILES,
|
|
67
|
+
OfflineUnavailableError,
|
|
68
|
+
allowWhileReconnecting,
|
|
69
|
+
blockOfflineStore,
|
|
70
|
+
canReachApi,
|
|
71
|
+
configureOffline,
|
|
72
|
+
forgetOfflineStore,
|
|
73
|
+
getOfflineConfig,
|
|
74
|
+
getOfflineState,
|
|
75
|
+
getOfflineStore,
|
|
76
|
+
isOfflineAvailable,
|
|
77
|
+
isOfflineConfigured,
|
|
78
|
+
isOfflineEnabledOnDevice,
|
|
79
|
+
isReconnecting,
|
|
80
|
+
patternToString,
|
|
81
|
+
refreshOfflineDeviceState,
|
|
82
|
+
setDefaultDeviceStoreOpener,
|
|
83
|
+
setDeviceStoreOpener,
|
|
84
|
+
setOfflineEnabledOnDevice,
|
|
85
|
+
setOfflineState,
|
|
86
|
+
setReconnecting,
|
|
87
|
+
subscribeOfflineState,
|
|
88
|
+
writeOfflineLastSync
|
|
89
|
+
} from "../chunk-27LQDGNP.mjs";
|
|
90
|
+
import {
|
|
91
|
+
JsonApiGet,
|
|
92
|
+
JsonApiPost,
|
|
93
|
+
buildUrl
|
|
94
|
+
} from "../chunk-DGWOVYEC.mjs";
|
|
95
|
+
import {
|
|
96
|
+
getClientToken
|
|
97
|
+
} from "../chunk-AUXK7QSA.mjs";
|
|
98
|
+
import {
|
|
99
|
+
__name
|
|
100
|
+
} from "../chunk-PAWJFY3S.mjs";
|
|
101
|
+
|
|
102
|
+
// src/offline/configure.ts
|
|
103
|
+
function configureOffline2(config) {
|
|
104
|
+
setDefaultDeviceStoreOpener(
|
|
105
|
+
(userId) => import("../lifecycle-LKGPRBS7.mjs").then((lifecycle) => lifecycle.openDeviceStore(userId))
|
|
106
|
+
);
|
|
107
|
+
configureOffline(config);
|
|
108
|
+
}
|
|
109
|
+
__name(configureOffline2, "configureOffline");
|
|
110
|
+
|
|
111
|
+
// src/offline/sync/session-refresh.ts
|
|
112
|
+
import { getCookie } from "cookies-next";
|
|
113
|
+
function currentLocale() {
|
|
114
|
+
if (typeof window === "undefined") return getI18nDefaultLocale();
|
|
115
|
+
const first = window.location.pathname.split("/").filter(Boolean)[0];
|
|
116
|
+
return first && getI18nSupportedLocales().includes(first) ? first : getI18nDefaultLocale();
|
|
117
|
+
}
|
|
118
|
+
__name(currentLocale, "currentLocale");
|
|
119
|
+
async function refreshSession() {
|
|
120
|
+
try {
|
|
121
|
+
await fetch(getOfflineConfig().shellPath(currentLocale()), { cache: "no-store", credentials: "same-origin" });
|
|
122
|
+
} catch {
|
|
123
|
+
return "unreachable";
|
|
124
|
+
}
|
|
125
|
+
const token = await getCookie("token");
|
|
126
|
+
return token ? "ok" : "expired";
|
|
127
|
+
}
|
|
128
|
+
__name(refreshSession, "refreshSession");
|
|
129
|
+
|
|
130
|
+
// src/offline/sync/sync-root.ts
|
|
131
|
+
var SyncInterruptedError = class extends Error {
|
|
132
|
+
static {
|
|
133
|
+
__name(this, "SyncInterruptedError");
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
function assertStillOnline() {
|
|
137
|
+
if (!canReachApi()) throw new SyncInterruptedError("The connection dropped during the sync");
|
|
138
|
+
}
|
|
139
|
+
__name(assertStillOnline, "assertStillOnline");
|
|
140
|
+
var isSkippable = /* @__PURE__ */ __name((status) => status === 403 || status === 404, "isSkippable");
|
|
141
|
+
async function fetchResources(module, endpoint, skipped, included) {
|
|
142
|
+
const response = await JsonApiGet({ classKey: module, endpoint, language: currentLocale() });
|
|
143
|
+
assertStillOnline();
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
if (isSkippable(response.response)) {
|
|
146
|
+
skipped.push(endpoint);
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`[offline] GET ${endpoint} answered ${response.response}`);
|
|
150
|
+
}
|
|
151
|
+
included.push(...response.raw?.included ?? []);
|
|
152
|
+
const data = response.raw?.data;
|
|
153
|
+
if (!data) return [];
|
|
154
|
+
return Array.isArray(data) ? data : [data];
|
|
155
|
+
}
|
|
156
|
+
__name(fetchResources, "fetchResources");
|
|
157
|
+
async function imagesOfRoot(store, rootId) {
|
|
158
|
+
const images = [];
|
|
159
|
+
for (const typeConfig of getOfflineConfig().types) {
|
|
160
|
+
for (const record of await store.listRecords({ type: typeConfig.module.name, rootId })) {
|
|
161
|
+
const url = imageUrlOf(record.resource);
|
|
162
|
+
if (url) images.push({ type: record.type, id: record.id, url });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return images;
|
|
166
|
+
}
|
|
167
|
+
__name(imagesOfRoot, "imagesOfRoot");
|
|
168
|
+
async function removeRootIfIdle(store, rootId) {
|
|
169
|
+
if ((await store.queue()).some((entry) => entry.rootId === rootId)) return;
|
|
170
|
+
const images = await imagesOfRoot(store, rootId);
|
|
171
|
+
await store.removeRoot(rootId);
|
|
172
|
+
for (const image of images) {
|
|
173
|
+
if (!await store.getRecord(image.type, image.id)) await removeArtworkImage(image.url);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
__name(removeRootIfIdle, "removeRootIfIdle");
|
|
177
|
+
async function syncRoot(rootId) {
|
|
178
|
+
const config = getOfflineConfig();
|
|
179
|
+
const store = await getOfflineStore();
|
|
180
|
+
const previous = await store.getSyncState(rootId);
|
|
181
|
+
const syncEndpoint = new EndpointCreator({
|
|
182
|
+
endpoint: config.rootModule,
|
|
183
|
+
id: rootId,
|
|
184
|
+
childEndpoint: ROOT_SYNC_ENDPOINT
|
|
185
|
+
}).generate();
|
|
186
|
+
const syncResponse = await JsonApiGet({
|
|
187
|
+
classKey: Modules.RootSync,
|
|
188
|
+
endpoint: syncEndpoint,
|
|
189
|
+
language: currentLocale()
|
|
190
|
+
});
|
|
191
|
+
assertStillOnline();
|
|
192
|
+
if (!syncResponse.ok) {
|
|
193
|
+
if (isSkippable(syncResponse.response)) {
|
|
194
|
+
await removeRootIfIdle(store, rootId);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`[offline] GET ${syncEndpoint} answered ${syncResponse.response}`);
|
|
198
|
+
}
|
|
199
|
+
const sync = syncResponse.data;
|
|
200
|
+
if (sync.locked) {
|
|
201
|
+
await store.setSyncState(rootId, {
|
|
202
|
+
serverTime: previous.serverTime,
|
|
203
|
+
locked: true,
|
|
204
|
+
lockedFrom: sync.lockedFrom ?? null
|
|
205
|
+
});
|
|
206
|
+
await refreshLockedRoots();
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const typeNames = new Set(config.types.map((typeConfig) => typeConfig.module.name));
|
|
210
|
+
const entries = sync.entries.filter((entry) => typeNames.has(entry.type));
|
|
211
|
+
const entryUpdatedAt = new Map(entries.map((entry) => [recordKeyOf(entry.type, entry.id), entry.updatedAt]));
|
|
212
|
+
const since = previous.serverTime;
|
|
213
|
+
const inWindow = /* @__PURE__ */ __name((entry) => !since || entry.updatedAt.getTime() > since.getTime(), "inWindow");
|
|
214
|
+
const stored = /* @__PURE__ */ new Map();
|
|
215
|
+
for (const typeConfig of config.types) {
|
|
216
|
+
for (const record of await store.listRecords({ type: typeConfig.module.name, rootId })) {
|
|
217
|
+
stored.set(recordKeyOf(record.type, record.id), record);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const previouslySkipped = new Map(
|
|
221
|
+
(await store.skippedRecords(rootId)).map((record) => [recordKeyOf(record.type, record.id), record.updatedAt])
|
|
222
|
+
);
|
|
223
|
+
const isStale = /* @__PURE__ */ __name((entry) => {
|
|
224
|
+
const key = recordKeyOf(entry.type, entry.id);
|
|
225
|
+
const record = stored.get(key);
|
|
226
|
+
if (record && record.updatedAt !== null) return false;
|
|
227
|
+
const skippedAt = previouslySkipped.get(key);
|
|
228
|
+
return !skippedAt || entry.updatedAt.getTime() > skippedAt.getTime();
|
|
229
|
+
}, "isStale");
|
|
230
|
+
const changed = entries.filter((entry) => inWindow(entry) || isStale(entry));
|
|
231
|
+
const skipped = [];
|
|
232
|
+
const fetched = [];
|
|
233
|
+
const included = [];
|
|
234
|
+
for (const typeConfig of config.types) {
|
|
235
|
+
const changedOfType = changed.filter((entry) => entry.type === typeConfig.module.name);
|
|
236
|
+
if (changedOfType.length === 0) continue;
|
|
237
|
+
if (typeConfig.fetch === "root-list") {
|
|
238
|
+
const endpoint = new EndpointCreator({
|
|
239
|
+
endpoint: config.rootModule,
|
|
240
|
+
id: rootId,
|
|
241
|
+
childEndpoint: typeConfig.module
|
|
242
|
+
}).addAdditionalParam("fetchAll", "true");
|
|
243
|
+
if (since && changedOfType.every(inWindow)) endpoint.addAdditionalParam("updatedSince", since.toISOString());
|
|
244
|
+
fetched.push(...await fetchResources(typeConfig.module, endpoint.generate(), skipped, included));
|
|
245
|
+
} else if (typeConfig.fetch === "linked-list") {
|
|
246
|
+
if (!typeConfig.linkedList) throw new Error(`[offline] ${typeConfig.module.name}: linked-list needs linkedList`);
|
|
247
|
+
const { linkKind, endpoint } = typeConfig.linkedList;
|
|
248
|
+
const changedIds = new Set(changedOfType.map((entry) => entry.id));
|
|
249
|
+
const targets = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const link of sync.links) {
|
|
251
|
+
if (link.kind !== linkKind || link.fromType !== typeConfig.module.name || !changedIds.has(link.fromId))
|
|
252
|
+
continue;
|
|
253
|
+
targets.set(recordKeyOf(link.toType, link.toId), { type: link.toType, id: link.toId });
|
|
254
|
+
}
|
|
255
|
+
for (const target of targets.values()) {
|
|
256
|
+
const resources = await fetchResources(typeConfig.module, endpoint(rootId, target), skipped, included);
|
|
257
|
+
fetched.push(
|
|
258
|
+
...resources.filter((resource) => resource?.type === typeConfig.module.name && changedIds.has(resource.id))
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
for (const entry of changedOfType) {
|
|
263
|
+
const endpoint = new EndpointCreator({ endpoint: typeConfig.module, id: entry.id }).generate();
|
|
264
|
+
fetched.push(...await fetchResources(typeConfig.module, endpoint, skipped, included));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const fetchedKeys = new Set(fetched.map((resource) => recordKeyOf(resource?.type, resource?.id)));
|
|
269
|
+
const previousImageUrls = /* @__PURE__ */ new Map();
|
|
270
|
+
const removedImageUrls = [];
|
|
271
|
+
await store.transaction(async (tx) => {
|
|
272
|
+
const waiting = waitingRecordKeys(await tx.queue(), typeNames);
|
|
273
|
+
for (const typeConfig of config.types) {
|
|
274
|
+
for (const record of await tx.listRecords({ type: typeConfig.module.name, rootId })) {
|
|
275
|
+
const key = recordKeyOf(record.type, record.id);
|
|
276
|
+
if (waiting.has(key)) {
|
|
277
|
+
if (record.updatedAt !== null) await tx.putRecord({ ...record, updatedAt: null });
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (entryUpdatedAt.has(key)) continue;
|
|
281
|
+
await tx.removeRecordFromRoot(record.type, record.id, rootId);
|
|
282
|
+
const url = imageUrlOf(record.resource);
|
|
283
|
+
if (url && !await tx.getRecord(record.type, record.id)) removedImageUrls.push(url);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
for (const resource of fetched) {
|
|
287
|
+
const key = recordKeyOf(resource?.type, resource?.id);
|
|
288
|
+
if (!entryUpdatedAt.has(key)) continue;
|
|
289
|
+
if (waiting.has(key)) continue;
|
|
290
|
+
const existing = await tx.getRecord(resource.type, resource.id);
|
|
291
|
+
const previousUrl = imageUrlOf(existing?.resource);
|
|
292
|
+
if (previousUrl) previousImageUrls.set(key, previousUrl);
|
|
293
|
+
await tx.putRecord({
|
|
294
|
+
type: resource.type,
|
|
295
|
+
id: resource.id,
|
|
296
|
+
rootId,
|
|
297
|
+
resource,
|
|
298
|
+
updatedAt: entryUpdatedAt.get(key) ?? null,
|
|
299
|
+
base: null,
|
|
300
|
+
localOnly: false
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
for (const resource of included) {
|
|
304
|
+
if (!resource?.type || !resource?.id || typeNames.has(resource.type)) continue;
|
|
305
|
+
await tx.putRecord({
|
|
306
|
+
type: resource.type,
|
|
307
|
+
id: resource.id,
|
|
308
|
+
rootId,
|
|
309
|
+
resource,
|
|
310
|
+
updatedAt: null,
|
|
311
|
+
base: null,
|
|
312
|
+
localOnly: false
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
const skippedRecords = [];
|
|
316
|
+
const changedKeys = new Set(changed.map((entry) => recordKeyOf(entry.type, entry.id)));
|
|
317
|
+
for (const entry of entries) {
|
|
318
|
+
const key = recordKeyOf(entry.type, entry.id);
|
|
319
|
+
if (waiting.has(key) || fetchedKeys.has(key)) continue;
|
|
320
|
+
if (changedKeys.has(key)) skippedRecords.push({ type: entry.type, id: entry.id, updatedAt: entry.updatedAt });
|
|
321
|
+
else {
|
|
322
|
+
const skippedAt = previouslySkipped.get(key);
|
|
323
|
+
if (skippedAt) skippedRecords.push({ type: entry.type, id: entry.id, updatedAt: skippedAt });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
await tx.setSkippedRecords(rootId, skippedRecords);
|
|
327
|
+
await tx.replaceRootLinks(rootId, sync.links);
|
|
328
|
+
await tx.setSyncState(rootId, {
|
|
329
|
+
serverTime: sync.serverTime,
|
|
330
|
+
locked: false,
|
|
331
|
+
lockedFrom: sync.lockedFrom ?? null
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
if (skipped.length > 0) {
|
|
335
|
+
console.warn(`[offline] root record ${rootId}: skipped records the GM cannot read:`, skipped.join(", "));
|
|
336
|
+
}
|
|
337
|
+
for (const url of removedImageUrls) await removeArtworkImage(url);
|
|
338
|
+
for (const typeConfig of config.types) {
|
|
339
|
+
for (const record of await store.listRecords({ type: typeConfig.module.name, rootId })) {
|
|
340
|
+
const url = imageUrlOf(record.resource);
|
|
341
|
+
if (url) await cacheArtworkImage(url, previousImageUrls.get(recordKeyOf(record.type, record.id)));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
await refreshLockedRoots();
|
|
345
|
+
}
|
|
346
|
+
__name(syncRoot, "syncRoot");
|
|
347
|
+
async function syncAll() {
|
|
348
|
+
if (!isOfflineConfigured()) return;
|
|
349
|
+
const config = getOfflineConfig();
|
|
350
|
+
if (!config.getUserId() || !canReachApi()) return;
|
|
351
|
+
let roots;
|
|
352
|
+
try {
|
|
353
|
+
roots = await config.listSyncRoots();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.warn("[offline] could not list the root records to sync", error);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (!canReachApi()) return;
|
|
359
|
+
rememberSyncRoots(roots);
|
|
360
|
+
const total = roots.length;
|
|
361
|
+
setOfflineState({ syncProgress: { done: 0, total } });
|
|
362
|
+
try {
|
|
363
|
+
let done = 0;
|
|
364
|
+
for (const rootId of roots) {
|
|
365
|
+
try {
|
|
366
|
+
await syncRoot(rootId);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.warn(`[offline] sync of root record ${rootId} failed`, error);
|
|
369
|
+
if (!canReachApi()) return;
|
|
370
|
+
} finally {
|
|
371
|
+
done += 1;
|
|
372
|
+
setOfflineState({ syncProgress: { done, total } });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const finishedAt = Date.now();
|
|
376
|
+
const userId = config.getUserId();
|
|
377
|
+
if (userId) writeOfflineLastSync(userId, finishedAt);
|
|
378
|
+
setOfflineState({ lastSyncedAt: finishedAt, rootCount: total });
|
|
379
|
+
const store = await getOfflineStore();
|
|
380
|
+
const rootSet = new Set(roots);
|
|
381
|
+
for (const rootId of await knownRootIds(store)) {
|
|
382
|
+
if (!rootSet.has(rootId)) await removeRootIfIdle(store, rootId);
|
|
383
|
+
}
|
|
384
|
+
await refreshLockedRoots();
|
|
385
|
+
} finally {
|
|
386
|
+
setOfflineState({ syncProgress: null });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
__name(syncAll, "syncAll");
|
|
390
|
+
|
|
391
|
+
// src/offline/sync/counts.ts
|
|
392
|
+
import { useSyncExternalStore } from "react";
|
|
393
|
+
var EMPTY = [];
|
|
394
|
+
var _conflicts = EMPTY;
|
|
395
|
+
var _listeners = /* @__PURE__ */ new Set();
|
|
396
|
+
function subscribe(listener) {
|
|
397
|
+
_listeners.add(listener);
|
|
398
|
+
return () => {
|
|
399
|
+
_listeners.delete(listener);
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
__name(subscribe, "subscribe");
|
|
403
|
+
async function refreshOfflineCounts(store) {
|
|
404
|
+
if (!store) {
|
|
405
|
+
if (!isOfflineConfigured() || !getOfflineConfig().getUserId()) return;
|
|
406
|
+
store = await getOfflineStore();
|
|
407
|
+
}
|
|
408
|
+
const entries = await store.queue();
|
|
409
|
+
_conflicts = await store.conflicts();
|
|
410
|
+
for (const listener of [..._listeners]) listener();
|
|
411
|
+
setOfflineState({
|
|
412
|
+
pending: entries.filter((entry) => entry.status !== "conflict").length,
|
|
413
|
+
conflicts: _conflicts.length
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
__name(refreshOfflineCounts, "refreshOfflineCounts");
|
|
417
|
+
function useConflicts() {
|
|
418
|
+
return useSyncExternalStore(
|
|
419
|
+
subscribe,
|
|
420
|
+
() => _conflicts,
|
|
421
|
+
() => EMPTY
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
__name(useConflicts, "useConflicts");
|
|
425
|
+
|
|
426
|
+
// src/offline/sync/drain-queue.ts
|
|
427
|
+
var syncedTypeNames = /* @__PURE__ */ __name(() => new Set(getOfflineConfig().types.map((typeConfig) => typeConfig.module.name)), "syncedTypeNames");
|
|
428
|
+
function withoutServerOwnedFields(body) {
|
|
429
|
+
const data = body?.data;
|
|
430
|
+
if (!data) return body;
|
|
431
|
+
const owned = new Map(
|
|
432
|
+
getOfflineConfig().types.map((typeConfig) => [typeConfig.module.name, typeConfig.serverOwnedFields])
|
|
433
|
+
);
|
|
434
|
+
const strip = /* @__PURE__ */ __name((resource) => {
|
|
435
|
+
const fields = owned.get(resource?.type) ?? [];
|
|
436
|
+
if (!resource?.attributes || fields.length === 0) return resource;
|
|
437
|
+
const attributes = { ...resource.attributes };
|
|
438
|
+
for (const field of fields) delete attributes[field];
|
|
439
|
+
return { ...resource, attributes };
|
|
440
|
+
}, "strip");
|
|
441
|
+
return { ...body, data: Array.isArray(data) ? data.map(strip) : strip(data) };
|
|
442
|
+
}
|
|
443
|
+
__name(withoutServerOwnedFields, "withoutServerOwnedFields");
|
|
444
|
+
function toChange(entry) {
|
|
445
|
+
const change = { seq: entry.seq, method: entry.method, path: entry.path };
|
|
446
|
+
if (entry.body !== void 0) change.body = withoutServerOwnedFields(entry.body);
|
|
447
|
+
if (entry.recordType) change.recordType = entry.recordType;
|
|
448
|
+
if (entry.recordId) change.recordId = entry.recordId;
|
|
449
|
+
change.base = entry.base ?? null;
|
|
450
|
+
if (entry.resolution) change.resolution = entry.resolution;
|
|
451
|
+
return change;
|
|
452
|
+
}
|
|
453
|
+
__name(toChange, "toChange");
|
|
454
|
+
function mergedResource(current, merged) {
|
|
455
|
+
if (!merged || !current?.type) return void 0;
|
|
456
|
+
return { ...current, attributes: { ...current.attributes ?? {}, ...merged } };
|
|
457
|
+
}
|
|
458
|
+
__name(mergedResource, "mergedResource");
|
|
459
|
+
function newPackageId() {
|
|
460
|
+
return globalThis.crypto.randomUUID();
|
|
461
|
+
}
|
|
462
|
+
__name(newPackageId, "newPackageId");
|
|
463
|
+
function packageIdFor(entries) {
|
|
464
|
+
return entries.find((entry) => entry.packageId)?.packageId ?? newPackageId();
|
|
465
|
+
}
|
|
466
|
+
__name(packageIdFor, "packageIdFor");
|
|
467
|
+
async function entriesToSend(store) {
|
|
468
|
+
const all = await store.queue();
|
|
469
|
+
const typeNames = syncedTypeNames();
|
|
470
|
+
const blocked = waitingRecordKeys(
|
|
471
|
+
all.filter((entry) => entry.status === "conflict"),
|
|
472
|
+
typeNames
|
|
473
|
+
);
|
|
474
|
+
const lockedIds = new Set(getOfflineState().lockedRootIds);
|
|
475
|
+
const lockedByRoot = /* @__PURE__ */ new Map();
|
|
476
|
+
const now = /* @__PURE__ */ new Date();
|
|
477
|
+
const result = [];
|
|
478
|
+
for (const entry of all) {
|
|
479
|
+
if (entry.status !== "pending" && entry.status !== "sending") continue;
|
|
480
|
+
if (recordKeysOfEntry(entry, typeNames).some((key) => blocked.has(key))) continue;
|
|
481
|
+
if (!lockedByRoot.has(entry.rootId)) {
|
|
482
|
+
lockedByRoot.set(
|
|
483
|
+
entry.rootId,
|
|
484
|
+
lockedIds.has(entry.rootId) || isRootLocked(await store.getSyncState(entry.rootId), now)
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
if (lockedByRoot.get(entry.rootId)) continue;
|
|
488
|
+
result.push(entry);
|
|
489
|
+
}
|
|
490
|
+
return result.sort((a, b) => a.seq - b.seq);
|
|
491
|
+
}
|
|
492
|
+
__name(entriesToSend, "entriesToSend");
|
|
493
|
+
async function applyResult(store, entry, result) {
|
|
494
|
+
if (result.status === "applied") {
|
|
495
|
+
await store.removeQueueEntry(entry.seq);
|
|
496
|
+
const typeNames = syncedTypeNames();
|
|
497
|
+
const stillWaiting = waitingRecordKeys(await store.queue(), typeNames);
|
|
498
|
+
for (const key of recordKeysOfEntry(entry, typeNames)) {
|
|
499
|
+
if (stillWaiting.has(key)) continue;
|
|
500
|
+
const separator = key.indexOf(":");
|
|
501
|
+
const record = await store.getRecord(key.slice(0, separator), key.slice(separator + 1));
|
|
502
|
+
if (record && (record.base !== null || record.localOnly)) {
|
|
503
|
+
await store.putRecord({ ...record, base: null, localOnly: false });
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (result.response) {
|
|
507
|
+
await writeThrough(
|
|
508
|
+
{ method: entry.method, url: entry.path },
|
|
509
|
+
{ ok: true, status: 200, statusText: "OK", data: result.response }
|
|
510
|
+
);
|
|
511
|
+
} else if (entry.method === "DELETE") {
|
|
512
|
+
await writeThrough(
|
|
513
|
+
{ method: entry.method, url: entry.path },
|
|
514
|
+
{ ok: true, status: 204, statusText: "OK", data: void 0 }
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return "applied";
|
|
518
|
+
}
|
|
519
|
+
if (result.status === "conflict" || result.status === "could-not-apply") {
|
|
520
|
+
await store.addConflict({
|
|
521
|
+
queueSeq: entry.seq,
|
|
522
|
+
kind: result.status === "could-not-apply" ? "could-not-apply" : result.conflict ?? "fields",
|
|
523
|
+
recordType: entry.recordType,
|
|
524
|
+
recordId: entry.recordId,
|
|
525
|
+
mine: entry.body ?? null,
|
|
526
|
+
theirs: result.current ?? null,
|
|
527
|
+
base: entry.base ?? null,
|
|
528
|
+
merged: mergedResource(result.current, result.merged),
|
|
529
|
+
fields: result.conflictFields ?? [],
|
|
530
|
+
blocks: result.conflictBlocks,
|
|
531
|
+
reason: result.detail
|
|
532
|
+
});
|
|
533
|
+
await store.setQueueStatus(entry.seq, "conflict");
|
|
534
|
+
return "conflict";
|
|
535
|
+
}
|
|
536
|
+
await store.setQueueStatus(entry.seq, "pending");
|
|
537
|
+
return "pending";
|
|
538
|
+
}
|
|
539
|
+
__name(applyResult, "applyResult");
|
|
540
|
+
async function drain() {
|
|
541
|
+
const outcome = { applied: 0, conflicts: 0, stoppedForLogin: false };
|
|
542
|
+
if (!isOfflineConfigured() || !getOfflineConfig().getUserId()) return outcome;
|
|
543
|
+
const store = await getOfflineStore();
|
|
544
|
+
const entries = await entriesToSend(store);
|
|
545
|
+
if (entries.length === 0) return outcome;
|
|
546
|
+
const session = await refreshSession();
|
|
547
|
+
if (session === "unreachable") return outcome;
|
|
548
|
+
if (session === "expired") return { ...outcome, stoppedForLogin: true };
|
|
549
|
+
setOfflineState({ draining: true });
|
|
550
|
+
const handled = /* @__PURE__ */ new Set();
|
|
551
|
+
try {
|
|
552
|
+
const packageId = packageIdFor(entries);
|
|
553
|
+
await store.setQueuePackage(
|
|
554
|
+
entries.map((entry) => entry.seq),
|
|
555
|
+
packageId
|
|
556
|
+
);
|
|
557
|
+
for (const entry of entries) await store.setQueueStatus(entry.seq, "sending");
|
|
558
|
+
const endpoint = new EndpointCreator({ endpoint: Modules.OfflineChangeSet }).generate();
|
|
559
|
+
const release = allowWhileReconnecting(buildUrl(endpoint));
|
|
560
|
+
let response;
|
|
561
|
+
try {
|
|
562
|
+
response = await JsonApiPost({
|
|
563
|
+
classKey: Modules.OfflineChangeSet,
|
|
564
|
+
endpoint,
|
|
565
|
+
body: { id: packageId, changes: entries.map(toChange) },
|
|
566
|
+
language: currentLocale()
|
|
567
|
+
});
|
|
568
|
+
} finally {
|
|
569
|
+
release();
|
|
570
|
+
}
|
|
571
|
+
if (!response.ok) {
|
|
572
|
+
if (response.response === 401) outcome.stoppedForLogin = true;
|
|
573
|
+
return outcome;
|
|
574
|
+
}
|
|
575
|
+
await store.setQueuePackage(
|
|
576
|
+
entries.map((entry) => entry.seq),
|
|
577
|
+
null
|
|
578
|
+
);
|
|
579
|
+
const results = new Map(response.data.results.map((result) => [result.seq, result]));
|
|
580
|
+
for (const entry of entries) {
|
|
581
|
+
const result = results.get(entry.seq);
|
|
582
|
+
const status = result ? await applyResult(store, entry, result) : "pending";
|
|
583
|
+
if (!result) await store.setQueueStatus(entry.seq, "pending");
|
|
584
|
+
handled.add(entry.seq);
|
|
585
|
+
if (status === "applied") outcome.applied++;
|
|
586
|
+
if (status === "conflict") outcome.conflicts++;
|
|
587
|
+
}
|
|
588
|
+
return outcome;
|
|
589
|
+
} catch (error) {
|
|
590
|
+
console.error("[offline] sending the waiting changes failed", error);
|
|
591
|
+
return outcome;
|
|
592
|
+
} finally {
|
|
593
|
+
for (const entry of entries) {
|
|
594
|
+
if (!handled.has(entry.seq)) await store.setQueueStatus(entry.seq, "pending");
|
|
595
|
+
}
|
|
596
|
+
setOfflineState({ draining: false });
|
|
597
|
+
await refreshOfflineCounts(store);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
__name(drain, "drain");
|
|
601
|
+
var _inFlight;
|
|
602
|
+
function drainQueue() {
|
|
603
|
+
if (!_inFlight) {
|
|
604
|
+
_inFlight = drain().finally(() => {
|
|
605
|
+
_inFlight = void 0;
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
return _inFlight;
|
|
609
|
+
}
|
|
610
|
+
__name(drainQueue, "drainQueue");
|
|
611
|
+
|
|
612
|
+
// src/offline/sync/conflicts.ts
|
|
613
|
+
function updatedAtOf(resource) {
|
|
614
|
+
const value = resource?.attributes?.updatedAt;
|
|
615
|
+
if (!value) return null;
|
|
616
|
+
const date = new Date(value);
|
|
617
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
618
|
+
}
|
|
619
|
+
__name(updatedAtOf, "updatedAtOf");
|
|
620
|
+
async function applyChoicesToRecord(store, conflict, choices, mergedBody) {
|
|
621
|
+
if (!conflict.recordType || !conflict.recordId) return;
|
|
622
|
+
const record = await store.getRecord(conflict.recordType, conflict.recordId);
|
|
623
|
+
if (!record) return;
|
|
624
|
+
const attributes = { ...record.resource?.attributes ?? {} };
|
|
625
|
+
const relationships = { ...record.resource?.relationships ?? {} };
|
|
626
|
+
for (const [field, choice] of Object.entries(choices)) {
|
|
627
|
+
const source = choice === "theirs" ? conflict.theirs : choice === "merged" ? mergedBody?.data : void 0;
|
|
628
|
+
if (!source) continue;
|
|
629
|
+
if (source.attributes && field in source.attributes) attributes[field] = source.attributes[field];
|
|
630
|
+
else if (source.relationships && field in source.relationships) relationships[field] = source.relationships[field];
|
|
631
|
+
}
|
|
632
|
+
await store.putRecord({ ...record, resource: { ...record.resource, attributes, relationships } });
|
|
633
|
+
}
|
|
634
|
+
__name(applyChoicesToRecord, "applyChoicesToRecord");
|
|
635
|
+
async function rootIdOf(store, conflict, entry) {
|
|
636
|
+
if (entry) return entry.rootId;
|
|
637
|
+
if (conflict.recordType && conflict.recordId) {
|
|
638
|
+
const record = await store.getRecord(conflict.recordType, conflict.recordId);
|
|
639
|
+
if (record) return record.rootId;
|
|
640
|
+
}
|
|
641
|
+
throw new Error(`[offline] conflict ${conflict.id} belongs to no root record on this device`);
|
|
642
|
+
}
|
|
643
|
+
__name(rootIdOf, "rootIdOf");
|
|
644
|
+
function collectionPath(recordType) {
|
|
645
|
+
const module = getOfflineConfig().types.find((typeConfig) => typeConfig.module.name === recordType)?.module;
|
|
646
|
+
return new EndpointCreator({ endpoint: module ?? recordType }).generate();
|
|
647
|
+
}
|
|
648
|
+
__name(collectionPath, "collectionPath");
|
|
649
|
+
async function requeueOriginal(store, conflict, entry, fallbackMethod, resolution) {
|
|
650
|
+
const method = entry?.method ?? fallbackMethod;
|
|
651
|
+
const body = method === "DELETE" ? void 0 : entry?.body ?? conflict.mine ?? void 0;
|
|
652
|
+
await store.enqueue({
|
|
653
|
+
rootId: await rootIdOf(store, conflict, entry),
|
|
654
|
+
method,
|
|
655
|
+
path: entry?.path ?? new EndpointCreator({ endpoint: collectionPath(conflict.recordType), id: conflict.recordId }).generate(),
|
|
656
|
+
...body !== void 0 ? { body } : {},
|
|
657
|
+
recordType: entry?.recordType ?? conflict.recordType,
|
|
658
|
+
recordId: entry?.recordId ?? conflict.recordId,
|
|
659
|
+
base: entry ? entry.base : conflict.base ?? null,
|
|
660
|
+
// QueueEntry["resolution"] is the store's copy of OfflineChange["resolution"].
|
|
661
|
+
resolution
|
|
662
|
+
});
|
|
663
|
+
if (entry) await store.removeQueueEntry(entry.seq);
|
|
664
|
+
}
|
|
665
|
+
__name(requeueOriginal, "requeueOriginal");
|
|
666
|
+
async function resolve(store, conflict, choice) {
|
|
667
|
+
const entry = (await store.queue()).find((candidate) => candidate.seq === conflict.queueSeq);
|
|
668
|
+
const dropEntry = /* @__PURE__ */ __name(async () => {
|
|
669
|
+
if (entry) await store.removeQueueEntry(entry.seq);
|
|
670
|
+
}, "dropEntry");
|
|
671
|
+
switch (choice.kind) {
|
|
672
|
+
case "mine":
|
|
673
|
+
case "fields": {
|
|
674
|
+
if (choice.kind === "fields" && Object.values(choice.choices).every((value) => value === "theirs")) {
|
|
675
|
+
return resolve(store, conflict, { kind: "theirs" });
|
|
676
|
+
}
|
|
677
|
+
const resolution = choice.kind === "mine" ? { kind: "mine" } : {
|
|
678
|
+
kind: "merged",
|
|
679
|
+
choices: choice.choices,
|
|
680
|
+
...choice.mergedBody !== void 0 ? { mergedBody: choice.mergedBody } : {}
|
|
681
|
+
};
|
|
682
|
+
await requeueOriginal(store, conflict, entry, "PATCH", resolution);
|
|
683
|
+
if (choice.kind === "fields") await applyChoicesToRecord(store, conflict, choice.choices, choice.mergedBody);
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
case "theirs": {
|
|
687
|
+
await dropEntry();
|
|
688
|
+
if (conflict.recordType && conflict.recordId) {
|
|
689
|
+
if (conflict.theirs) {
|
|
690
|
+
await store.putRecord({
|
|
691
|
+
type: conflict.recordType,
|
|
692
|
+
id: conflict.recordId,
|
|
693
|
+
rootId: await rootIdOf(store, conflict, entry),
|
|
694
|
+
resource: conflict.theirs,
|
|
695
|
+
updatedAt: updatedAtOf(conflict.theirs),
|
|
696
|
+
base: null,
|
|
697
|
+
localOnly: false
|
|
698
|
+
});
|
|
699
|
+
} else {
|
|
700
|
+
await store.deleteRecord(conflict.recordType, conflict.recordId);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
case "restore": {
|
|
706
|
+
if (!conflict.recordType) throw new Error(`[offline] conflict ${conflict.id} has no record to restore`);
|
|
707
|
+
const body = conflict.mine ?? entry?.body;
|
|
708
|
+
const rootId = await rootIdOf(store, conflict, entry);
|
|
709
|
+
await store.enqueue({
|
|
710
|
+
rootId,
|
|
711
|
+
method: "POST",
|
|
712
|
+
path: collectionPath(conflict.recordType),
|
|
713
|
+
body,
|
|
714
|
+
recordType: conflict.recordType,
|
|
715
|
+
recordId: conflict.recordId,
|
|
716
|
+
base: null
|
|
717
|
+
});
|
|
718
|
+
await dropEntry();
|
|
719
|
+
if (conflict.recordId) {
|
|
720
|
+
const record = await store.getRecord(conflict.recordType, conflict.recordId);
|
|
721
|
+
if (record) await store.putRecord({ ...record, base: null, localOnly: true });
|
|
722
|
+
else if (body?.data)
|
|
723
|
+
await store.putRecord({
|
|
724
|
+
type: conflict.recordType,
|
|
725
|
+
id: conflict.recordId,
|
|
726
|
+
rootId,
|
|
727
|
+
resource: body.data,
|
|
728
|
+
updatedAt: null,
|
|
729
|
+
base: null,
|
|
730
|
+
localOnly: true
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
case "accept-deletion": {
|
|
736
|
+
await dropEntry();
|
|
737
|
+
if (conflict.recordType && conflict.recordId) await store.deleteRecord(conflict.recordType, conflict.recordId);
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
case "delete-anyway": {
|
|
741
|
+
await requeueOriginal(store, conflict, entry, "DELETE", { kind: "mine" });
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
case "retry": {
|
|
745
|
+
if (entry) await store.setQueueStatus(entry.seq, "pending");
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
case "discard": {
|
|
749
|
+
await dropEntry();
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
await store.removeConflict(conflict.id);
|
|
754
|
+
}
|
|
755
|
+
__name(resolve, "resolve");
|
|
756
|
+
async function resolveConflict(id, choice) {
|
|
757
|
+
const store = await getOfflineStore();
|
|
758
|
+
const conflict = (await store.conflicts()).find((candidate) => candidate.id === id);
|
|
759
|
+
if (!conflict) throw new Error(`[offline] no conflict ${id}`);
|
|
760
|
+
await store.transaction((tx) => resolve(tx, conflict, choice));
|
|
761
|
+
await refreshOfflineCounts(store);
|
|
762
|
+
if (getOfflineState().online) {
|
|
763
|
+
drainQueue().catch((error) => console.warn("[offline] sending the resolved change failed", error));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
__name(resolveConflict, "resolveConflict");
|
|
767
|
+
|
|
768
|
+
// src/offline/sync/runtime.ts
|
|
769
|
+
import { getCookie as getCookie2 } from "cookies-next";
|
|
770
|
+
var LOCK_CHECK_INTERVAL_MS = 6e4;
|
|
771
|
+
var PROBE_INTERVAL_MS = 3e4;
|
|
772
|
+
async function probeApi() {
|
|
773
|
+
const config = getOfflineConfig();
|
|
774
|
+
const store = await getOfflineStore();
|
|
775
|
+
const [rootId] = await knownRootIds(store);
|
|
776
|
+
const endpoint = rootId ? new EndpointCreator({ endpoint: config.rootModule, id: rootId, childEndpoint: ROOT_SYNC_ENDPOINT }) : new EndpointCreator({ endpoint: config.rootModule }).addAdditionalParam("page[size]", "1");
|
|
777
|
+
const response = await directFetch({
|
|
778
|
+
method: "GET",
|
|
779
|
+
url: buildUrl(endpoint.generate()),
|
|
780
|
+
token: await getClientToken(),
|
|
781
|
+
language: currentLocale(),
|
|
782
|
+
additionalHeaders: { [OFFLINE_REPLAY_HEADER]: "1" }
|
|
783
|
+
});
|
|
784
|
+
return !response.networkError;
|
|
785
|
+
}
|
|
786
|
+
__name(probeApi, "probeApi");
|
|
787
|
+
var LOGIN_RELOAD_KEY = "nja-offline-login-reload";
|
|
788
|
+
var _reloadedForSignIn;
|
|
789
|
+
async function signInFingerprint() {
|
|
790
|
+
const token = await getCookie2("token");
|
|
791
|
+
if (!token) return "none";
|
|
792
|
+
let hash = 5381;
|
|
793
|
+
for (const char of String(token)) hash = (hash * 33 ^ char.charCodeAt(0)) >>> 0;
|
|
794
|
+
return hash.toString(36);
|
|
795
|
+
}
|
|
796
|
+
__name(signInFingerprint, "signInFingerprint");
|
|
797
|
+
var readReloadMarker = /* @__PURE__ */ __name(() => {
|
|
798
|
+
try {
|
|
799
|
+
return window.sessionStorage.getItem(LOGIN_RELOAD_KEY);
|
|
800
|
+
} catch {
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
}, "readReloadMarker");
|
|
804
|
+
var writeReloadMarker = /* @__PURE__ */ __name((value) => {
|
|
805
|
+
try {
|
|
806
|
+
if (value === null) window.sessionStorage.removeItem(LOGIN_RELOAD_KEY);
|
|
807
|
+
else window.sessionStorage.setItem(LOGIN_RELOAD_KEY, value);
|
|
808
|
+
} catch {
|
|
809
|
+
}
|
|
810
|
+
}, "writeReloadMarker");
|
|
811
|
+
async function signInExpired() {
|
|
812
|
+
const signIn = await signInFingerprint();
|
|
813
|
+
if (_reloadedForSignIn === signIn || readReloadMarker() === signIn) {
|
|
814
|
+
console.warn("[offline] the sign-in expired; log in again to send the waiting changes");
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
_reloadedForSignIn = signIn;
|
|
818
|
+
writeReloadMarker(signIn);
|
|
819
|
+
try {
|
|
820
|
+
window.location.reload();
|
|
821
|
+
} catch (error) {
|
|
822
|
+
console.warn("[offline] the sign-in expired; log in again to send the waiting changes", error);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
__name(signInExpired, "signInExpired");
|
|
826
|
+
var warnUnlessUnavailable = /* @__PURE__ */ __name((message) => (error) => {
|
|
827
|
+
if (!(error instanceof OfflineUnavailableError)) console.warn(message, error);
|
|
828
|
+
}, "warnUnlessUnavailable");
|
|
829
|
+
var _runningRuntimes = /* @__PURE__ */ new Set();
|
|
830
|
+
function stopOfflineRuntime() {
|
|
831
|
+
for (const stop of [..._runningRuntimes]) stop();
|
|
832
|
+
}
|
|
833
|
+
__name(stopOfflineRuntime, "stopOfflineRuntime");
|
|
834
|
+
function startOfflineRuntime() {
|
|
835
|
+
refreshOfflineDeviceState();
|
|
836
|
+
if (!isOfflineConfigured()) return () => {
|
|
837
|
+
};
|
|
838
|
+
const config = getOfflineConfig();
|
|
839
|
+
let running;
|
|
840
|
+
let goingOnlineAfterSync = false;
|
|
841
|
+
const run = /* @__PURE__ */ __name((reconnect) => {
|
|
842
|
+
if (!running) {
|
|
843
|
+
running = (async () => {
|
|
844
|
+
let succeeded = false;
|
|
845
|
+
if (reconnect) setReconnecting(true);
|
|
846
|
+
try {
|
|
847
|
+
const drained = await drainQueue();
|
|
848
|
+
if (drained.stoppedForLogin) {
|
|
849
|
+
await signInExpired();
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
writeReloadMarker(null);
|
|
853
|
+
if (reconnect ? !isReconnecting() : !getOfflineState().online) return;
|
|
854
|
+
await syncAll();
|
|
855
|
+
succeeded = reconnect && isReconnecting();
|
|
856
|
+
} catch (error) {
|
|
857
|
+
warnUnlessUnavailable("[offline] background sync failed")(error);
|
|
858
|
+
} finally {
|
|
859
|
+
if (reconnect) setReconnecting(false);
|
|
860
|
+
running = void 0;
|
|
861
|
+
if (succeeded) {
|
|
862
|
+
goingOnlineAfterSync = true;
|
|
863
|
+
setOfflineState({ online: true });
|
|
864
|
+
goingOnlineAfterSync = false;
|
|
865
|
+
if (getOfflineState().pending > 0) {
|
|
866
|
+
drainQueue().catch(warnUnlessUnavailable("[offline] sending the waiting changes failed"));
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
})();
|
|
871
|
+
}
|
|
872
|
+
return running;
|
|
873
|
+
}, "run");
|
|
874
|
+
const runOnline = /* @__PURE__ */ __name(() => run(false), "runOnline");
|
|
875
|
+
let probing = false;
|
|
876
|
+
const probe = /* @__PURE__ */ __name(async () => {
|
|
877
|
+
if (probing || running || getOfflineState().online) return;
|
|
878
|
+
if (typeof navigator !== "undefined" && navigator.onLine === false) return;
|
|
879
|
+
probing = true;
|
|
880
|
+
try {
|
|
881
|
+
if (await probeApi()) await run(true);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
warnUnlessUnavailable("[offline] probing the API failed")(error);
|
|
884
|
+
} finally {
|
|
885
|
+
probing = false;
|
|
886
|
+
}
|
|
887
|
+
}, "probe");
|
|
888
|
+
const onBrowserOnline = /* @__PURE__ */ __name(() => void probe(), "onBrowserOnline");
|
|
889
|
+
if (typeof window !== "undefined") window.addEventListener("online", onBrowserOnline);
|
|
890
|
+
let wasOnline = getOfflineState().online;
|
|
891
|
+
const unsubscribe = subscribeOfflineState((state) => {
|
|
892
|
+
if (!state.available) {
|
|
893
|
+
stop();
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (state.online && !wasOnline && !goingOnlineAfterSync) void runOnline();
|
|
897
|
+
wasOnline = state.online;
|
|
898
|
+
});
|
|
899
|
+
void refreshLockedRoots().catch(warnUnlessUnavailable("[offline] lock check failed"));
|
|
900
|
+
void refreshOfflineCounts().catch(warnUnlessUnavailable("[offline] reading the queue failed"));
|
|
901
|
+
if (getOfflineState().online) void runOnline();
|
|
902
|
+
const syncTimer = setInterval(() => {
|
|
903
|
+
if (getOfflineState().online) void runOnline();
|
|
904
|
+
}, config.syncIntervalMs);
|
|
905
|
+
const probeTimer = setInterval(() => void probe(), PROBE_INTERVAL_MS);
|
|
906
|
+
const lockTimer = setInterval(() => {
|
|
907
|
+
void refreshLockedRoots().catch(warnUnlessUnavailable("[offline] lock check failed"));
|
|
908
|
+
}, LOCK_CHECK_INTERVAL_MS);
|
|
909
|
+
const stop = /* @__PURE__ */ __name(() => {
|
|
910
|
+
_runningRuntimes.delete(stop);
|
|
911
|
+
clearInterval(syncTimer);
|
|
912
|
+
clearInterval(probeTimer);
|
|
913
|
+
clearInterval(lockTimer);
|
|
914
|
+
if (typeof window !== "undefined") window.removeEventListener("online", onBrowserOnline);
|
|
915
|
+
unsubscribe();
|
|
916
|
+
}, "stop");
|
|
917
|
+
_runningRuntimes.add(stop);
|
|
918
|
+
return stop;
|
|
919
|
+
}
|
|
920
|
+
__name(startOfflineRuntime, "startOfflineRuntime");
|
|
921
|
+
|
|
922
|
+
// src/offline/logout.ts
|
|
923
|
+
async function prepareOfflineLogout() {
|
|
924
|
+
if (!isOfflineConfigured()) return { pending: 0 };
|
|
925
|
+
if (!getOfflineConfig().getUserId()) return { pending: 0 };
|
|
926
|
+
const store = await getOfflineStore();
|
|
927
|
+
const entries = await store.queue();
|
|
928
|
+
return { pending: entries.length };
|
|
929
|
+
}
|
|
930
|
+
__name(prepareOfflineLogout, "prepareOfflineLogout");
|
|
931
|
+
async function finishOfflineLogout(userId) {
|
|
932
|
+
stopOfflineRuntime();
|
|
933
|
+
blockOfflineStore(userId);
|
|
934
|
+
const { deleteDeviceStore: deleteDeviceStore2 } = await import("../lifecycle-LKGPRBS7.mjs");
|
|
935
|
+
await deleteDeviceStore2(userId);
|
|
936
|
+
await deleteImagesCaches();
|
|
937
|
+
setOfflineEnabledOnDevice(userId, false);
|
|
938
|
+
writeOfflineLastSync(userId, null);
|
|
939
|
+
setOfflineState({ enabled: false, syncProgress: null, lastSyncedAt: null, rootCount: null });
|
|
940
|
+
}
|
|
941
|
+
__name(finishOfflineLogout, "finishOfflineLogout");
|
|
942
|
+
|
|
943
|
+
// src/offline/device-switch.ts
|
|
944
|
+
function signedInUserId() {
|
|
945
|
+
const userId = getOfflineConfig().getUserId();
|
|
946
|
+
if (!userId) throw new Error("No signed-in user");
|
|
947
|
+
return userId;
|
|
948
|
+
}
|
|
949
|
+
__name(signedInUserId, "signedInUserId");
|
|
950
|
+
async function turnOnOffline() {
|
|
951
|
+
const userId = signedInUserId();
|
|
952
|
+
setOfflineEnabledOnDevice(userId, true);
|
|
953
|
+
setOfflineState({ enabled: true });
|
|
954
|
+
try {
|
|
955
|
+
await navigator.storage?.persist?.();
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
__name(turnOnOffline, "turnOnOffline");
|
|
960
|
+
async function turnOffOffline() {
|
|
961
|
+
const userId = signedInUserId();
|
|
962
|
+
if (isOfflineAvailable()) {
|
|
963
|
+
let pending = 0;
|
|
964
|
+
try {
|
|
965
|
+
const store = await getOfflineStore();
|
|
966
|
+
pending = (await store.queue()).length;
|
|
967
|
+
} catch {
|
|
968
|
+
const { hasUnsentChanges: hasUnsentChanges2 } = await import("../lifecycle-LKGPRBS7.mjs");
|
|
969
|
+
if (await hasUnsentChanges2(userId).catch(() => false)) pending = 1;
|
|
970
|
+
}
|
|
971
|
+
if (pending > 0) return { turnedOff: false, pending };
|
|
972
|
+
}
|
|
973
|
+
setOfflineEnabledOnDevice(userId, false);
|
|
974
|
+
if (isOfflineAvailable()) {
|
|
975
|
+
stopOfflineRuntime();
|
|
976
|
+
try {
|
|
977
|
+
const { deleteDeviceStore: deleteDeviceStore2 } = await import("../lifecycle-LKGPRBS7.mjs");
|
|
978
|
+
await deleteDeviceStore2(userId);
|
|
979
|
+
await deleteImagesCaches();
|
|
980
|
+
} catch (error) {
|
|
981
|
+
console.warn("[offline] the device copy could not be deleted", error);
|
|
982
|
+
}
|
|
983
|
+
forgetOfflineStore(userId);
|
|
984
|
+
}
|
|
985
|
+
writeOfflineLastSync(userId, null);
|
|
986
|
+
setOfflineState({ enabled: false, syncProgress: null, lastSyncedAt: null, rootCount: null });
|
|
987
|
+
return { turnedOff: true };
|
|
988
|
+
}
|
|
989
|
+
__name(turnOffOffline, "turnOffOffline");
|
|
990
|
+
export {
|
|
991
|
+
DEFAULT_DEVICE_WORKER_URL,
|
|
992
|
+
DEFAULT_SQLITE_MODULE_URL,
|
|
993
|
+
DEVICE_WORKER_FILE,
|
|
994
|
+
DeviceStoreUnavailableError,
|
|
995
|
+
NeedsConnectionError,
|
|
996
|
+
OFFLINE_REPLAY_HEADER,
|
|
997
|
+
OFFLINE_SQLITE_FILES,
|
|
998
|
+
OfflineUnavailableError,
|
|
999
|
+
SERVER_DEFAULT_ORDER_BY,
|
|
1000
|
+
configureOffline2 as configureOffline,
|
|
1001
|
+
deleteDeviceStore,
|
|
1002
|
+
drainQueue,
|
|
1003
|
+
errorAnswer,
|
|
1004
|
+
finishOfflineLogout,
|
|
1005
|
+
forgetOfflineStore,
|
|
1006
|
+
getOfflineConfig,
|
|
1007
|
+
getOfflineState,
|
|
1008
|
+
getOfflineStore,
|
|
1009
|
+
hasUnsentChanges,
|
|
1010
|
+
includedFor,
|
|
1011
|
+
isOfflineAvailable,
|
|
1012
|
+
isOfflineConfigured,
|
|
1013
|
+
isOfflineEnabledOnDevice,
|
|
1014
|
+
isRootLocked,
|
|
1015
|
+
linkKindFor,
|
|
1016
|
+
linkKindsOf,
|
|
1017
|
+
linksOfKind,
|
|
1018
|
+
listAnswer,
|
|
1019
|
+
localTransport,
|
|
1020
|
+
matchPattern,
|
|
1021
|
+
matchRoute,
|
|
1022
|
+
mergeResource,
|
|
1023
|
+
needsConnectionAnswer,
|
|
1024
|
+
notFoundAnswer,
|
|
1025
|
+
onUserSignedIn,
|
|
1026
|
+
openDeviceStore,
|
|
1027
|
+
openMemoryDeviceStore,
|
|
1028
|
+
orderRecords,
|
|
1029
|
+
orderedListAnswer,
|
|
1030
|
+
patternToString,
|
|
1031
|
+
prepareOfflineLogout,
|
|
1032
|
+
refreshOfflineDeviceState,
|
|
1033
|
+
resolveConflict,
|
|
1034
|
+
resourceFromDocument,
|
|
1035
|
+
resourceListAnswer,
|
|
1036
|
+
searchRecords,
|
|
1037
|
+
setDeviceStoreOpener,
|
|
1038
|
+
setOfflineState,
|
|
1039
|
+
singleAnswer,
|
|
1040
|
+
startOfflineRuntime,
|
|
1041
|
+
stopOfflineRuntime,
|
|
1042
|
+
subscribeOfflineState,
|
|
1043
|
+
syncAll,
|
|
1044
|
+
syncRoot,
|
|
1045
|
+
turnOffOffline,
|
|
1046
|
+
turnOnOffline,
|
|
1047
|
+
useConflicts,
|
|
1048
|
+
useNeedsConnection,
|
|
1049
|
+
useOfflineState,
|
|
1050
|
+
writeThrough
|
|
1051
|
+
};
|
|
1052
|
+
//# sourceMappingURL=index.mjs.map
|