@carlonicora/nextjs-jsonapi 6.12.2 → 6.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/dist/{ApiData-DPKNfY-9.d.mts → ApiData-Be8yxCOX.d.mts} +2 -0
  2. package/dist/{ApiData-DPKNfY-9.d.ts → ApiData-Be8yxCOX.d.ts} +2 -0
  3. package/dist/{BlockNoteEditor-7M5QW2YQ.js → BlockNoteEditor-UBVEUFNR.js} +34 -22
  4. package/dist/BlockNoteEditor-UBVEUFNR.js.map +1 -0
  5. package/dist/{BlockNoteEditor-75ED2CN2.mjs → BlockNoteEditor-VXMDSQAA.mjs} +20 -8
  6. package/dist/BlockNoteEditor-VXMDSQAA.mjs.map +1 -0
  7. package/dist/{BlockNoteViewer-WGCDB7IJ.js → BlockNoteViewer-2XYASA5Y.js} +8 -5
  8. package/dist/BlockNoteViewer-2XYASA5Y.js.map +1 -0
  9. package/dist/{BlockNoteViewer-MTGS6YJV.mjs → BlockNoteViewer-NBW2H6LC.mjs} +7 -4
  10. package/dist/{BlockNoteViewer-MTGS6YJV.mjs.map → BlockNoteViewer-NBW2H6LC.mjs.map} +1 -1
  11. package/dist/{JsonApiRequest-A66XK5WT.mjs → JsonApiRequest-F6TMJ42G.mjs} +2 -2
  12. package/dist/JsonApiRequest-WISJZSDE.js +26 -0
  13. package/dist/{JsonApiRequest-FZGPLNUF.js.map → JsonApiRequest-WISJZSDE.js.map} +1 -1
  14. package/dist/billing/index.js +499 -494
  15. package/dist/billing/index.js.map +1 -1
  16. package/dist/billing/index.mjs +13 -8
  17. package/dist/billing/index.mjs.map +1 -1
  18. package/dist/chunk-27LQDGNP.mjs +317 -0
  19. package/dist/chunk-27LQDGNP.mjs.map +1 -0
  20. package/dist/{chunk-KSYQEKJX.js → chunk-2SV4SS6N.js} +14 -14
  21. package/dist/{chunk-KSYQEKJX.js.map → chunk-2SV4SS6N.js.map} +1 -1
  22. package/dist/{chunk-4FNGWALA.mjs → chunk-47BBY5KA.mjs} +6 -4
  23. package/dist/{chunk-4FNGWALA.mjs.map → chunk-47BBY5KA.mjs.map} +1 -1
  24. package/dist/chunk-6JUCIWDC.mjs +570 -0
  25. package/dist/chunk-6JUCIWDC.mjs.map +1 -0
  26. package/dist/chunk-7DBJTNSU.mjs +1049 -0
  27. package/dist/chunk-7DBJTNSU.mjs.map +1 -0
  28. package/dist/chunk-7JZHPMGW.mjs +704 -0
  29. package/dist/chunk-7JZHPMGW.mjs.map +1 -0
  30. package/dist/{chunk-L6PYFQTO.js → chunk-7ZHTPFFT.js} +16 -14
  31. package/dist/chunk-7ZHTPFFT.js.map +1 -0
  32. package/dist/chunk-B6JGKDP2.js +214 -0
  33. package/dist/chunk-B6JGKDP2.js.map +1 -0
  34. package/dist/{chunk-3YW35YHQ.mjs → chunk-BH6JWIKI.mjs} +5 -5
  35. package/dist/chunk-BZ5J2FIV.js +570 -0
  36. package/dist/chunk-BZ5J2FIV.js.map +1 -0
  37. package/dist/{chunk-V5MI7RNB.mjs → chunk-DGWOVYEC.mjs} +2 -2
  38. package/dist/chunk-DOCIFE6A.mjs +212 -0
  39. package/dist/chunk-DOCIFE6A.mjs.map +1 -0
  40. package/dist/chunk-GB6DBFYY.js +25 -0
  41. package/dist/chunk-GB6DBFYY.js.map +1 -0
  42. package/dist/{chunk-EWCY6QJA.mjs → chunk-JJMMT24D.mjs} +37 -3
  43. package/dist/chunk-JJMMT24D.mjs.map +1 -0
  44. package/dist/{chunk-JTPRJNT2.js → chunk-KMW7UNL2.js} +38 -3
  45. package/dist/chunk-KMW7UNL2.js.map +1 -0
  46. package/dist/chunk-MHB36MYE.js +317 -0
  47. package/dist/chunk-MHB36MYE.js.map +1 -0
  48. package/dist/{chunk-FF4EIQ6H.js → chunk-MOGEGWXW.js} +1039 -1680
  49. package/dist/chunk-MOGEGWXW.js.map +1 -0
  50. package/dist/{chunk-RXQYEEKZ.mjs → chunk-N4RZZKL5.mjs} +58 -25
  51. package/dist/chunk-N4RZZKL5.mjs.map +1 -0
  52. package/dist/{chunk-AA52ORW2.mjs → chunk-P445QGOC.mjs} +61 -702
  53. package/dist/chunk-P445QGOC.mjs.map +1 -0
  54. package/dist/chunk-PV4Z5W6V.js +212 -0
  55. package/dist/chunk-PV4Z5W6V.js.map +1 -0
  56. package/dist/chunk-PXDUJ47N.js +1049 -0
  57. package/dist/chunk-PXDUJ47N.js.map +1 -0
  58. package/dist/{chunk-V3YDVJ63.js → chunk-PYRZCKC2.js} +1209 -1176
  59. package/dist/chunk-PYRZCKC2.js.map +1 -0
  60. package/dist/{chunk-GHE4VT2R.js → chunk-QKNGZDSQ.js} +2 -2
  61. package/dist/{chunk-GHE4VT2R.js.map → chunk-QKNGZDSQ.js.map} +1 -1
  62. package/dist/chunk-W62MHDWM.js +704 -0
  63. package/dist/chunk-W62MHDWM.js.map +1 -0
  64. package/dist/chunk-XXP3TQHF.mjs +25 -0
  65. package/dist/chunk-XXP3TQHF.mjs.map +1 -0
  66. package/dist/chunk-YRKIFYOE.mjs +214 -0
  67. package/dist/chunk-YRKIFYOE.mjs.map +1 -0
  68. package/dist/{chunk-O3PDL2CV.js → chunk-Z4POQWFG.js} +40 -6
  69. package/dist/chunk-Z4POQWFG.js.map +1 -0
  70. package/dist/{chunk-PI56A4DG.mjs → chunk-ZLTOUZN2.mjs} +37 -2
  71. package/dist/chunk-ZLTOUZN2.mjs.map +1 -0
  72. package/dist/client/index.d.mts +2 -2
  73. package/dist/client/index.d.ts +2 -2
  74. package/dist/client/index.js +14 -9
  75. package/dist/client/index.js.map +1 -1
  76. package/dist/client/index.mjs +24 -19
  77. package/dist/components/index.d.mts +7 -1
  78. package/dist/components/index.d.ts +7 -1
  79. package/dist/components/index.js +11 -7
  80. package/dist/components/index.js.map +1 -1
  81. package/dist/components/index.mjs +10 -6
  82. package/dist/contexts/index.d.mts +1 -1
  83. package/dist/contexts/index.d.ts +1 -1
  84. package/dist/contexts/index.js +11 -7
  85. package/dist/contexts/index.js.map +1 -1
  86. package/dist/contexts/index.mjs +10 -6
  87. package/dist/core/index.d.mts +128 -2
  88. package/dist/core/index.d.ts +128 -2
  89. package/dist/core/index.js +17 -4
  90. package/dist/core/index.js.map +1 -1
  91. package/dist/core/index.mjs +22 -9
  92. package/dist/email-automations/index.js +101 -96
  93. package/dist/email-automations/index.js.map +1 -1
  94. package/dist/email-automations/index.mjs +13 -8
  95. package/dist/email-automations/index.mjs.map +1 -1
  96. package/dist/features/analytics/index.js +128 -123
  97. package/dist/features/analytics/index.js.map +1 -1
  98. package/dist/features/analytics/index.mjs +13 -8
  99. package/dist/features/analytics/index.mjs.map +1 -1
  100. package/dist/features/analytics/server.js +11 -8
  101. package/dist/features/analytics/server.js.map +1 -1
  102. package/dist/features/analytics/server.mjs +9 -6
  103. package/dist/features/analytics/server.mjs.map +1 -1
  104. package/dist/features/blog/index.js +158 -153
  105. package/dist/features/blog/index.js.map +1 -1
  106. package/dist/features/blog/index.mjs +14 -9
  107. package/dist/features/blog/index.mjs.map +1 -1
  108. package/dist/features/blog/server.js +30 -26
  109. package/dist/features/blog/server.js.map +1 -1
  110. package/dist/features/blog/server.mjs +10 -6
  111. package/dist/features/blog/server.mjs.map +1 -1
  112. package/dist/features/help/index.js +44 -40
  113. package/dist/features/help/index.js.map +1 -1
  114. package/dist/features/help/index.mjs +10 -6
  115. package/dist/features/help/index.mjs.map +1 -1
  116. package/dist/features/tokenusage/index.js +134 -129
  117. package/dist/features/tokenusage/index.js.map +1 -1
  118. package/dist/features/tokenusage/index.mjs +13 -8
  119. package/dist/features/tokenusage/index.mjs.map +1 -1
  120. package/dist/index.d.mts +2 -2
  121. package/dist/index.d.ts +2 -2
  122. package/dist/index.js +18 -5
  123. package/dist/index.js.map +1 -1
  124. package/dist/index.mjs +24 -11
  125. package/dist/lifecycle-4GUOTB46.js +23 -0
  126. package/dist/lifecycle-4GUOTB46.js.map +1 -0
  127. package/dist/lifecycle-LKGPRBS7.mjs +23 -0
  128. package/dist/local-transport-7H3SPZSA.js +15 -0
  129. package/dist/local-transport-7H3SPZSA.js.map +1 -0
  130. package/dist/local-transport-QYFKA6AN.mjs +15 -0
  131. package/dist/local-transport-QYFKA6AN.mjs.map +1 -0
  132. package/dist/offline/index.d.mts +607 -0
  133. package/dist/offline/index.d.ts +607 -0
  134. package/dist/offline/index.js +1052 -0
  135. package/dist/offline/index.js.map +1 -0
  136. package/dist/offline/index.mjs +1052 -0
  137. package/dist/offline/index.mjs.map +1 -0
  138. package/dist/offline/sqlite/device-worker.mjs +62 -0
  139. package/dist/offline/sqlite/index.mjs +17392 -0
  140. package/dist/offline/sqlite/sqlite3-opfs-async-proxy.js +881 -0
  141. package/dist/offline/sqlite/sqlite3.wasm +0 -0
  142. package/dist/request-KTBV6HHH.js +10 -0
  143. package/dist/request-KTBV6HHH.js.map +1 -0
  144. package/dist/request-SQOUNWZ4.mjs +10 -0
  145. package/dist/request-SQOUNWZ4.mjs.map +1 -0
  146. package/dist/server/cachedGet.d.mts +1 -1
  147. package/dist/server/cachedGet.d.ts +1 -1
  148. package/dist/server/cachedGet.stub.d.mts +1 -1
  149. package/dist/server/cachedGet.stub.d.ts +1 -1
  150. package/dist/server/index.d.mts +28 -2
  151. package/dist/server/index.d.ts +28 -2
  152. package/dist/server/index.js +18 -8
  153. package/dist/server/index.js.map +1 -1
  154. package/dist/server/index.mjs +18 -8
  155. package/dist/{useSocket-IRHtk2xc.d.ts → useSocket-Bo9M0t0J.d.ts} +3 -1
  156. package/dist/{useSocket-C8fqU4od.d.mts → useSocket-J5qanQPN.d.mts} +3 -1
  157. package/dist/write-through-FDQ7N4YI.js +15 -0
  158. package/dist/write-through-FDQ7N4YI.js.map +1 -0
  159. package/dist/write-through-R25VQMH6.mjs +15 -0
  160. package/dist/write-through-R25VQMH6.mjs.map +1 -0
  161. package/package.json +7 -1
  162. package/pwa/sw.js +277 -26
  163. package/src/client/request.ts +37 -0
  164. package/src/components/editors/BlockNoteEditor.tsx +14 -0
  165. package/src/components/editors/__tests__/BlockNoteEditor.empty-change.spec.tsx +205 -0
  166. package/src/contexts/SocketContext.tsx +7 -1
  167. package/src/contexts/__tests__/SocketContext.session.spec.tsx +52 -5
  168. package/src/core/factories/RehydrationFactory.ts +28 -8
  169. package/src/core/factories/__tests__/RehydrationFactory.spec.ts +33 -0
  170. package/src/core/index.ts +3 -0
  171. package/src/core/interfaces/ApiData.ts +2 -0
  172. package/src/core/registry/ModuleRegistry.ts +3 -0
  173. package/src/features/auth/components/forms/Logout.tsx +8 -1
  174. package/src/features/auth/components/forms/__tests__/Logout.spec.tsx +62 -0
  175. package/src/features/onboarding/contexts/OnboardingContext.spec.tsx +42 -1
  176. package/src/features/onboarding/contexts/OnboardingContext.tsx +11 -1
  177. package/src/hooks/__tests__/useSocket.pause.spec.ts +39 -0
  178. package/src/hooks/useSocket.ts +20 -1
  179. package/src/offline/__tests__/answer-builders.spec.ts +176 -0
  180. package/src/offline/__tests__/device-switch.spec.ts +179 -0
  181. package/src/offline/__tests__/generic-answers.spec.ts +380 -0
  182. package/src/offline/__tests__/local-transport.spec.ts +150 -0
  183. package/src/offline/__tests__/logout.spec.ts +122 -0
  184. package/src/offline/__tests__/offline-fixtures.ts +89 -0
  185. package/src/offline/__tests__/router.spec.ts +56 -0
  186. package/src/offline/__tests__/state.spec.ts +62 -0
  187. package/src/offline/__tests__/write-through.spec.ts +137 -0
  188. package/src/offline/config.ts +229 -0
  189. package/src/offline/configure.ts +14 -0
  190. package/src/offline/device-switch.ts +75 -0
  191. package/src/offline/generic-answers.ts +706 -0
  192. package/src/offline/index.ts +67 -0
  193. package/src/offline/local-transport.ts +189 -0
  194. package/src/offline/logout.ts +41 -0
  195. package/src/offline/router.ts +41 -0
  196. package/src/offline/server/__tests__/build-offline-manifest.spec.ts +76 -0
  197. package/src/offline/server/build-offline-manifest.ts +49 -0
  198. package/src/offline/state-hooks.ts +10 -0
  199. package/src/offline/state.ts +143 -0
  200. package/src/offline/store/DeviceStore.ts +112 -0
  201. package/src/offline/store/__tests__/device-store-lifecycle.spec.ts +343 -0
  202. package/src/offline/store/__tests__/shared-database.spec.ts +151 -0
  203. package/src/offline/store/__tests__/sql-device-store.spec.ts +399 -0
  204. package/src/offline/store/lifecycle.ts +315 -0
  205. package/src/offline/store/schema.ts +101 -0
  206. package/src/offline/store/shared-database.ts +321 -0
  207. package/src/offline/store/sql-device-store.ts +469 -0
  208. package/src/offline/store/worker-client.ts +71 -0
  209. package/src/offline/store/worker.ts +87 -0
  210. package/src/offline/sync/__tests__/conflicts.spec.ts +185 -0
  211. package/src/offline/sync/__tests__/queue-drain.spec.ts +364 -0
  212. package/src/offline/sync/__tests__/runtime.spec.ts +337 -0
  213. package/src/offline/sync/__tests__/sync-fixtures.ts +103 -0
  214. package/src/offline/sync/__tests__/sync-root.spec.ts +727 -0
  215. package/src/offline/sync/artwork-cache.ts +67 -0
  216. package/src/offline/sync/conflicts.ts +211 -0
  217. package/src/offline/sync/counts.ts +39 -0
  218. package/src/offline/sync/drain-queue.ts +246 -0
  219. package/src/offline/sync/known-roots.ts +37 -0
  220. package/src/offline/sync/models/OfflineChangeSet.ts +34 -0
  221. package/src/offline/sync/models/OfflineChangeSetInterface.ts +54 -0
  222. package/src/offline/sync/models/RootSync.ts +68 -0
  223. package/src/offline/sync/models/RootSyncInterface.ts +20 -0
  224. package/src/offline/sync/models/__tests__/OfflineChangeSet.spec.ts +47 -0
  225. package/src/offline/sync/models/__tests__/RootSync.spec.ts +54 -0
  226. package/src/offline/sync/models/index.ts +5 -0
  227. package/src/offline/sync/models/offline.modules.ts +14 -0
  228. package/src/offline/sync/runtime.ts +226 -0
  229. package/src/offline/sync/session-refresh.ts +32 -0
  230. package/src/offline/sync/sync-root.ts +336 -0
  231. package/src/offline/sync/waiting.ts +40 -0
  232. package/src/offline/types.ts +115 -0
  233. package/src/offline/write-through.ts +189 -0
  234. package/src/pwa/__tests__/sw.spec.ts +358 -1
  235. package/src/server/index.ts +5 -0
  236. package/src/unified/__tests__/JsonApiRequest.offline.spec.ts +243 -0
  237. package/dist/BlockNoteEditor-75ED2CN2.mjs.map +0 -1
  238. package/dist/BlockNoteEditor-7M5QW2YQ.js.map +0 -1
  239. package/dist/BlockNoteViewer-WGCDB7IJ.js.map +0 -1
  240. package/dist/JsonApiRequest-FZGPLNUF.js +0 -26
  241. package/dist/chunk-AA52ORW2.mjs.map +0 -1
  242. package/dist/chunk-EWCY6QJA.mjs.map +0 -1
  243. package/dist/chunk-FF4EIQ6H.js.map +0 -1
  244. package/dist/chunk-JTPRJNT2.js.map +0 -1
  245. package/dist/chunk-L6PYFQTO.js.map +0 -1
  246. package/dist/chunk-O3PDL2CV.js.map +0 -1
  247. package/dist/chunk-PI56A4DG.mjs.map +0 -1
  248. package/dist/chunk-RXQYEEKZ.mjs.map +0 -1
  249. package/dist/chunk-V3YDVJ63.js.map +0 -1
  250. package/dist/request-5RLK2AM3.mjs +0 -9
  251. package/dist/request-EUGTI3BE.js +0 -9
  252. package/dist/request-EUGTI3BE.js.map +0 -1
  253. /package/dist/{JsonApiRequest-A66XK5WT.mjs.map → JsonApiRequest-F6TMJ42G.mjs.map} +0 -0
  254. /package/dist/{chunk-3YW35YHQ.mjs.map → chunk-BH6JWIKI.mjs.map} +0 -0
  255. /package/dist/{chunk-V5MI7RNB.mjs.map → chunk-DGWOVYEC.mjs.map} +0 -0
  256. /package/dist/{request-5RLK2AM3.mjs.map → lifecycle-LKGPRBS7.mjs.map} +0 -0
@@ -0,0 +1,607 @@
1
+ type GetterKeys<T> = {
2
+ [K in keyof T]: T[K] extends () => any ? never : K;
3
+ }[keyof T];
4
+ type FieldSelector<T> = {
5
+ type: string;
6
+ fields: ReadonlyArray<GetterKeys<T>>;
7
+ };
8
+
9
+ type ApiRequestDataTypeInterface = {
10
+ name: string;
11
+ cache?: string;
12
+ inclusions?: Record<string, {
13
+ types?: string[];
14
+ fields?: FieldSelector<any>[];
15
+ include?: string[];
16
+ }>;
17
+ model: new () => any;
18
+ };
19
+
20
+ type OfflineChangeResolution = {
21
+ kind: "mine" | "merged";
22
+ /** Per conflicting field: whose value wins. */
23
+ choices?: Record<string, "mine" | "theirs" | "merged">;
24
+ /** The model-built JSON:API document holding only the fields chosen "merged", with their hand-merged values. */
25
+ mergedBody?: Record<string, unknown>;
26
+ };
27
+
28
+ type StoredRecord = {
29
+ type: string;
30
+ id: string;
31
+ rootId: string;
32
+ resource: any;
33
+ updatedAt: Date | null;
34
+ base: any | null;
35
+ localOnly: boolean;
36
+ };
37
+ type StoredLink = {
38
+ fromType: string;
39
+ fromId: string;
40
+ toType: string;
41
+ toId: string;
42
+ kind: string;
43
+ props: Record<string, unknown>;
44
+ };
45
+ type QueueStatus = "pending" | "sending" | "conflict" | "failed";
46
+ /** The `resolution` of a server-side `OfflineChange` (C1b): kind, per-field choices, merged body. */
47
+ type QueueResolution = OfflineChangeResolution;
48
+ type QueueEntry = {
49
+ seq: number;
50
+ rootId: string;
51
+ method: OfflineMethod;
52
+ path: string;
53
+ body?: any;
54
+ recordType?: string;
55
+ recordId?: string;
56
+ base: any | null;
57
+ resolution?: QueueResolution;
58
+ createdAt: Date;
59
+ status: QueueStatus;
60
+ /**
61
+ * The id of the package (`offline-change-sets`) the entry was last sent in, kept until the
62
+ * server answers for it: a package that never got its answer is sent again with the same id,
63
+ * so the server applies it once.
64
+ */
65
+ packageId?: string;
66
+ };
67
+ type ConflictKind = "fields" | "deleted-on-server" | "changed-on-server" | "could-not-apply";
68
+ type Conflict = {
69
+ id: string;
70
+ queueSeq: number;
71
+ kind: ConflictKind;
72
+ recordType?: string;
73
+ recordId?: string;
74
+ mine: any;
75
+ theirs: any;
76
+ base: any;
77
+ /** The merge the server settled, as a resource: its current resource with the merged attributes. */
78
+ merged?: any;
79
+ fields: string[];
80
+ blocks?: Record<string, string[]>;
81
+ reason?: string;
82
+ };
83
+ type SyncState = {
84
+ serverTime: Date | null;
85
+ locked: boolean;
86
+ lockedFrom: Date | null;
87
+ };
88
+ /** A record a sync could not read (403/404, or absent from the answer), with its `updatedAt` then. */
89
+ type SkippedRecord = {
90
+ type: string;
91
+ id: string;
92
+ updatedAt: Date;
93
+ };
94
+ interface DeviceStore {
95
+ getRecord(type: string, id: string): Promise<StoredRecord | undefined>;
96
+ /** With `rootId`: every record that root record uses, shared records included. */
97
+ listRecords(filter: {
98
+ type: string;
99
+ rootId?: string;
100
+ ids?: string[];
101
+ }): Promise<StoredRecord[]>;
102
+ putRecord(record: Omit<StoredRecord, "base" | "localOnly"> & {
103
+ base?: any | null;
104
+ localOnly?: boolean;
105
+ }): Promise<void>;
106
+ /** Removes the record (every root record's copy) and every link from or to it. */
107
+ deleteRecord(type: string, id: string): Promise<void>;
108
+ /**
109
+ * The record leaves `rootId`. A shared record (an artwork, a template) that another
110
+ * root record still uses is kept for that root record; it is deleted only when no root record keeps it.
111
+ */
112
+ removeRecordFromRoot(type: string, id: string, rootId: string): Promise<void>;
113
+ linksFrom(fromId: string, kind?: string): Promise<StoredLink[]>;
114
+ linksTo(toId: string, kind?: string): Promise<StoredLink[]>;
115
+ replaceRootLinks(rootId: string, links: StoredLink[]): Promise<void>;
116
+ putLink(link: StoredLink): Promise<void>;
117
+ deleteLink(link: Pick<StoredLink, "fromId" | "toId" | "kind">): Promise<void>;
118
+ enqueue(entry: Omit<QueueEntry, "seq" | "createdAt" | "status">): Promise<number>;
119
+ queue(filter?: {
120
+ status?: QueueStatus;
121
+ }): Promise<QueueEntry[]>;
122
+ setQueueStatus(seq: number, status: QueueStatus): Promise<void>;
123
+ /** Stores (or with `null` clears) the package id of the given entries. */
124
+ setQueuePackage(seqs: number[], packageId: string | null): Promise<void>;
125
+ removeQueueEntry(seq: number): Promise<void>;
126
+ addConflict(c: Omit<Conflict, "id">): Promise<string>;
127
+ conflicts(): Promise<Conflict[]>;
128
+ removeConflict(id: string): Promise<void>;
129
+ getSyncState(rootId: string): Promise<SyncState>;
130
+ /** The store writes ISO strings and parses them back with `new Date`. */
131
+ setSyncState(rootId: string, s: SyncState): Promise<void>;
132
+ /** The records the last sync of `rootId` skipped: asked for again only once they change. */
133
+ skippedRecords(rootId: string): Promise<SkippedRecord[]>;
134
+ /** Replaces the skipped records of `rootId`. */
135
+ setSkippedRecords(rootId: string, records: SkippedRecord[]): Promise<void>;
136
+ /**
137
+ * Records, links, queue, conflicts, sync_state, skipped records — kept only when its queue is empty. A shared
138
+ * record another root record still uses is kept for that root record.
139
+ */
140
+ removeRoot(rootId: string): Promise<void>;
141
+ transaction<T>(fn: (store: DeviceStore) => Promise<T>): Promise<T>;
142
+ }
143
+
144
+ /** Header of a request the server runs as an offline replay. Never answered locally. */
145
+ declare const OFFLINE_REPLAY_HEADER = "x-offline-replay";
146
+ type OfflineMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
147
+ /** A Module (matched by its endpoint), a ":param" capture, or a literal segment ("playing", "merge"). */
148
+ type PatternSegment = ApiRequestDataTypeInterface | string;
149
+ /** `url` (beyond the contract, optional): the absolute URL the request was made to, for the `links` of a list. */
150
+ type OfflineRequest = {
151
+ method: OfflineMethod;
152
+ path: string;
153
+ query: URLSearchParams;
154
+ body?: any;
155
+ url?: string;
156
+ };
157
+ /** `body` is a JSON:API document, `undefined` for a 204. */
158
+ type OfflineAnswer = {
159
+ status: number;
160
+ body: any;
161
+ };
162
+ type LocalAnswerContext = {
163
+ request: OfflineRequest;
164
+ params: Record<string, string>;
165
+ store: DeviceStore;
166
+ };
167
+ type LocalAnswer = (ctx: LocalAnswerContext) => Promise<OfflineAnswer>;
168
+ type OfflineRoute = {
169
+ method: OfflineMethod;
170
+ pattern: PatternSegment[];
171
+ answer: LocalAnswer;
172
+ queue: boolean;
173
+ };
174
+ /**
175
+ * A type the API reads only as a list per target record: each changed record's target is the
176
+ * other end of its sync link of `linkKind` (from the record to the target), and `endpoint`
177
+ * builds that target's list path with `EndpointCreator`.
178
+ */
179
+ type OfflineLinkedListFetch = {
180
+ linkKind: string;
181
+ endpoint: (rootId: string, target: {
182
+ type: string;
183
+ id: string;
184
+ }) => string;
185
+ };
186
+ type OfflineTypeConfig = {
187
+ module: ApiRequestDataTypeInterface;
188
+ /**
189
+ * How the sync fetches the type's changed records: the root record's list (`root-list`), one
190
+ * GET per record (`by-id`), or (`linked-list`) the list of each record's target, found through
191
+ * the sync answer's links of `linkedList.linkKind` (e.g. player notes, read per target only).
192
+ */
193
+ fetch: "root-list" | "by-id" | "linked-list";
194
+ /** Required with `fetch: "linked-list"`. */
195
+ linkedList?: OfflineLinkedListFetch;
196
+ readOnly: boolean;
197
+ serverOwnedFields: string[];
198
+ /**
199
+ * Relationship name → Neo4j relationship type (`faction` → `MEMBER_OF`). A link the device
200
+ * writes from a request's relationships carries the mapped type, the kind synced links carry;
201
+ * an unmapped name is stored as the name itself.
202
+ */
203
+ relationshipKinds?: Record<string, string>;
204
+ /** The string attributes the server's fulltext index covers (default: every string attribute). */
205
+ searchFields?: string[];
206
+ /** The server's order with no `orderBy` (default `updatedAt DESC`). */
207
+ defaultOrderBy?: string;
208
+ };
209
+ type OfflineConfig = {
210
+ getUserId: () => string | undefined;
211
+ /**
212
+ * Offline mode is off on each device until the user turns it on (`turnOnOffline`), stored per
213
+ * user per device. Default (undefined/false): always on, every device keeps the copy.
214
+ */
215
+ requireOptIn?: boolean;
216
+ /** The module whose records own every other record (in Wyrdli, Modules.Campaign). */
217
+ rootModule: ApiRequestDataTypeInterface;
218
+ types: OfflineTypeConfig[];
219
+ routes: OfflineRoute[];
220
+ /** Ids of the root records to keep on the device. */
221
+ listSyncRoots: () => Promise<string[]>;
222
+ shellPath: (locale: string) => string;
223
+ syncIntervalMs: number;
224
+ /**
225
+ * Where the device worker loads SQLite from at run time (default `/offline-sqlite/index.mjs`).
226
+ * SQLite is never bundled: its own helper worker uses a computed URL that app bundlers refuse.
227
+ * The app serves the files of `dist/offline/sqlite/` at this URL's folder.
228
+ */
229
+ sqliteModuleUrl?: string;
230
+ /**
231
+ * Where the device-copy worker is started from (default `/offline-sqlite/device-worker.mjs`).
232
+ * It is a standalone file in `dist/offline/sqlite/`, never bundled by the app.
233
+ */
234
+ deviceWorkerUrl?: string;
235
+ };
236
+ declare const DEFAULT_SQLITE_MODULE_URL = "/offline-sqlite/index.mjs";
237
+ declare const DEVICE_WORKER_FILE = "device-worker.mjs";
238
+ declare const DEFAULT_DEVICE_WORKER_URL = "/offline-sqlite/device-worker.mjs";
239
+ /**
240
+ * The files of `dist/offline/sqlite/` the app serves: the device worker and the SQLite files
241
+ * copied from @sqlite.org/sqlite-wasm.
242
+ */
243
+ declare const OFFLINE_SQLITE_FILES: readonly ["device-worker.mjs", "index.mjs", "sqlite3.wasm", "sqlite3-opfs-async-proxy.js"];
244
+ /** Thrown by a local answer for something that only the server can do. Answered as a 503 "needs-connection". */
245
+ declare class NeedsConnectionError extends Error {
246
+ readonly status = 503;
247
+ readonly code = "needs-connection";
248
+ constructor(message?: string);
249
+ }
250
+ /** Segments joined with "/", modules by their endpoint. */
251
+ declare function patternToString(pattern: PatternSegment[]): string;
252
+
253
+ /**
254
+ * Opts the app into offline mode (the `./offline` entry's `configureOffline`). It also registers
255
+ * the browser device-store opener, so the store, the worker and SQLite are reachable only from
256
+ * this entry, never from the request path every other entry shares.
257
+ */
258
+ declare function configureOffline(config: OfflineConfig): void;
259
+
260
+ /**
261
+ * The signed-in user's device store cannot be used: it could not be opened (opfs-sahpool holds
262
+ * one connection per origin, so a second tab cannot open it) or the user logged out. A request
263
+ * answered from the device copy then needs a connection (503 "needs-connection").
264
+ */
265
+ declare class DeviceStoreUnavailableError extends NeedsConnectionError {
266
+ constructor(message?: string);
267
+ }
268
+ /**
269
+ * This browser cannot keep the device copy at all: not a secure context (https or `localhost`),
270
+ * no private file storage (`navigator.storage.getDirectory`), or the worker reports the OPFS
271
+ * APIs missing. Offline mode switches off for the page (`markOfflineUnavailable`). A failure on
272
+ * a browser that can keep the copy (the file held by another tab) is a `DeviceStoreUnavailableError`.
273
+ */
274
+ declare class OfflineUnavailableError extends DeviceStoreUnavailableError {
275
+ constructor(reason: string);
276
+ }
277
+ /** Whether this browser can keep the device copy (true until found otherwise). */
278
+ declare function isOfflineAvailable(): boolean;
279
+ /**
280
+ * Configured, the browser can keep the device copy (see `markOfflineUnavailable`), and offline
281
+ * mode is on on this device for the signed-in user (see `requireOptIn`).
282
+ */
283
+ declare function isOfflineConfigured(): boolean;
284
+ /**
285
+ * Whether offline mode is on on this device for `userId`. Always true without
286
+ * `OfflineConfig.requireOptIn`; otherwise the stored switch, false with no user.
287
+ */
288
+ declare function isOfflineEnabledOnDevice(userId?: string | null): boolean;
289
+ /**
290
+ * Reads the device switch and the last sync time of the signed-in user into the offline state
291
+ * (`enabled`, `lastSyncedAt`). Does nothing before `configureOffline`.
292
+ */
293
+ declare function refreshOfflineDeviceState(): void;
294
+ declare function getOfflineConfig(): OfflineConfig;
295
+ /** Replaces how a user's device store is opened (tests pass `openMemoryDeviceStore`). */
296
+ declare function setDeviceStoreOpener(opener: ((userId: string) => Promise<DeviceStore>) | null): void;
297
+ /**
298
+ * The signed-in user's device store, opened once per user id. Only the signed-in user's file is
299
+ * ever opened; with no signed-in user the request needs a connection.
300
+ */
301
+ declare function getOfflineStore(): Promise<DeviceStore>;
302
+ /** Forgets the opened store of a user (logout, deleted file). */
303
+ declare function forgetOfflineStore(userId: string): void;
304
+
305
+ type OfflineState = {
306
+ online: boolean;
307
+ pending: number;
308
+ conflicts: number;
309
+ draining: boolean;
310
+ lockedRootIds: string[];
311
+ /**
312
+ * False once this browser was found unable to keep the device copy (no secure context, no
313
+ * private file storage, or the worker reports the OPFS APIs missing). Offline mode is then off
314
+ * for the rest of the page: `isOfflineConfigured()` answers false and nothing offline runs.
315
+ */
316
+ available: boolean;
317
+ /**
318
+ * Offline mode is on on this device for the signed-in user. Always true once configured
319
+ * without `OfflineConfig.requireOptIn`; otherwise the GM's per-device switch.
320
+ */
321
+ enabled: boolean;
322
+ /** The running full sync: root records done out of the total, null when no sync runs. */
323
+ syncProgress: {
324
+ done: number;
325
+ total: number;
326
+ } | null;
327
+ /** When the last full sync on this device finished (epoch ms), null when never. */
328
+ lastSyncedAt: number | null;
329
+ /** How many root records the last full sync kept on this device, null when unknown. */
330
+ rootCount: number | null;
331
+ };
332
+ declare function getOfflineState(): OfflineState;
333
+ declare function subscribeOfflineState(listener: (s: OfflineState) => void): () => void;
334
+ /** Applies a partial change. Listeners run once, and only when a value actually changed. */
335
+ declare function setOfflineState(patch: Partial<OfflineState>): void;
336
+
337
+ declare function useOfflineState(): OfflineState;
338
+ declare function useNeedsConnection(): boolean;
339
+
340
+ type RouteMatch = {
341
+ route: OfflineRoute;
342
+ params: Record<string, string>;
343
+ };
344
+ declare function matchPattern(pattern: PatternSegment[], path: string): Record<string, string> | null;
345
+ /** The first route, in the order given, whose method and pattern match. The query string is ignored. */
346
+ declare function matchRoute(routes: OfflineRoute[], method: OfflineMethod, path: string): RouteMatch | null;
347
+
348
+ type ApiData = {
349
+ data: any;
350
+ ok: boolean;
351
+ status: number;
352
+ statusText: string;
353
+ token?: string;
354
+ refreshToken?: string;
355
+ /** Set by `directFetch` when the API could not be reached at all (the 503 it reports). */
356
+ networkError?: boolean;
357
+ };
358
+
359
+ type LocalTransportRequest = {
360
+ method: string;
361
+ url: string;
362
+ body?: any;
363
+ files?: unknown;
364
+ };
365
+ /**
366
+ * Answers a request from the device copy: the app's custom routes first, then the generic ones.
367
+ * The result has the `ApiData` shape `directFetch` returns, so `translateResponse` and
368
+ * `rehydrate()` run unchanged.
369
+ */
370
+ declare function localTransport(request: LocalTransportRequest): Promise<ApiData>;
371
+
372
+ type WriteThroughRequest = {
373
+ method: string;
374
+ url: string;
375
+ };
376
+ /**
377
+ * After a successful online write, copies the answer into the device copy so it is never behind
378
+ * the GM's own work: the resource and its `included` for POST/PUT/PATCH, the record removed for a
379
+ * DELETE. It never fails the request: a problem is logged and the next sync repairs the copy.
380
+ */
381
+ declare function writeThrough(request: WriteThroughRequest, response: ApiData): Promise<void>;
382
+
383
+ /** A JSON:API errors document with one error, answered with `status`. */
384
+ declare function errorAnswer(status: number, title: string, detail: string, code?: string): OfflineAnswer;
385
+ declare function needsConnectionAnswer(detail?: string): OfflineAnswer;
386
+ declare function notFoundAnswer(detail: string): OfflineAnswer;
387
+ /** A single document of one stored record, with `included` (`includedFor`). */
388
+ declare function singleAnswer(store: DeviceStore, record: StoredRecord, status?: number): Promise<OfflineAnswer>;
389
+ /**
390
+ * The `included` of a local answer: the stored records linked to `records` (the synced links),
391
+ * then the stored records their relationships point to, and one level further the stored records
392
+ * the relationships of those included records point to (an NPC's root record, then the root record's
393
+ * owner), so a model reads the same nested relationships it reads from the server.
394
+ *
395
+ * Stored resources point at each other (a root record at its owner, the owner at the root record's
396
+ * company, the company back at the owner); `RehydrationFactory` skips a resource met again inside
397
+ * its own chain, so those circles never recurse.
398
+ */
399
+ declare function includedFor(store: DeviceStore, records: StoredRecord[]): Promise<any[]>;
400
+ /** The server's default list order (`defineEntity` sets `defaultOrderBy: "updatedAt DESC"`). */
401
+ declare const SERVER_DEFAULT_ORDER_BY = "updatedAt DESC";
402
+ type ListOptions = {
403
+ /** The string attributes the server's fulltext index covers (default: every string attribute). */
404
+ searchFields?: string[];
405
+ /** The order with no `orderBy` (default `updatedAt DESC`, the server's). */
406
+ defaultOrderBy?: string;
407
+ };
408
+ /**
409
+ * `search` as the server runs it: the term is split into letter/digit tokens and a record
410
+ * matches when every token appears, case-insensitive, inside one of its indexed string
411
+ * attributes (the fulltext query `*token* AND ...`).
412
+ */
413
+ declare function searchRecords(records: StoredRecord[], query: URLSearchParams, options?: ListOptions): StoredRecord[];
414
+ /**
415
+ * Orders records as the server's `ORDER BY` does for `orderBy` (`field` or `field ASC|DESC`),
416
+ * or for the default order when there is none. `null` when the server would refuse the
417
+ * `orderBy` (400).
418
+ */
419
+ declare function orderRecords(records: StoredRecord[], query: URLSearchParams, options?: ListOptions): StoredRecord[] | null;
420
+ /**
421
+ * One list answer for a set of stored records, as the server answers a list: `search`, then
422
+ * `orderBy` (or the default order), then paged the way `JsonApiPaginator` pages (`fetchAll`,
423
+ * `page[size]`, `page[offset]`), with `included`. App answers pass the records they selected.
424
+ */
425
+ declare function listAnswer(ctx: LocalAnswerContext, records: StoredRecord[], options?: ListOptions): Promise<OfflineAnswer>;
426
+ /**
427
+ * A list document of stored records in the order the caller settled, every record in one page,
428
+ * with `included`: for an app's custom list query that has its own `ORDER BY` and no paging.
429
+ */
430
+ declare function orderedListAnswer(store: DeviceStore, records: StoredRecord[]): Promise<OfflineAnswer>;
431
+ /**
432
+ * A list document of resources the device computed rather than stored (a query-result
433
+ * projection such as a search hit, built by its model), answered as they are, nothing included.
434
+ */
435
+ declare function resourceListAnswer(resources: any[]): OfflineAnswer;
436
+ /**
437
+ * The kind a link written on the device carries for relationship `name`: the Neo4j type the
438
+ * type config maps it to (the kind synced links carry), otherwise the name itself.
439
+ */
440
+ declare function linkKindFor(typeConfig: OfflineTypeConfig | undefined, name: string): string;
441
+ /**
442
+ * Every kind a link of relationship `name` may carry on the device: the name (written on the
443
+ * device before a mapping existed) and the mapped Neo4j type (synced). `undefined` when the
444
+ * Neo4j type is not known: then a link of any kind may be the one.
445
+ */
446
+ declare function linkKindsOf(typeConfig: OfflineTypeConfig | undefined, name: string): string[] | undefined;
447
+ /**
448
+ * The stored links of `id` whose kind is one of `kinds` (Neo4j types and/or relationship
449
+ * names). `out`: links from `id`; `in`: links to `id`; `both`: either.
450
+ */
451
+ declare function linksOfKind(store: DeviceStore, id: string, kinds: string[], direction?: "out" | "in" | "both"): Promise<StoredLink[]>;
452
+ /** The `data` of a JSON:API document a model built (`createJsonApi()` or a dedicated method). */
453
+ type ResourceDocumentData = {
454
+ attributes?: Record<string, unknown>;
455
+ relationships?: Record<string, unknown>;
456
+ meta?: Record<string, unknown>;
457
+ };
458
+ /**
459
+ * The resource a create document stands for, under `identity`: the document's attributes,
460
+ * relationships and meta, as the device stores a record it creates.
461
+ */
462
+ declare function resourceFromDocument(document: any, identity: {
463
+ type: string;
464
+ id: string;
465
+ }): any;
466
+ /**
467
+ * A stored resource with a document's `data` written over it: attributes and relationships merged
468
+ * by name, and meta merged when the document carries one, as the device applies an edit.
469
+ */
470
+ declare function mergeResource(before: any, identity: {
471
+ type: string;
472
+ id: string;
473
+ }, data: ResourceDocumentData): any;
474
+
475
+ /**
476
+ * Opens the device copy of `userId` (`nja-offline-<userId>.sqlite3` in the OPFS,
477
+ * through the worker). Only one user's file is open at a time: opening another
478
+ * user's file closes the previous one first.
479
+ */
480
+ declare function openDeviceStore(userId: string): Promise<DeviceStore>;
481
+ /** The same store and schema over an in-memory database, for tests. */
482
+ declare function openMemoryDeviceStore(): Promise<DeviceStore>;
483
+ /**
484
+ * Closes `userId`'s device copy in every page and removes every OPFS entry of it. The files can
485
+ * only be removed once no page holds them, so the removal waits for every page to let go.
486
+ */
487
+ declare function deleteDeviceStore(userId: string): Promise<void>;
488
+ /** Whether `userId`'s device copy holds unsent changes. Never opens another user's file. */
489
+ declare function hasUnsentChanges(userId: string): Promise<boolean>;
490
+ /**
491
+ * A user signed in on this browser: their device copy may be opened again (it is blocked after
492
+ * a logout), and every other user's device copy is deleted when it has nothing unsent, and kept
493
+ * untouched (never opened) when it has. When the images cache was not this user's (another user
494
+ * signed in before, or no owner is known), it is deleted: the user's sync downloads their artwork.
495
+ */
496
+ declare function onUserSignedIn(userId: string): Promise<void>;
497
+ /** Locked now: the server said so, or the lock date it gave has passed. */
498
+ declare function isRootLocked(s: {
499
+ locked: boolean;
500
+ lockedFrom: Date | null;
501
+ }, now: Date): boolean;
502
+
503
+ /**
504
+ * Brings one root record's device copy up to date with the sync answer (C2): records changed since
505
+ * the stored server time are fetched (C3), records gone from the root record are deleted, the links
506
+ * are replaced, and artwork images are downloaded. The sync state is stored only once every
507
+ * fetch succeeded (or answered 403/404). A record with a waiting change is never overwritten.
508
+ */
509
+ declare function syncRoot(rootId: string): Promise<void>;
510
+ /**
511
+ * Syncs every root record `listSyncRoots()` returns, then removes from the device the root records
512
+ * that are no longer roots (unless they still have waiting changes). Does nothing offline.
513
+ */
514
+ declare function syncAll(): Promise<void>;
515
+
516
+ type DrainResult = {
517
+ applied: number;
518
+ conflicts: number;
519
+ stoppedForLogin: boolean;
520
+ };
521
+ /**
522
+ * Sends every waiting change of the unlocked root records in ONE `offline-change-sets` request,
523
+ * after renewing the sign-in. Applied entries are removed and their answer written to the
524
+ * device copy; conflicts become Conflict rows; waiting entries stay pending. A network error
525
+ * or an expired sign-in stops it and keeps every entry.
526
+ */
527
+ declare function drainQueue(): Promise<DrainResult>;
528
+
529
+ /** The unresolved conflicts of the signed-in user's device. */
530
+ declare function useConflicts(): Conflict[];
531
+
532
+ type ConflictChoice = {
533
+ kind: "mine";
534
+ } | {
535
+ kind: "theirs";
536
+ }
537
+ /**
538
+ * Per conflicting field: whose value wins. `mergedBody`: the JSON:API document the record
539
+ * type's model built with createJsonApi() holding only the fields chosen "merged".
540
+ */
541
+ | {
542
+ kind: "fields";
543
+ choices: Record<string, "mine" | "theirs" | "merged">;
544
+ mergedBody?: any;
545
+ } | {
546
+ kind: "restore";
547
+ } | {
548
+ kind: "accept-deletion";
549
+ } | {
550
+ kind: "delete-anyway";
551
+ } | {
552
+ kind: "retry";
553
+ } | {
554
+ kind: "discard";
555
+ };
556
+ /**
557
+ * Applies the GM's choice for one conflict. "keep mine", per-field choices, "restore" and "delete anyway"
558
+ * queue one more change (sent with the next package); "keep theirs" and "let the deletion
559
+ * stand" only update the device. Online, the queue is sent right away.
560
+ */
561
+ declare function resolveConflict(id: string, choice: ConflictChoice): Promise<void>;
562
+
563
+ /** Stops every running offline runtime (timers, listeners). Called on logout. */
564
+ declare function stopOfflineRuntime(): void;
565
+ /**
566
+ * Starts the offline runtime: drain-then-sync on open (online) and on every reconnect, syncAll
567
+ * every `syncIntervalMs` while online, a probe of the API while the session is offline but the
568
+ * browser is online (a probe that reaches the API runs the drain and the sync, and the session
569
+ * goes online once they succeeded), and the lock dates checked every minute. Returns the stop
570
+ * function.
571
+ */
572
+ declare function startOfflineRuntime(): () => void;
573
+
574
+ /**
575
+ * How many changes the signed-in user's device still has to send (every queue entry: pending,
576
+ * sending, in conflict or failed). The app warns before logging out when this is above 0.
577
+ */
578
+ declare function prepareOfflineLogout(): Promise<{
579
+ pending: number;
580
+ }>;
581
+ /**
582
+ * Deletes the user's device database and the cached artwork images. Call it right before logging
583
+ * out. The runtime is stopped first and the store blocked until the next sign-in
584
+ * (`onUserSignedIn`), so a sync still running cannot open, and so recreate, the deleted file.
585
+ * The device switch (`requireOptIn`) is turned off too: after a logout nothing is downloaded again
586
+ * until the user turns offline mode back on.
587
+ */
588
+ declare function finishOfflineLogout(userId: string): Promise<void>;
589
+
590
+ /**
591
+ * Turns offline mode on on this device for the signed-in user (`OfflineConfig.requireOptIn`).
592
+ * It does not start the runtime: the app starts it once `enabled` is true in the offline state.
593
+ */
594
+ declare function turnOnOffline(): Promise<void>;
595
+ /**
596
+ * Turns offline mode off on this device for the signed-in user and deletes this device's copy
597
+ * (database and cached images). Refuses, changing nothing, while the copy has changes still to
598
+ * send: the answer then carries how many.
599
+ */
600
+ declare function turnOffOffline(): Promise<{
601
+ turnedOff: true;
602
+ } | {
603
+ turnedOff: false;
604
+ pending: number;
605
+ }>;
606
+
607
+ export { type Conflict, type ConflictChoice, type ConflictKind, DEFAULT_DEVICE_WORKER_URL, DEFAULT_SQLITE_MODULE_URL, DEVICE_WORKER_FILE, type DeviceStore, DeviceStoreUnavailableError, type DrainResult, type ListOptions, type LocalAnswer, type LocalAnswerContext, NeedsConnectionError, OFFLINE_REPLAY_HEADER, OFFLINE_SQLITE_FILES, type OfflineAnswer, type OfflineChangeResolution, type OfflineConfig, type OfflineLinkedListFetch, type OfflineMethod, type OfflineRequest, type OfflineRoute, type OfflineState, type OfflineTypeConfig, OfflineUnavailableError, type PatternSegment, type QueueEntry, type QueueResolution, type QueueStatus, type ResourceDocumentData, type RouteMatch, SERVER_DEFAULT_ORDER_BY, type SkippedRecord, type StoredLink, type StoredRecord, type SyncState, configureOffline, deleteDeviceStore, drainQueue, errorAnswer, finishOfflineLogout, forgetOfflineStore, getOfflineConfig, getOfflineState, getOfflineStore, hasUnsentChanges, includedFor, isOfflineAvailable, isOfflineConfigured, isOfflineEnabledOnDevice, isRootLocked, linkKindFor, linkKindsOf, linksOfKind, listAnswer, localTransport, matchPattern, matchRoute, mergeResource, needsConnectionAnswer, notFoundAnswer, onUserSignedIn, openDeviceStore, openMemoryDeviceStore, orderRecords, orderedListAnswer, patternToString, prepareOfflineLogout, refreshOfflineDeviceState, resolveConflict, resourceFromDocument, resourceListAnswer, searchRecords, setDeviceStoreOpener, setOfflineState, singleAnswer, startOfflineRuntime, stopOfflineRuntime, subscribeOfflineState, syncAll, syncRoot, turnOffOffline, turnOnOffline, useConflicts, useNeedsConnection, useOfflineState, writeThrough };