@multiplatform.one/frappe 6.0.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.
Files changed (858) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +241 -0
  3. package/dist/cjs/auth.cjs +50 -0
  4. package/dist/cjs/auth.native.js +100 -0
  5. package/dist/cjs/auth.native.js.map +1 -0
  6. package/dist/cjs/auth.spec.cjs +322 -0
  7. package/dist/cjs/auth.spec.native.js +329 -0
  8. package/dist/cjs/auth.spec.native.js.map +1 -0
  9. package/dist/cjs/call.cjs +45 -0
  10. package/dist/cjs/call.native.js +93 -0
  11. package/dist/cjs/call.native.js.map +1 -0
  12. package/dist/cjs/call.spec.cjs +427 -0
  13. package/dist/cjs/call.spec.native.js +430 -0
  14. package/dist/cjs/call.spec.native.js.map +1 -0
  15. package/dist/cjs/collection.cjs +278 -0
  16. package/dist/cjs/collection.native.js +494 -0
  17. package/dist/cjs/collection.native.js.map +1 -0
  18. package/dist/cjs/collection.spec.cjs +186 -0
  19. package/dist/cjs/collection.spec.native.js +216 -0
  20. package/dist/cjs/collection.spec.native.js.map +1 -0
  21. package/dist/cjs/common/types.cjs +18 -0
  22. package/dist/cjs/common/types.native.js +21 -0
  23. package/dist/cjs/common/types.native.js.map +1 -0
  24. package/dist/cjs/common/types.spec.cjs +266 -0
  25. package/dist/cjs/common/types.spec.native.js +271 -0
  26. package/dist/cjs/common/types.spec.native.js.map +1 -0
  27. package/dist/cjs/core/http.cjs +289 -0
  28. package/dist/cjs/core/http.native.js +484 -0
  29. package/dist/cjs/core/http.native.js.map +1 -0
  30. package/dist/cjs/core/http.spec.cjs +1259 -0
  31. package/dist/cjs/core/http.spec.native.js +1401 -0
  32. package/dist/cjs/core/http.spec.native.js.map +1 -0
  33. package/dist/cjs/core/types.cjs +53 -0
  34. package/dist/cjs/core/types.native.js +178 -0
  35. package/dist/cjs/core/types.native.js.map +1 -0
  36. package/dist/cjs/core/wire.cjs +53 -0
  37. package/dist/cjs/core/wire.native.js +56 -0
  38. package/dist/cjs/core/wire.native.js.map +1 -0
  39. package/dist/cjs/core/wire.spec.cjs +478 -0
  40. package/dist/cjs/core/wire.spec.native.js +482 -0
  41. package/dist/cjs/core/wire.spec.native.js.map +1 -0
  42. package/dist/cjs/db.cjs +355 -0
  43. package/dist/cjs/db.native.js +469 -0
  44. package/dist/cjs/db.native.js.map +1 -0
  45. package/dist/cjs/db.spec.cjs +862 -0
  46. package/dist/cjs/db.spec.native.js +865 -0
  47. package/dist/cjs/db.spec.native.js.map +1 -0
  48. package/dist/cjs/devtools/FrappeDevtoolsPanel.cjs +838 -0
  49. package/dist/cjs/devtools/FrappeDevtoolsPanel.native.js +919 -0
  50. package/dist/cjs/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
  51. package/dist/cjs/devtools/index.cjs +30 -0
  52. package/dist/cjs/devtools/index.native.js +33 -0
  53. package/dist/cjs/devtools/index.native.js.map +1 -0
  54. package/dist/cjs/devtools/plugin.cjs +39 -0
  55. package/dist/cjs/devtools/plugin.native.js +56 -0
  56. package/dist/cjs/devtools/plugin.native.js.map +1 -0
  57. package/dist/cjs/devtools/theme.cjs +55 -0
  58. package/dist/cjs/devtools/theme.native.js +58 -0
  59. package/dist/cjs/devtools/theme.native.js.map +1 -0
  60. package/dist/cjs/doctype.cjs +410 -0
  61. package/dist/cjs/doctype.native.js +521 -0
  62. package/dist/cjs/doctype.native.js.map +1 -0
  63. package/dist/cjs/doctype.spec.cjs +460 -0
  64. package/dist/cjs/doctype.spec.native.js +538 -0
  65. package/dist/cjs/doctype.spec.native.js.map +1 -0
  66. package/dist/cjs/file.cjs +41 -0
  67. package/dist/cjs/file.native.js +84 -0
  68. package/dist/cjs/file.native.js.map +1 -0
  69. package/dist/cjs/file.spec.cjs +315 -0
  70. package/dist/cjs/file.spec.native.js +342 -0
  71. package/dist/cjs/file.spec.native.js.map +1 -0
  72. package/dist/cjs/fixtureDb.cjs +212 -0
  73. package/dist/cjs/fixtureDb.native.js +313 -0
  74. package/dist/cjs/fixtureDb.native.js.map +1 -0
  75. package/dist/cjs/fixtures.cjs +199 -0
  76. package/dist/cjs/fixtures.native.js +400 -0
  77. package/dist/cjs/fixtures.native.js.map +1 -0
  78. package/dist/cjs/frappeApp.cjs +117 -0
  79. package/dist/cjs/frappeApp.native.js +179 -0
  80. package/dist/cjs/frappeApp.native.js.map +1 -0
  81. package/dist/cjs/frappeApp.spec.cjs +306 -0
  82. package/dist/cjs/frappeApp.spec.native.js +308 -0
  83. package/dist/cjs/frappeApp.spec.native.js.map +1 -0
  84. package/dist/cjs/frappeReact.cjs +314 -0
  85. package/dist/cjs/frappeReact.native.js +351 -0
  86. package/dist/cjs/frappeReact.native.js.map +1 -0
  87. package/dist/cjs/frappeReact.spec.cjs +531 -0
  88. package/dist/cjs/frappeReact.spec.native.js +670 -0
  89. package/dist/cjs/frappeReact.spec.native.js.map +1 -0
  90. package/dist/cjs/index.cjs +149 -0
  91. package/dist/cjs/index.native.js +152 -0
  92. package/dist/cjs/index.native.js.map +1 -0
  93. package/dist/cjs/optimistic.cjs +28 -0
  94. package/dist/cjs/optimistic.native.js +31 -0
  95. package/dist/cjs/optimistic.native.js.map +1 -0
  96. package/dist/cjs/optimistic.spec.cjs +11 -0
  97. package/dist/cjs/optimistic.spec.native.js +19 -0
  98. package/dist/cjs/optimistic.spec.native.js.map +1 -0
  99. package/dist/cjs/pacer.cjs +200 -0
  100. package/dist/cjs/pacer.native.js +274 -0
  101. package/dist/cjs/pacer.native.js.map +1 -0
  102. package/dist/cjs/pacer.spec.cjs +470 -0
  103. package/dist/cjs/pacer.spec.native.js +537 -0
  104. package/dist/cjs/pacer.spec.native.js.map +1 -0
  105. package/dist/cjs/plugin/index.cjs +124 -0
  106. package/dist/cjs/plugin/index.native.js +193 -0
  107. package/dist/cjs/plugin/index.native.js.map +1 -0
  108. package/dist/cjs/realtime.cjs +265 -0
  109. package/dist/cjs/realtime.native.js +387 -0
  110. package/dist/cjs/realtime.native.js.map +1 -0
  111. package/dist/cjs/realtime.spec.cjs +326 -0
  112. package/dist/cjs/realtime.spec.native.js +392 -0
  113. package/dist/cjs/realtime.spec.native.js.map +1 -0
  114. package/dist/cjs/sync/backfill.cjs +314 -0
  115. package/dist/cjs/sync/backfill.native.js +487 -0
  116. package/dist/cjs/sync/backfill.native.js.map +1 -0
  117. package/dist/cjs/sync/backfill.spec.cjs +586 -0
  118. package/dist/cjs/sync/backfill.spec.native.js +712 -0
  119. package/dist/cjs/sync/backfill.spec.native.js.map +1 -0
  120. package/dist/cjs/sync/debug.spec.cjs +125 -0
  121. package/dist/cjs/sync/debug.spec.native.js +133 -0
  122. package/dist/cjs/sync/debug.spec.native.js.map +1 -0
  123. package/dist/cjs/sync/filters.cjs +311 -0
  124. package/dist/cjs/sync/filters.native.js +446 -0
  125. package/dist/cjs/sync/filters.native.js.map +1 -0
  126. package/dist/cjs/sync/filters.spec.cjs +168 -0
  127. package/dist/cjs/sync/filters.spec.native.js +173 -0
  128. package/dist/cjs/sync/filters.spec.native.js.map +1 -0
  129. package/dist/cjs/sync/index.cjs +303 -0
  130. package/dist/cjs/sync/index.native.js +462 -0
  131. package/dist/cjs/sync/index.native.js.map +1 -0
  132. package/dist/cjs/sync/merge.cjs +435 -0
  133. package/dist/cjs/sync/merge.native.js +717 -0
  134. package/dist/cjs/sync/merge.native.js.map +1 -0
  135. package/dist/cjs/sync/merge.spec.cjs +908 -0
  136. package/dist/cjs/sync/merge.spec.native.js +937 -0
  137. package/dist/cjs/sync/merge.spec.native.js.map +1 -0
  138. package/dist/cjs/sync/multiOrderBy.spec.cjs +165 -0
  139. package/dist/cjs/sync/multiOrderBy.spec.native.js +229 -0
  140. package/dist/cjs/sync/multiOrderBy.spec.native.js.map +1 -0
  141. package/dist/cjs/sync/mutationEngine.cjs +781 -0
  142. package/dist/cjs/sync/mutationEngine.native.js +1159 -0
  143. package/dist/cjs/sync/mutationEngine.native.js.map +1 -0
  144. package/dist/cjs/sync/mutationEngine.spec.cjs +1158 -0
  145. package/dist/cjs/sync/mutationEngine.spec.native.js +1173 -0
  146. package/dist/cjs/sync/mutationEngine.spec.native.js.map +1 -0
  147. package/dist/cjs/sync/mutationJournal.cjs +150 -0
  148. package/dist/cjs/sync/mutationJournal.native.js +282 -0
  149. package/dist/cjs/sync/mutationJournal.native.js.map +1 -0
  150. package/dist/cjs/sync/mutationJournal.spec.cjs +330 -0
  151. package/dist/cjs/sync/mutationJournal.spec.native.js +333 -0
  152. package/dist/cjs/sync/mutationJournal.spec.native.js.map +1 -0
  153. package/dist/cjs/sync/mutations.spec.cjs +327 -0
  154. package/dist/cjs/sync/mutations.spec.native.js +330 -0
  155. package/dist/cjs/sync/mutations.spec.native.js.map +1 -0
  156. package/dist/cjs/sync/normalizedCache.cjs +140 -0
  157. package/dist/cjs/sync/normalizedCache.native.js +229 -0
  158. package/dist/cjs/sync/normalizedCache.native.js.map +1 -0
  159. package/dist/cjs/sync/normalizedCache.spec.cjs +302 -0
  160. package/dist/cjs/sync/normalizedCache.spec.native.js +311 -0
  161. package/dist/cjs/sync/normalizedCache.spec.native.js.map +1 -0
  162. package/dist/cjs/sync/persistence.cjs +627 -0
  163. package/dist/cjs/sync/persistence.native.js +911 -0
  164. package/dist/cjs/sync/persistence.native.js.map +1 -0
  165. package/dist/cjs/sync/persistence.spec.cjs +562 -0
  166. package/dist/cjs/sync/persistence.spec.native.js +572 -0
  167. package/dist/cjs/sync/persistence.spec.native.js.map +1 -0
  168. package/dist/cjs/sync/realtime.cjs +214 -0
  169. package/dist/cjs/sync/realtime.native.js +340 -0
  170. package/dist/cjs/sync/realtime.native.js.map +1 -0
  171. package/dist/cjs/sync/realtime.spec.cjs +499 -0
  172. package/dist/cjs/sync/realtime.spec.native.js +512 -0
  173. package/dist/cjs/sync/realtime.spec.native.js.map +1 -0
  174. package/dist/cjs/sync/rehydration.cjs +323 -0
  175. package/dist/cjs/sync/rehydration.native.js +531 -0
  176. package/dist/cjs/sync/rehydration.native.js.map +1 -0
  177. package/dist/cjs/sync/rehydration.spec.cjs +731 -0
  178. package/dist/cjs/sync/rehydration.spec.native.js +736 -0
  179. package/dist/cjs/sync/rehydration.spec.native.js.map +1 -0
  180. package/dist/cjs/sync/storage.cjs +142 -0
  181. package/dist/cjs/sync/storage.native.js +209 -0
  182. package/dist/cjs/sync/storage.native.js.map +1 -0
  183. package/dist/cjs/sync/storage.spec.cjs +446 -0
  184. package/dist/cjs/sync/storage.spec.native.js +470 -0
  185. package/dist/cjs/sync/storage.spec.native.js.map +1 -0
  186. package/dist/cjs/sync/subscriptionManager.cjs +487 -0
  187. package/dist/cjs/sync/subscriptionManager.native.js +948 -0
  188. package/dist/cjs/sync/subscriptionManager.native.js.map +1 -0
  189. package/dist/cjs/sync/subscriptionManager.spec.cjs +1102 -0
  190. package/dist/cjs/sync/subscriptionManager.spec.native.js +1136 -0
  191. package/dist/cjs/sync/subscriptionManager.spec.native.js.map +1 -0
  192. package/dist/cjs/sync/subscriptions.spec.cjs +937 -0
  193. package/dist/cjs/sync/subscriptions.spec.native.js +969 -0
  194. package/dist/cjs/sync/subscriptions.spec.native.js.map +1 -0
  195. package/dist/cjs/sync/syncModule.spec.cjs +206 -0
  196. package/dist/cjs/sync/syncModule.spec.native.js +222 -0
  197. package/dist/cjs/sync/syncModule.spec.native.js.map +1 -0
  198. package/dist/cjs/sync/types.cjs +99 -0
  199. package/dist/cjs/sync/types.native.js +102 -0
  200. package/dist/cjs/sync/types.native.js.map +1 -0
  201. package/dist/cjs/sync/types.spec.cjs +246 -0
  202. package/dist/cjs/sync/types.spec.native.js +256 -0
  203. package/dist/cjs/sync/types.spec.native.js.map +1 -0
  204. package/dist/cjs/sync/virtualPageManager.cjs +821 -0
  205. package/dist/cjs/sync/virtualPageManager.native.js +1125 -0
  206. package/dist/cjs/sync/virtualPageManager.native.js.map +1 -0
  207. package/dist/cjs/sync/virtualPageManager.spec.cjs +801 -0
  208. package/dist/cjs/sync/virtualPageManager.spec.native.js +873 -0
  209. package/dist/cjs/sync/virtualPageManager.spec.native.js.map +1 -0
  210. package/dist/cjs/util.cjs +31 -0
  211. package/dist/cjs/util.native.js +34 -0
  212. package/dist/cjs/util.native.js.map +1 -0
  213. package/dist/cjs/util.spec.cjs +43 -0
  214. package/dist/cjs/util.spec.native.js +48 -0
  215. package/dist/cjs/util.spec.native.js.map +1 -0
  216. package/dist/esm/auth.mjs +25 -0
  217. package/dist/esm/auth.mjs.map +1 -0
  218. package/dist/esm/auth.native.js +72 -0
  219. package/dist/esm/auth.native.js.map +1 -0
  220. package/dist/esm/auth.spec.mjs +323 -0
  221. package/dist/esm/auth.spec.mjs.map +1 -0
  222. package/dist/esm/auth.spec.native.js +327 -0
  223. package/dist/esm/auth.spec.native.js.map +1 -0
  224. package/dist/esm/call.mjs +20 -0
  225. package/dist/esm/call.mjs.map +1 -0
  226. package/dist/esm/call.native.js +65 -0
  227. package/dist/esm/call.native.js.map +1 -0
  228. package/dist/esm/call.spec.mjs +428 -0
  229. package/dist/esm/call.spec.mjs.map +1 -0
  230. package/dist/esm/call.spec.native.js +428 -0
  231. package/dist/esm/call.spec.native.js.map +1 -0
  232. package/dist/esm/collection.mjs +250 -0
  233. package/dist/esm/collection.mjs.map +1 -0
  234. package/dist/esm/collection.native.js +463 -0
  235. package/dist/esm/collection.native.js.map +1 -0
  236. package/dist/esm/collection.spec.mjs +187 -0
  237. package/dist/esm/collection.spec.mjs.map +1 -0
  238. package/dist/esm/collection.spec.native.js +214 -0
  239. package/dist/esm/collection.spec.native.js.map +1 -0
  240. package/dist/esm/common/types.mjs +2 -0
  241. package/dist/esm/common/types.mjs.map +1 -0
  242. package/dist/esm/common/types.native.js +2 -0
  243. package/dist/esm/common/types.native.js.map +1 -0
  244. package/dist/esm/common/types.spec.mjs +267 -0
  245. package/dist/esm/common/types.spec.mjs.map +1 -0
  246. package/dist/esm/common/types.spec.native.js +269 -0
  247. package/dist/esm/common/types.spec.native.js.map +1 -0
  248. package/dist/esm/core/http.mjs +258 -0
  249. package/dist/esm/core/http.mjs.map +1 -0
  250. package/dist/esm/core/http.native.js +450 -0
  251. package/dist/esm/core/http.native.js.map +1 -0
  252. package/dist/esm/core/http.spec.mjs +1260 -0
  253. package/dist/esm/core/http.spec.mjs.map +1 -0
  254. package/dist/esm/core/http.spec.native.js +1399 -0
  255. package/dist/esm/core/http.spec.native.js.map +1 -0
  256. package/dist/esm/core/types.mjs +26 -0
  257. package/dist/esm/core/types.mjs.map +1 -0
  258. package/dist/esm/core/types.native.js +148 -0
  259. package/dist/esm/core/types.native.js.map +1 -0
  260. package/dist/esm/core/wire.mjs +23 -0
  261. package/dist/esm/core/wire.mjs.map +1 -0
  262. package/dist/esm/core/wire.native.js +23 -0
  263. package/dist/esm/core/wire.native.js.map +1 -0
  264. package/dist/esm/core/wire.spec.mjs +479 -0
  265. package/dist/esm/core/wire.spec.mjs.map +1 -0
  266. package/dist/esm/core/wire.spec.native.js +480 -0
  267. package/dist/esm/core/wire.spec.native.js.map +1 -0
  268. package/dist/esm/db.mjs +329 -0
  269. package/dist/esm/db.mjs.map +1 -0
  270. package/dist/esm/db.native.js +440 -0
  271. package/dist/esm/db.native.js.map +1 -0
  272. package/dist/esm/db.spec.mjs +863 -0
  273. package/dist/esm/db.spec.mjs.map +1 -0
  274. package/dist/esm/db.spec.native.js +863 -0
  275. package/dist/esm/db.spec.native.js.map +1 -0
  276. package/dist/esm/devtools/FrappeDevtoolsPanel.mjs +813 -0
  277. package/dist/esm/devtools/FrappeDevtoolsPanel.mjs.map +1 -0
  278. package/dist/esm/devtools/FrappeDevtoolsPanel.native.js +880 -0
  279. package/dist/esm/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
  280. package/dist/esm/devtools/index.mjs +4 -0
  281. package/dist/esm/devtools/index.mjs.map +1 -0
  282. package/dist/esm/devtools/index.native.js +4 -0
  283. package/dist/esm/devtools/index.native.js.map +1 -0
  284. package/dist/esm/devtools/plugin.mjs +14 -0
  285. package/dist/esm/devtools/plugin.mjs.map +1 -0
  286. package/dist/esm/devtools/plugin.native.js +17 -0
  287. package/dist/esm/devtools/plugin.native.js.map +1 -0
  288. package/dist/esm/devtools/theme.mjs +30 -0
  289. package/dist/esm/devtools/theme.mjs.map +1 -0
  290. package/dist/esm/devtools/theme.native.js +30 -0
  291. package/dist/esm/devtools/theme.native.js.map +1 -0
  292. package/dist/esm/doctype.mjs +381 -0
  293. package/dist/esm/doctype.mjs.map +1 -0
  294. package/dist/esm/doctype.native.js +489 -0
  295. package/dist/esm/doctype.native.js.map +1 -0
  296. package/dist/esm/doctype.spec.mjs +461 -0
  297. package/dist/esm/doctype.spec.mjs.map +1 -0
  298. package/dist/esm/doctype.spec.native.js +536 -0
  299. package/dist/esm/doctype.spec.native.js.map +1 -0
  300. package/dist/esm/file.mjs +16 -0
  301. package/dist/esm/file.mjs.map +1 -0
  302. package/dist/esm/file.native.js +56 -0
  303. package/dist/esm/file.native.js.map +1 -0
  304. package/dist/esm/file.spec.mjs +316 -0
  305. package/dist/esm/file.spec.mjs.map +1 -0
  306. package/dist/esm/file.spec.native.js +340 -0
  307. package/dist/esm/file.spec.native.js.map +1 -0
  308. package/dist/esm/fixtureDb.mjs +187 -0
  309. package/dist/esm/fixtureDb.mjs.map +1 -0
  310. package/dist/esm/fixtureDb.native.js +285 -0
  311. package/dist/esm/fixtureDb.native.js.map +1 -0
  312. package/dist/esm/fixtures.mjs +174 -0
  313. package/dist/esm/fixtures.mjs.map +1 -0
  314. package/dist/esm/fixtures.native.js +372 -0
  315. package/dist/esm/fixtures.native.js.map +1 -0
  316. package/dist/esm/frappeApp.mjs +92 -0
  317. package/dist/esm/frappeApp.mjs.map +1 -0
  318. package/dist/esm/frappeApp.native.js +151 -0
  319. package/dist/esm/frappeApp.native.js.map +1 -0
  320. package/dist/esm/frappeApp.spec.mjs +307 -0
  321. package/dist/esm/frappeApp.spec.mjs.map +1 -0
  322. package/dist/esm/frappeApp.spec.native.js +306 -0
  323. package/dist/esm/frappeApp.spec.native.js.map +1 -0
  324. package/dist/esm/frappeReact.mjs +286 -0
  325. package/dist/esm/frappeReact.mjs.map +1 -0
  326. package/dist/esm/frappeReact.native.js +320 -0
  327. package/dist/esm/frappeReact.native.js.map +1 -0
  328. package/dist/esm/frappeReact.spec.mjs +532 -0
  329. package/dist/esm/frappeReact.spec.mjs.map +1 -0
  330. package/dist/esm/frappeReact.spec.native.js +668 -0
  331. package/dist/esm/frappeReact.spec.native.js.map +1 -0
  332. package/dist/esm/index.mjs +25 -0
  333. package/dist/esm/index.mjs.map +1 -0
  334. package/dist/esm/index.native.js +25 -0
  335. package/dist/esm/index.native.js.map +1 -0
  336. package/dist/esm/optimistic.mjs +3 -0
  337. package/dist/esm/optimistic.mjs.map +1 -0
  338. package/dist/esm/optimistic.native.js +3 -0
  339. package/dist/esm/optimistic.native.js.map +1 -0
  340. package/dist/esm/optimistic.spec.mjs +12 -0
  341. package/dist/esm/optimistic.spec.mjs.map +1 -0
  342. package/dist/esm/optimistic.spec.native.js +17 -0
  343. package/dist/esm/optimistic.spec.native.js.map +1 -0
  344. package/dist/esm/pacer.mjs +173 -0
  345. package/dist/esm/pacer.mjs.map +1 -0
  346. package/dist/esm/pacer.native.js +244 -0
  347. package/dist/esm/pacer.native.js.map +1 -0
  348. package/dist/esm/pacer.spec.mjs +447 -0
  349. package/dist/esm/pacer.spec.mjs.map +1 -0
  350. package/dist/esm/pacer.spec.native.js +511 -0
  351. package/dist/esm/pacer.spec.native.js.map +1 -0
  352. package/dist/esm/plugin/index.mjs +99 -0
  353. package/dist/esm/plugin/index.mjs.map +1 -0
  354. package/dist/esm/plugin/index.native.js +165 -0
  355. package/dist/esm/plugin/index.native.js.map +1 -0
  356. package/dist/esm/realtime.mjs +239 -0
  357. package/dist/esm/realtime.mjs.map +1 -0
  358. package/dist/esm/realtime.native.js +358 -0
  359. package/dist/esm/realtime.native.js.map +1 -0
  360. package/dist/esm/realtime.spec.mjs +327 -0
  361. package/dist/esm/realtime.spec.mjs.map +1 -0
  362. package/dist/esm/realtime.spec.native.js +390 -0
  363. package/dist/esm/realtime.spec.native.js.map +1 -0
  364. package/dist/esm/sync/backfill.mjs +287 -0
  365. package/dist/esm/sync/backfill.mjs.map +1 -0
  366. package/dist/esm/sync/backfill.native.js +457 -0
  367. package/dist/esm/sync/backfill.native.js.map +1 -0
  368. package/dist/esm/sync/backfill.spec.mjs +563 -0
  369. package/dist/esm/sync/backfill.spec.mjs.map +1 -0
  370. package/dist/esm/sync/backfill.spec.native.js +686 -0
  371. package/dist/esm/sync/backfill.spec.native.js.map +1 -0
  372. package/dist/esm/sync/debug.spec.mjs +102 -0
  373. package/dist/esm/sync/debug.spec.mjs.map +1 -0
  374. package/dist/esm/sync/debug.spec.native.js +107 -0
  375. package/dist/esm/sync/debug.spec.native.js.map +1 -0
  376. package/dist/esm/sync/filters.mjs +276 -0
  377. package/dist/esm/sync/filters.mjs.map +1 -0
  378. package/dist/esm/sync/filters.native.js +408 -0
  379. package/dist/esm/sync/filters.native.js.map +1 -0
  380. package/dist/esm/sync/filters.spec.mjs +169 -0
  381. package/dist/esm/sync/filters.spec.mjs.map +1 -0
  382. package/dist/esm/sync/filters.spec.native.js +171 -0
  383. package/dist/esm/sync/filters.spec.native.js.map +1 -0
  384. package/dist/esm/sync/index.mjs +278 -0
  385. package/dist/esm/sync/index.mjs.map +1 -0
  386. package/dist/esm/sync/index.native.js +434 -0
  387. package/dist/esm/sync/index.native.js.map +1 -0
  388. package/dist/esm/sync/merge.mjs +396 -0
  389. package/dist/esm/sync/merge.mjs.map +1 -0
  390. package/dist/esm/sync/merge.native.js +675 -0
  391. package/dist/esm/sync/merge.native.js.map +1 -0
  392. package/dist/esm/sync/merge.spec.mjs +909 -0
  393. package/dist/esm/sync/merge.spec.mjs.map +1 -0
  394. package/dist/esm/sync/merge.spec.native.js +935 -0
  395. package/dist/esm/sync/merge.spec.native.js.map +1 -0
  396. package/dist/esm/sync/multiOrderBy.spec.mjs +166 -0
  397. package/dist/esm/sync/multiOrderBy.spec.mjs.map +1 -0
  398. package/dist/esm/sync/multiOrderBy.spec.native.js +227 -0
  399. package/dist/esm/sync/multiOrderBy.spec.native.js.map +1 -0
  400. package/dist/esm/sync/mutationEngine.mjs +756 -0
  401. package/dist/esm/sync/mutationEngine.mjs.map +1 -0
  402. package/dist/esm/sync/mutationEngine.native.js +1131 -0
  403. package/dist/esm/sync/mutationEngine.native.js.map +1 -0
  404. package/dist/esm/sync/mutationEngine.spec.mjs +1159 -0
  405. package/dist/esm/sync/mutationEngine.spec.mjs.map +1 -0
  406. package/dist/esm/sync/mutationEngine.spec.native.js +1171 -0
  407. package/dist/esm/sync/mutationEngine.spec.native.js.map +1 -0
  408. package/dist/esm/sync/mutationJournal.mjs +124 -0
  409. package/dist/esm/sync/mutationJournal.mjs.map +1 -0
  410. package/dist/esm/sync/mutationJournal.native.js +253 -0
  411. package/dist/esm/sync/mutationJournal.native.js.map +1 -0
  412. package/dist/esm/sync/mutationJournal.spec.mjs +331 -0
  413. package/dist/esm/sync/mutationJournal.spec.mjs.map +1 -0
  414. package/dist/esm/sync/mutationJournal.spec.native.js +331 -0
  415. package/dist/esm/sync/mutationJournal.spec.native.js.map +1 -0
  416. package/dist/esm/sync/mutations.spec.mjs +328 -0
  417. package/dist/esm/sync/mutations.spec.mjs.map +1 -0
  418. package/dist/esm/sync/mutations.spec.native.js +328 -0
  419. package/dist/esm/sync/mutations.spec.native.js.map +1 -0
  420. package/dist/esm/sync/normalizedCache.mjs +115 -0
  421. package/dist/esm/sync/normalizedCache.mjs.map +1 -0
  422. package/dist/esm/sync/normalizedCache.native.js +201 -0
  423. package/dist/esm/sync/normalizedCache.native.js.map +1 -0
  424. package/dist/esm/sync/normalizedCache.spec.mjs +303 -0
  425. package/dist/esm/sync/normalizedCache.spec.mjs.map +1 -0
  426. package/dist/esm/sync/normalizedCache.spec.native.js +309 -0
  427. package/dist/esm/sync/normalizedCache.spec.native.js.map +1 -0
  428. package/dist/esm/sync/persistence.mjs +602 -0
  429. package/dist/esm/sync/persistence.mjs.map +1 -0
  430. package/dist/esm/sync/persistence.native.js +883 -0
  431. package/dist/esm/sync/persistence.native.js.map +1 -0
  432. package/dist/esm/sync/persistence.spec.mjs +563 -0
  433. package/dist/esm/sync/persistence.spec.mjs.map +1 -0
  434. package/dist/esm/sync/persistence.spec.native.js +570 -0
  435. package/dist/esm/sync/persistence.spec.native.js.map +1 -0
  436. package/dist/esm/sync/realtime.mjs +189 -0
  437. package/dist/esm/sync/realtime.mjs.map +1 -0
  438. package/dist/esm/sync/realtime.native.js +312 -0
  439. package/dist/esm/sync/realtime.native.js.map +1 -0
  440. package/dist/esm/sync/realtime.spec.mjs +500 -0
  441. package/dist/esm/sync/realtime.spec.mjs.map +1 -0
  442. package/dist/esm/sync/realtime.spec.native.js +510 -0
  443. package/dist/esm/sync/realtime.spec.native.js.map +1 -0
  444. package/dist/esm/sync/rehydration.mjs +298 -0
  445. package/dist/esm/sync/rehydration.mjs.map +1 -0
  446. package/dist/esm/sync/rehydration.native.js +503 -0
  447. package/dist/esm/sync/rehydration.native.js.map +1 -0
  448. package/dist/esm/sync/rehydration.spec.mjs +732 -0
  449. package/dist/esm/sync/rehydration.spec.mjs.map +1 -0
  450. package/dist/esm/sync/rehydration.spec.native.js +734 -0
  451. package/dist/esm/sync/rehydration.spec.native.js.map +1 -0
  452. package/dist/esm/sync/storage.mjs +116 -0
  453. package/dist/esm/sync/storage.mjs.map +1 -0
  454. package/dist/esm/sync/storage.native.js +180 -0
  455. package/dist/esm/sync/storage.native.js.map +1 -0
  456. package/dist/esm/sync/storage.spec.mjs +423 -0
  457. package/dist/esm/sync/storage.spec.mjs.map +1 -0
  458. package/dist/esm/sync/storage.spec.native.js +444 -0
  459. package/dist/esm/sync/storage.spec.native.js.map +1 -0
  460. package/dist/esm/sync/subscriptionManager.mjs +462 -0
  461. package/dist/esm/sync/subscriptionManager.mjs.map +1 -0
  462. package/dist/esm/sync/subscriptionManager.native.js +920 -0
  463. package/dist/esm/sync/subscriptionManager.native.js.map +1 -0
  464. package/dist/esm/sync/subscriptionManager.spec.mjs +1079 -0
  465. package/dist/esm/sync/subscriptionManager.spec.mjs.map +1 -0
  466. package/dist/esm/sync/subscriptionManager.spec.native.js +1110 -0
  467. package/dist/esm/sync/subscriptionManager.spec.native.js.map +1 -0
  468. package/dist/esm/sync/subscriptions.spec.mjs +914 -0
  469. package/dist/esm/sync/subscriptions.spec.mjs.map +1 -0
  470. package/dist/esm/sync/subscriptions.spec.native.js +943 -0
  471. package/dist/esm/sync/subscriptions.spec.native.js.map +1 -0
  472. package/dist/esm/sync/syncModule.spec.mjs +207 -0
  473. package/dist/esm/sync/syncModule.spec.mjs.map +1 -0
  474. package/dist/esm/sync/syncModule.spec.native.js +220 -0
  475. package/dist/esm/sync/syncModule.spec.native.js.map +1 -0
  476. package/dist/esm/sync/types.mjs +65 -0
  477. package/dist/esm/sync/types.mjs.map +1 -0
  478. package/dist/esm/sync/types.native.js +65 -0
  479. package/dist/esm/sync/types.native.js.map +1 -0
  480. package/dist/esm/sync/types.spec.mjs +247 -0
  481. package/dist/esm/sync/types.spec.mjs.map +1 -0
  482. package/dist/esm/sync/types.spec.native.js +254 -0
  483. package/dist/esm/sync/types.spec.native.js.map +1 -0
  484. package/dist/esm/sync/virtualPageManager.mjs +794 -0
  485. package/dist/esm/sync/virtualPageManager.mjs.map +1 -0
  486. package/dist/esm/sync/virtualPageManager.native.js +1095 -0
  487. package/dist/esm/sync/virtualPageManager.native.js.map +1 -0
  488. package/dist/esm/sync/virtualPageManager.spec.mjs +802 -0
  489. package/dist/esm/sync/virtualPageManager.spec.mjs.map +1 -0
  490. package/dist/esm/sync/virtualPageManager.spec.native.js +871 -0
  491. package/dist/esm/sync/virtualPageManager.spec.native.js.map +1 -0
  492. package/dist/esm/util.mjs +6 -0
  493. package/dist/esm/util.mjs.map +1 -0
  494. package/dist/esm/util.native.js +6 -0
  495. package/dist/esm/util.native.js.map +1 -0
  496. package/dist/esm/util.spec.mjs +20 -0
  497. package/dist/esm/util.spec.mjs.map +1 -0
  498. package/dist/esm/util.spec.native.js +22 -0
  499. package/dist/esm/util.spec.native.js.map +1 -0
  500. package/dist/jsx/auth.mjs +25 -0
  501. package/dist/jsx/auth.mjs.map +1 -0
  502. package/dist/jsx/auth.native.js +100 -0
  503. package/dist/jsx/auth.native.js.map +1 -0
  504. package/dist/jsx/auth.spec.mjs +323 -0
  505. package/dist/jsx/auth.spec.mjs.map +1 -0
  506. package/dist/jsx/auth.spec.native.js +329 -0
  507. package/dist/jsx/auth.spec.native.js.map +1 -0
  508. package/dist/jsx/call.mjs +20 -0
  509. package/dist/jsx/call.mjs.map +1 -0
  510. package/dist/jsx/call.native.js +93 -0
  511. package/dist/jsx/call.native.js.map +1 -0
  512. package/dist/jsx/call.spec.mjs +428 -0
  513. package/dist/jsx/call.spec.mjs.map +1 -0
  514. package/dist/jsx/call.spec.native.js +430 -0
  515. package/dist/jsx/call.spec.native.js.map +1 -0
  516. package/dist/jsx/collection.mjs +250 -0
  517. package/dist/jsx/collection.mjs.map +1 -0
  518. package/dist/jsx/collection.native.js +494 -0
  519. package/dist/jsx/collection.native.js.map +1 -0
  520. package/dist/jsx/collection.spec.mjs +187 -0
  521. package/dist/jsx/collection.spec.mjs.map +1 -0
  522. package/dist/jsx/collection.spec.native.js +216 -0
  523. package/dist/jsx/collection.spec.native.js.map +1 -0
  524. package/dist/jsx/common/types.mjs +2 -0
  525. package/dist/jsx/common/types.mjs.map +1 -0
  526. package/dist/jsx/common/types.native.js +21 -0
  527. package/dist/jsx/common/types.native.js.map +1 -0
  528. package/dist/jsx/common/types.spec.mjs +267 -0
  529. package/dist/jsx/common/types.spec.mjs.map +1 -0
  530. package/dist/jsx/common/types.spec.native.js +271 -0
  531. package/dist/jsx/common/types.spec.native.js.map +1 -0
  532. package/dist/jsx/core/http.mjs +258 -0
  533. package/dist/jsx/core/http.mjs.map +1 -0
  534. package/dist/jsx/core/http.native.js +484 -0
  535. package/dist/jsx/core/http.native.js.map +1 -0
  536. package/dist/jsx/core/http.spec.mjs +1260 -0
  537. package/dist/jsx/core/http.spec.mjs.map +1 -0
  538. package/dist/jsx/core/http.spec.native.js +1401 -0
  539. package/dist/jsx/core/http.spec.native.js.map +1 -0
  540. package/dist/jsx/core/types.mjs +26 -0
  541. package/dist/jsx/core/types.mjs.map +1 -0
  542. package/dist/jsx/core/types.native.js +178 -0
  543. package/dist/jsx/core/types.native.js.map +1 -0
  544. package/dist/jsx/core/wire.mjs +23 -0
  545. package/dist/jsx/core/wire.mjs.map +1 -0
  546. package/dist/jsx/core/wire.native.js +56 -0
  547. package/dist/jsx/core/wire.native.js.map +1 -0
  548. package/dist/jsx/core/wire.spec.mjs +479 -0
  549. package/dist/jsx/core/wire.spec.mjs.map +1 -0
  550. package/dist/jsx/core/wire.spec.native.js +482 -0
  551. package/dist/jsx/core/wire.spec.native.js.map +1 -0
  552. package/dist/jsx/db.mjs +329 -0
  553. package/dist/jsx/db.mjs.map +1 -0
  554. package/dist/jsx/db.native.js +469 -0
  555. package/dist/jsx/db.native.js.map +1 -0
  556. package/dist/jsx/db.spec.mjs +863 -0
  557. package/dist/jsx/db.spec.mjs.map +1 -0
  558. package/dist/jsx/db.spec.native.js +865 -0
  559. package/dist/jsx/db.spec.native.js.map +1 -0
  560. package/dist/jsx/devtools/FrappeDevtoolsPanel.mjs +813 -0
  561. package/dist/jsx/devtools/FrappeDevtoolsPanel.mjs.map +1 -0
  562. package/dist/jsx/devtools/FrappeDevtoolsPanel.native.js +919 -0
  563. package/dist/jsx/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
  564. package/dist/jsx/devtools/index.mjs +4 -0
  565. package/dist/jsx/devtools/index.mjs.map +1 -0
  566. package/dist/jsx/devtools/index.native.js +33 -0
  567. package/dist/jsx/devtools/index.native.js.map +1 -0
  568. package/dist/jsx/devtools/plugin.mjs +14 -0
  569. package/dist/jsx/devtools/plugin.mjs.map +1 -0
  570. package/dist/jsx/devtools/plugin.native.js +56 -0
  571. package/dist/jsx/devtools/plugin.native.js.map +1 -0
  572. package/dist/jsx/devtools/theme.mjs +30 -0
  573. package/dist/jsx/devtools/theme.mjs.map +1 -0
  574. package/dist/jsx/devtools/theme.native.js +58 -0
  575. package/dist/jsx/devtools/theme.native.js.map +1 -0
  576. package/dist/jsx/doctype.mjs +381 -0
  577. package/dist/jsx/doctype.mjs.map +1 -0
  578. package/dist/jsx/doctype.native.js +521 -0
  579. package/dist/jsx/doctype.native.js.map +1 -0
  580. package/dist/jsx/doctype.spec.mjs +461 -0
  581. package/dist/jsx/doctype.spec.mjs.map +1 -0
  582. package/dist/jsx/doctype.spec.native.js +538 -0
  583. package/dist/jsx/doctype.spec.native.js.map +1 -0
  584. package/dist/jsx/file.mjs +16 -0
  585. package/dist/jsx/file.mjs.map +1 -0
  586. package/dist/jsx/file.native.js +84 -0
  587. package/dist/jsx/file.native.js.map +1 -0
  588. package/dist/jsx/file.spec.mjs +316 -0
  589. package/dist/jsx/file.spec.mjs.map +1 -0
  590. package/dist/jsx/file.spec.native.js +342 -0
  591. package/dist/jsx/file.spec.native.js.map +1 -0
  592. package/dist/jsx/fixtureDb.mjs +187 -0
  593. package/dist/jsx/fixtureDb.mjs.map +1 -0
  594. package/dist/jsx/fixtureDb.native.js +313 -0
  595. package/dist/jsx/fixtureDb.native.js.map +1 -0
  596. package/dist/jsx/fixtures.mjs +174 -0
  597. package/dist/jsx/fixtures.mjs.map +1 -0
  598. package/dist/jsx/fixtures.native.js +400 -0
  599. package/dist/jsx/fixtures.native.js.map +1 -0
  600. package/dist/jsx/frappeApp.mjs +92 -0
  601. package/dist/jsx/frappeApp.mjs.map +1 -0
  602. package/dist/jsx/frappeApp.native.js +179 -0
  603. package/dist/jsx/frappeApp.native.js.map +1 -0
  604. package/dist/jsx/frappeApp.spec.mjs +307 -0
  605. package/dist/jsx/frappeApp.spec.mjs.map +1 -0
  606. package/dist/jsx/frappeApp.spec.native.js +308 -0
  607. package/dist/jsx/frappeApp.spec.native.js.map +1 -0
  608. package/dist/jsx/frappeReact.mjs +286 -0
  609. package/dist/jsx/frappeReact.mjs.map +1 -0
  610. package/dist/jsx/frappeReact.native.js +351 -0
  611. package/dist/jsx/frappeReact.native.js.map +1 -0
  612. package/dist/jsx/frappeReact.spec.mjs +532 -0
  613. package/dist/jsx/frappeReact.spec.mjs.map +1 -0
  614. package/dist/jsx/frappeReact.spec.native.js +670 -0
  615. package/dist/jsx/frappeReact.spec.native.js.map +1 -0
  616. package/dist/jsx/index.js +25 -0
  617. package/dist/jsx/index.js.map +1 -0
  618. package/dist/jsx/index.mjs +25 -0
  619. package/dist/jsx/index.mjs.map +1 -0
  620. package/dist/jsx/index.native.js +152 -0
  621. package/dist/jsx/index.native.js.map +1 -0
  622. package/dist/jsx/optimistic.mjs +3 -0
  623. package/dist/jsx/optimistic.mjs.map +1 -0
  624. package/dist/jsx/optimistic.native.js +31 -0
  625. package/dist/jsx/optimistic.native.js.map +1 -0
  626. package/dist/jsx/optimistic.spec.mjs +12 -0
  627. package/dist/jsx/optimistic.spec.mjs.map +1 -0
  628. package/dist/jsx/optimistic.spec.native.js +19 -0
  629. package/dist/jsx/optimistic.spec.native.js.map +1 -0
  630. package/dist/jsx/pacer.mjs +173 -0
  631. package/dist/jsx/pacer.mjs.map +1 -0
  632. package/dist/jsx/pacer.native.js +274 -0
  633. package/dist/jsx/pacer.native.js.map +1 -0
  634. package/dist/jsx/pacer.spec.mjs +447 -0
  635. package/dist/jsx/pacer.spec.mjs.map +1 -0
  636. package/dist/jsx/pacer.spec.native.js +537 -0
  637. package/dist/jsx/pacer.spec.native.js.map +1 -0
  638. package/dist/jsx/plugin/index.mjs +99 -0
  639. package/dist/jsx/plugin/index.mjs.map +1 -0
  640. package/dist/jsx/plugin/index.native.js +193 -0
  641. package/dist/jsx/plugin/index.native.js.map +1 -0
  642. package/dist/jsx/realtime.mjs +239 -0
  643. package/dist/jsx/realtime.mjs.map +1 -0
  644. package/dist/jsx/realtime.native.js +387 -0
  645. package/dist/jsx/realtime.native.js.map +1 -0
  646. package/dist/jsx/realtime.spec.mjs +327 -0
  647. package/dist/jsx/realtime.spec.mjs.map +1 -0
  648. package/dist/jsx/realtime.spec.native.js +392 -0
  649. package/dist/jsx/realtime.spec.native.js.map +1 -0
  650. package/dist/jsx/sync/backfill.mjs +287 -0
  651. package/dist/jsx/sync/backfill.mjs.map +1 -0
  652. package/dist/jsx/sync/backfill.native.js +487 -0
  653. package/dist/jsx/sync/backfill.native.js.map +1 -0
  654. package/dist/jsx/sync/backfill.spec.mjs +563 -0
  655. package/dist/jsx/sync/backfill.spec.mjs.map +1 -0
  656. package/dist/jsx/sync/backfill.spec.native.js +712 -0
  657. package/dist/jsx/sync/backfill.spec.native.js.map +1 -0
  658. package/dist/jsx/sync/debug.spec.mjs +102 -0
  659. package/dist/jsx/sync/debug.spec.mjs.map +1 -0
  660. package/dist/jsx/sync/debug.spec.native.js +133 -0
  661. package/dist/jsx/sync/debug.spec.native.js.map +1 -0
  662. package/dist/jsx/sync/filters.mjs +276 -0
  663. package/dist/jsx/sync/filters.mjs.map +1 -0
  664. package/dist/jsx/sync/filters.native.js +446 -0
  665. package/dist/jsx/sync/filters.native.js.map +1 -0
  666. package/dist/jsx/sync/filters.spec.mjs +169 -0
  667. package/dist/jsx/sync/filters.spec.mjs.map +1 -0
  668. package/dist/jsx/sync/filters.spec.native.js +173 -0
  669. package/dist/jsx/sync/filters.spec.native.js.map +1 -0
  670. package/dist/jsx/sync/index.mjs +278 -0
  671. package/dist/jsx/sync/index.mjs.map +1 -0
  672. package/dist/jsx/sync/index.native.js +462 -0
  673. package/dist/jsx/sync/index.native.js.map +1 -0
  674. package/dist/jsx/sync/merge.mjs +396 -0
  675. package/dist/jsx/sync/merge.mjs.map +1 -0
  676. package/dist/jsx/sync/merge.native.js +717 -0
  677. package/dist/jsx/sync/merge.native.js.map +1 -0
  678. package/dist/jsx/sync/merge.spec.mjs +909 -0
  679. package/dist/jsx/sync/merge.spec.mjs.map +1 -0
  680. package/dist/jsx/sync/merge.spec.native.js +937 -0
  681. package/dist/jsx/sync/merge.spec.native.js.map +1 -0
  682. package/dist/jsx/sync/multiOrderBy.spec.mjs +166 -0
  683. package/dist/jsx/sync/multiOrderBy.spec.mjs.map +1 -0
  684. package/dist/jsx/sync/multiOrderBy.spec.native.js +229 -0
  685. package/dist/jsx/sync/multiOrderBy.spec.native.js.map +1 -0
  686. package/dist/jsx/sync/mutationEngine.mjs +756 -0
  687. package/dist/jsx/sync/mutationEngine.mjs.map +1 -0
  688. package/dist/jsx/sync/mutationEngine.native.js +1159 -0
  689. package/dist/jsx/sync/mutationEngine.native.js.map +1 -0
  690. package/dist/jsx/sync/mutationEngine.spec.mjs +1159 -0
  691. package/dist/jsx/sync/mutationEngine.spec.mjs.map +1 -0
  692. package/dist/jsx/sync/mutationEngine.spec.native.js +1173 -0
  693. package/dist/jsx/sync/mutationEngine.spec.native.js.map +1 -0
  694. package/dist/jsx/sync/mutationJournal.mjs +124 -0
  695. package/dist/jsx/sync/mutationJournal.mjs.map +1 -0
  696. package/dist/jsx/sync/mutationJournal.native.js +282 -0
  697. package/dist/jsx/sync/mutationJournal.native.js.map +1 -0
  698. package/dist/jsx/sync/mutationJournal.spec.mjs +331 -0
  699. package/dist/jsx/sync/mutationJournal.spec.mjs.map +1 -0
  700. package/dist/jsx/sync/mutationJournal.spec.native.js +333 -0
  701. package/dist/jsx/sync/mutationJournal.spec.native.js.map +1 -0
  702. package/dist/jsx/sync/mutations.spec.mjs +328 -0
  703. package/dist/jsx/sync/mutations.spec.mjs.map +1 -0
  704. package/dist/jsx/sync/mutations.spec.native.js +330 -0
  705. package/dist/jsx/sync/mutations.spec.native.js.map +1 -0
  706. package/dist/jsx/sync/normalizedCache.mjs +115 -0
  707. package/dist/jsx/sync/normalizedCache.mjs.map +1 -0
  708. package/dist/jsx/sync/normalizedCache.native.js +229 -0
  709. package/dist/jsx/sync/normalizedCache.native.js.map +1 -0
  710. package/dist/jsx/sync/normalizedCache.spec.mjs +303 -0
  711. package/dist/jsx/sync/normalizedCache.spec.mjs.map +1 -0
  712. package/dist/jsx/sync/normalizedCache.spec.native.js +311 -0
  713. package/dist/jsx/sync/normalizedCache.spec.native.js.map +1 -0
  714. package/dist/jsx/sync/persistence.mjs +602 -0
  715. package/dist/jsx/sync/persistence.mjs.map +1 -0
  716. package/dist/jsx/sync/persistence.native.js +911 -0
  717. package/dist/jsx/sync/persistence.native.js.map +1 -0
  718. package/dist/jsx/sync/persistence.spec.mjs +563 -0
  719. package/dist/jsx/sync/persistence.spec.mjs.map +1 -0
  720. package/dist/jsx/sync/persistence.spec.native.js +572 -0
  721. package/dist/jsx/sync/persistence.spec.native.js.map +1 -0
  722. package/dist/jsx/sync/realtime.mjs +189 -0
  723. package/dist/jsx/sync/realtime.mjs.map +1 -0
  724. package/dist/jsx/sync/realtime.native.js +340 -0
  725. package/dist/jsx/sync/realtime.native.js.map +1 -0
  726. package/dist/jsx/sync/realtime.spec.mjs +500 -0
  727. package/dist/jsx/sync/realtime.spec.mjs.map +1 -0
  728. package/dist/jsx/sync/realtime.spec.native.js +512 -0
  729. package/dist/jsx/sync/realtime.spec.native.js.map +1 -0
  730. package/dist/jsx/sync/rehydration.mjs +298 -0
  731. package/dist/jsx/sync/rehydration.mjs.map +1 -0
  732. package/dist/jsx/sync/rehydration.native.js +531 -0
  733. package/dist/jsx/sync/rehydration.native.js.map +1 -0
  734. package/dist/jsx/sync/rehydration.spec.mjs +732 -0
  735. package/dist/jsx/sync/rehydration.spec.mjs.map +1 -0
  736. package/dist/jsx/sync/rehydration.spec.native.js +736 -0
  737. package/dist/jsx/sync/rehydration.spec.native.js.map +1 -0
  738. package/dist/jsx/sync/storage.mjs +116 -0
  739. package/dist/jsx/sync/storage.mjs.map +1 -0
  740. package/dist/jsx/sync/storage.native.js +209 -0
  741. package/dist/jsx/sync/storage.native.js.map +1 -0
  742. package/dist/jsx/sync/storage.spec.mjs +423 -0
  743. package/dist/jsx/sync/storage.spec.mjs.map +1 -0
  744. package/dist/jsx/sync/storage.spec.native.js +470 -0
  745. package/dist/jsx/sync/storage.spec.native.js.map +1 -0
  746. package/dist/jsx/sync/subscriptionManager.mjs +462 -0
  747. package/dist/jsx/sync/subscriptionManager.mjs.map +1 -0
  748. package/dist/jsx/sync/subscriptionManager.native.js +948 -0
  749. package/dist/jsx/sync/subscriptionManager.native.js.map +1 -0
  750. package/dist/jsx/sync/subscriptionManager.spec.mjs +1079 -0
  751. package/dist/jsx/sync/subscriptionManager.spec.mjs.map +1 -0
  752. package/dist/jsx/sync/subscriptionManager.spec.native.js +1136 -0
  753. package/dist/jsx/sync/subscriptionManager.spec.native.js.map +1 -0
  754. package/dist/jsx/sync/subscriptions.spec.mjs +914 -0
  755. package/dist/jsx/sync/subscriptions.spec.mjs.map +1 -0
  756. package/dist/jsx/sync/subscriptions.spec.native.js +969 -0
  757. package/dist/jsx/sync/subscriptions.spec.native.js.map +1 -0
  758. package/dist/jsx/sync/syncModule.spec.mjs +207 -0
  759. package/dist/jsx/sync/syncModule.spec.mjs.map +1 -0
  760. package/dist/jsx/sync/syncModule.spec.native.js +222 -0
  761. package/dist/jsx/sync/syncModule.spec.native.js.map +1 -0
  762. package/dist/jsx/sync/types.mjs +65 -0
  763. package/dist/jsx/sync/types.mjs.map +1 -0
  764. package/dist/jsx/sync/types.native.js +102 -0
  765. package/dist/jsx/sync/types.native.js.map +1 -0
  766. package/dist/jsx/sync/types.spec.mjs +247 -0
  767. package/dist/jsx/sync/types.spec.mjs.map +1 -0
  768. package/dist/jsx/sync/types.spec.native.js +256 -0
  769. package/dist/jsx/sync/types.spec.native.js.map +1 -0
  770. package/dist/jsx/sync/virtualPageManager.mjs +794 -0
  771. package/dist/jsx/sync/virtualPageManager.mjs.map +1 -0
  772. package/dist/jsx/sync/virtualPageManager.native.js +1125 -0
  773. package/dist/jsx/sync/virtualPageManager.native.js.map +1 -0
  774. package/dist/jsx/sync/virtualPageManager.spec.mjs +802 -0
  775. package/dist/jsx/sync/virtualPageManager.spec.mjs.map +1 -0
  776. package/dist/jsx/sync/virtualPageManager.spec.native.js +873 -0
  777. package/dist/jsx/sync/virtualPageManager.spec.native.js.map +1 -0
  778. package/dist/jsx/util.mjs +6 -0
  779. package/dist/jsx/util.mjs.map +1 -0
  780. package/dist/jsx/util.native.js +34 -0
  781. package/dist/jsx/util.native.js.map +1 -0
  782. package/dist/jsx/util.spec.mjs +20 -0
  783. package/dist/jsx/util.spec.mjs.map +1 -0
  784. package/dist/jsx/util.spec.native.js +48 -0
  785. package/dist/jsx/util.spec.native.js.map +1 -0
  786. package/package.json +96 -0
  787. package/src/auth.spec.ts +363 -0
  788. package/src/auth.ts +29 -0
  789. package/src/call.spec.ts +541 -0
  790. package/src/call.ts +21 -0
  791. package/src/collection.spec.ts +213 -0
  792. package/src/collection.ts +328 -0
  793. package/src/common/types.spec.ts +294 -0
  794. package/src/common/types.ts +41 -0
  795. package/src/core/http.spec.ts +1128 -0
  796. package/src/core/http.ts +324 -0
  797. package/src/core/types.ts +85 -0
  798. package/src/core/wire.spec.ts +570 -0
  799. package/src/core/wire.ts +487 -0
  800. package/src/db.spec.ts +1004 -0
  801. package/src/db.ts +589 -0
  802. package/src/devtools/FrappeDevtoolsPanel.tsx +873 -0
  803. package/src/devtools/index.ts +3 -0
  804. package/src/devtools/plugin.tsx +13 -0
  805. package/src/devtools/theme.ts +42 -0
  806. package/src/doctype.spec.ts +451 -0
  807. package/src/doctype.ts +828 -0
  808. package/src/file.spec.ts +347 -0
  809. package/src/file.ts +27 -0
  810. package/src/fixtureDb.ts +208 -0
  811. package/src/fixtures.ts +268 -0
  812. package/src/frappeApp.spec.ts +382 -0
  813. package/src/frappeApp.ts +125 -0
  814. package/src/frappeReact.spec.ts +505 -0
  815. package/src/frappeReact.ts +598 -0
  816. package/src/index.ts +215 -0
  817. package/src/optimistic.spec.ts +13 -0
  818. package/src/optimistic.ts +39 -0
  819. package/src/pacer.spec.ts +493 -0
  820. package/src/pacer.ts +426 -0
  821. package/src/plugin/README.md +162 -0
  822. package/src/plugin/index.ts +141 -0
  823. package/src/realtime.spec.ts +453 -0
  824. package/src/realtime.ts +279 -0
  825. package/src/sync/backfill.spec.ts +714 -0
  826. package/src/sync/backfill.ts +378 -0
  827. package/src/sync/debug.spec.ts +110 -0
  828. package/src/sync/filters.spec.ts +223 -0
  829. package/src/sync/filters.ts +513 -0
  830. package/src/sync/index.ts +412 -0
  831. package/src/sync/merge.spec.ts +812 -0
  832. package/src/sync/merge.ts +532 -0
  833. package/src/sync/multiOrderBy.spec.ts +187 -0
  834. package/src/sync/mutationEngine.spec.ts +1418 -0
  835. package/src/sync/mutationEngine.ts +957 -0
  836. package/src/sync/mutationJournal.spec.ts +337 -0
  837. package/src/sync/mutationJournal.ts +157 -0
  838. package/src/sync/mutations.spec.ts +358 -0
  839. package/src/sync/normalizedCache.spec.ts +262 -0
  840. package/src/sync/normalizedCache.ts +152 -0
  841. package/src/sync/persistence.spec.ts +908 -0
  842. package/src/sync/persistence.ts +802 -0
  843. package/src/sync/realtime.spec.ts +769 -0
  844. package/src/sync/realtime.ts +259 -0
  845. package/src/sync/rehydration.spec.ts +923 -0
  846. package/src/sync/rehydration.ts +362 -0
  847. package/src/sync/storage.spec.ts +473 -0
  848. package/src/sync/storage.ts +174 -0
  849. package/src/sync/subscriptionManager.spec.ts +1226 -0
  850. package/src/sync/subscriptionManager.ts +577 -0
  851. package/src/sync/subscriptions.spec.ts +1048 -0
  852. package/src/sync/syncModule.spec.ts +217 -0
  853. package/src/sync/types.spec.ts +276 -0
  854. package/src/sync/types.ts +384 -0
  855. package/src/sync/virtualPageManager.spec.ts +825 -0
  856. package/src/sync/virtualPageManager.ts +1069 -0
  857. package/src/util.spec.ts +17 -0
  858. package/src/util.ts +5 -0
@@ -0,0 +1,1418 @@
1
+ import type { MockedObject } from "vitest";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import type { FrappeDoc, FrappeDocData } from "../common/types";
4
+ import type { DbModule } from "../db";
5
+ import type { RealtimeModule } from "../realtime";
6
+ import { MutationEngine } from "./mutationEngine";
7
+ import type { MutationJournal, Pending } from "./mutationJournal";
8
+ import type { NormalizedCache } from "./normalizedCache";
9
+ import type { RehydrationManager } from "./rehydration";
10
+ import type { SubscriptionManager } from "./subscriptionManager";
11
+ import { RehydrationReason } from "./types";
12
+ import { Operation, Status } from "./types";
13
+ import type {
14
+ CreateMutation,
15
+ DeleteMutation,
16
+ SyncOptions,
17
+ Subscription,
18
+ UpdateMutation,
19
+ } from "./types";
20
+
21
+ describe("MutationEngine", () => {
22
+ let mutationEngine: MutationEngine;
23
+ let mockDb: MockedObject<DbModule>;
24
+ let mockRealtime: MockedObject<RealtimeModule>;
25
+ let mockNormalizedCache: MockedObject<NormalizedCache>;
26
+ let mockMutationJournal: MockedObject<MutationJournal>;
27
+ let mockSubscriptionManager: MockedObject<SubscriptionManager>;
28
+ let mockRehydrationManager: MockedObject<RehydrationManager>;
29
+
30
+ const mockDoc: FrappeDoc<FrappeDocData> = {
31
+ name: "test-doc",
32
+ doctype: "TestDoc",
33
+ title: "Test Document",
34
+ modified: "2024-01-01T10:00:00",
35
+ creation: "2024-01-01T09:00:00",
36
+ owner: "test-user",
37
+ modified_by: "test-user",
38
+ };
39
+
40
+ beforeEach(() => {
41
+ vi.useFakeTimers();
42
+
43
+ // Mock database module
44
+ const mockCall = {
45
+ post: vi.fn().mockResolvedValue({ results: [] }),
46
+ };
47
+ mockDb = {
48
+ getDoc: vi.fn().mockResolvedValue(mockDoc),
49
+ createDoc: vi.fn().mockResolvedValue({ ...mockDoc, name: "real-doc" }),
50
+ updateDoc: vi.fn().mockResolvedValue({ ...mockDoc, title: "Updated" }),
51
+ deleteDoc: vi.fn().mockResolvedValue(undefined),
52
+ call: mockCall,
53
+ } as any;
54
+
55
+ // Mock realtime module
56
+ mockRealtime = {
57
+ isHealthy: vi.fn().mockReturnValue(true),
58
+ } as any;
59
+
60
+ // Mock normalized cache
61
+ mockNormalizedCache = {
62
+ get: vi.fn().mockReturnValue(mockDoc),
63
+ upsert: vi.fn(),
64
+ remove: vi.fn(),
65
+ } as any;
66
+
67
+ // Mock mutation journal
68
+ mockMutationJournal = {
69
+ add: vi.fn(),
70
+ confirm: vi.fn(),
71
+ fail: vi.fn(),
72
+ get: vi.fn().mockReturnValue({
73
+ doctype: "TestDoc",
74
+ docname: "test-doc",
75
+ values: mockDoc,
76
+ operation: Operation.Update,
77
+ version: "2024-01-01T10:00:00",
78
+ status: Status.Pending,
79
+ snapshot: mockDoc,
80
+ }),
81
+ listPending: vi.fn().mockReturnValue([]),
82
+ } as any;
83
+
84
+ // Mock subscription manager
85
+ const mockSubscription: Subscription = {
86
+ id: "sub1",
87
+ query: {
88
+ doctype: "TestDoc",
89
+ fields: ["*"],
90
+ filters: {},
91
+ limit: 20,
92
+ },
93
+ docIds: ["doc1", "doc2"],
94
+ checkpoint: "checkpoint1",
95
+ metadata: {},
96
+ };
97
+
98
+ mockSubscriptionManager = {
99
+ getAllSubscriptions: vi.fn().mockReturnValue(new Map([["sub1", mockSubscription]])),
100
+ updateSubscriptionMembership: vi.fn().mockReturnValue([]),
101
+ removeFromAllSubscriptions: vi.fn().mockReturnValue(["sub1"]),
102
+ insertDocumentIntoSubscription: vi.fn(),
103
+ } as any;
104
+
105
+ // Mock rehydration manager
106
+ mockRehydrationManager = {
107
+ scheduleRehydration: vi.fn(),
108
+ registerTempIdMapping: vi.fn(),
109
+ } as any;
110
+
111
+ // Mock global objects
112
+ Object.defineProperty(global, "crypto", {
113
+ value: {
114
+ randomUUID: vi.fn().mockReturnValue("uuid-12345"),
115
+ },
116
+ writable: true,
117
+ });
118
+
119
+ Object.defineProperty(global, "navigator", {
120
+ value: {
121
+ onLine: true,
122
+ },
123
+ writable: true,
124
+ });
125
+
126
+ // Mock console methods
127
+ vi.spyOn(console, "warn").mockImplementation(() => {});
128
+ vi.spyOn(console, "error").mockImplementation(() => {});
129
+ });
130
+
131
+ afterEach(() => {
132
+ vi.restoreAllMocks();
133
+ vi.useRealTimers();
134
+ });
135
+
136
+ describe("constructor", () => {
137
+ it("should initialize with default options", () => {
138
+ mutationEngine = new MutationEngine(
139
+ mockDb,
140
+ mockRealtime,
141
+ mockNormalizedCache,
142
+ mockMutationJournal,
143
+ mockSubscriptionManager,
144
+ mockRehydrationManager,
145
+ );
146
+
147
+ expect(mutationEngine).toBeDefined();
148
+ });
149
+
150
+ it("should initialize with custom options", () => {
151
+ const options: SyncOptions = {
152
+ batchSize: 20,
153
+ batchDelay: 200,
154
+ versionField: "timestamp",
155
+ };
156
+
157
+ mutationEngine = new MutationEngine(
158
+ mockDb,
159
+ mockRealtime,
160
+ mockNormalizedCache,
161
+ mockMutationJournal,
162
+ mockSubscriptionManager,
163
+ mockRehydrationManager,
164
+ options,
165
+ );
166
+
167
+ expect(mutationEngine).toBeDefined();
168
+ });
169
+
170
+ it("should handle missing realtime module", () => {
171
+ mutationEngine = new MutationEngine(
172
+ mockDb,
173
+ undefined,
174
+ mockNormalizedCache,
175
+ mockMutationJournal,
176
+ mockSubscriptionManager,
177
+ mockRehydrationManager,
178
+ );
179
+
180
+ expect(mutationEngine).toBeDefined();
181
+ });
182
+ });
183
+
184
+ describe("mutate", () => {
185
+ beforeEach(() => {
186
+ mutationEngine = new MutationEngine(
187
+ mockDb,
188
+ mockRealtime,
189
+ mockNormalizedCache,
190
+ mockMutationJournal,
191
+ mockSubscriptionManager,
192
+ mockRehydrationManager,
193
+ {},
194
+ );
195
+ });
196
+
197
+ describe("create mutations", () => {
198
+ it("should handle create mutations successfully", async () => {
199
+ const mutation: CreateMutation<FrappeDocData> = {
200
+ mutationId: "mut1",
201
+ operation: Operation.Create,
202
+ doc: {
203
+ doctype: "TestDoc",
204
+ name: "temp-doc",
205
+ title: "New Document",
206
+ },
207
+ version: undefined,
208
+ affected: [],
209
+ };
210
+
211
+ await mutationEngine.mutate(mutation);
212
+
213
+ expect(mockNormalizedCache.upsert).toHaveBeenCalledWith(
214
+ expect.objectContaining({
215
+ doctype: "TestDoc",
216
+ name: "temp-doc",
217
+ title: "New Document",
218
+ }),
219
+ );
220
+ // createDoc now takes a single doc object
221
+ expect(mockDb.createDoc).toHaveBeenCalledWith(
222
+ expect.objectContaining({
223
+ doctype: "TestDoc",
224
+ }),
225
+ );
226
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut1");
227
+ });
228
+
229
+ it("should handle create without provided name", async () => {
230
+ const mutation: CreateMutation<FrappeDocData> = {
231
+ mutationId: "mut2",
232
+ operation: Operation.Create,
233
+ doc: {
234
+ doctype: "TestDoc",
235
+ title: "New Document",
236
+ } as any,
237
+ version: undefined,
238
+ affected: [],
239
+ };
240
+
241
+ await mutationEngine.mutate(mutation);
242
+
243
+ expect(mockNormalizedCache.upsert).toHaveBeenCalledWith(
244
+ expect.objectContaining({
245
+ doctype: "TestDoc",
246
+ name: expect.stringContaining("tmp_"),
247
+ title: "New Document",
248
+ }),
249
+ );
250
+ });
251
+
252
+ it("should handle temporary to real ID mapping", async () => {
253
+ const mutation: CreateMutation<FrappeDocData> = {
254
+ mutationId: "mut3",
255
+ operation: Operation.Create,
256
+ doc: {
257
+ doctype: "TestDoc",
258
+ name: "temp-doc",
259
+ title: "New Document",
260
+ },
261
+ version: undefined,
262
+ affected: [],
263
+ };
264
+
265
+ mockDb.createDoc.mockResolvedValue({
266
+ ...mockDoc,
267
+ name: "real-doc-123",
268
+ });
269
+
270
+ await mutationEngine.mutate(mutation);
271
+
272
+ expect(mockRehydrationManager.registerTempIdMapping).toHaveBeenCalledWith(
273
+ "temp-doc",
274
+ "real-doc-123",
275
+ );
276
+ expect(mockNormalizedCache.remove).toHaveBeenCalledWith({
277
+ doctype: "TestDoc",
278
+ name: "temp-doc",
279
+ });
280
+ });
281
+
282
+ it("should schedule rehydration for affected subscriptions", async () => {
283
+ const mutation: CreateMutation<FrappeDocData> = {
284
+ mutationId: "mut4",
285
+ operation: Operation.Create,
286
+ doc: {
287
+ doctype: "TestDoc",
288
+ name: "temp-doc",
289
+ title: "New Document",
290
+ },
291
+ version: undefined,
292
+ affected: [],
293
+ };
294
+
295
+ await mutationEngine.mutate(mutation);
296
+
297
+ expect(mockRehydrationManager.scheduleRehydration).toHaveBeenCalledWith(
298
+ expect.any(Object),
299
+ RehydrationReason.Create,
300
+ );
301
+ });
302
+ });
303
+
304
+ describe("update mutations", () => {
305
+ it("should handle update mutations successfully", async () => {
306
+ const mutation: UpdateMutation<FrappeDocData> = {
307
+ mutationId: "mut5",
308
+ operation: Operation.Update,
309
+ doc: {
310
+ doctype: "TestDoc",
311
+ name: "test-doc",
312
+ title: "Updated Title",
313
+ },
314
+ version: "2024-01-01T10:00:00",
315
+ affected: [],
316
+ };
317
+
318
+ await mutationEngine.mutate(mutation);
319
+
320
+ expect(mockNormalizedCache.upsert).toHaveBeenCalledWith(
321
+ expect.objectContaining({
322
+ doctype: "TestDoc",
323
+ name: "test-doc",
324
+ title: "Updated Title",
325
+ }),
326
+ );
327
+ // updateDoc now takes a single doc object
328
+ expect(mockDb.updateDoc).toHaveBeenCalledWith(
329
+ expect.objectContaining({
330
+ doctype: "TestDoc",
331
+ name: "test-doc",
332
+ title: "Updated Title",
333
+ }),
334
+ );
335
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut5");
336
+ });
337
+
338
+ it("should throw error for update without docname", async () => {
339
+ const mutation: UpdateMutation<FrappeDocData> = {
340
+ mutationId: "mut6",
341
+ operation: Operation.Update,
342
+ doc: {
343
+ doctype: "TestDoc",
344
+ title: "Updated Title",
345
+ } as any,
346
+ version: undefined,
347
+ affected: [],
348
+ };
349
+
350
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow(
351
+ "docname is required for update/delete operations",
352
+ );
353
+ });
354
+
355
+ it("should determine version from cached document", async () => {
356
+ const mutation: UpdateMutation<FrappeDocData> = {
357
+ mutationId: "mut7",
358
+ operation: Operation.Update,
359
+ doc: {
360
+ doctype: "TestDoc",
361
+ name: "test-doc",
362
+ title: "Updated Title",
363
+ },
364
+ version: undefined,
365
+ affected: [],
366
+ };
367
+
368
+ await mutationEngine.mutate(mutation);
369
+
370
+ // updateDoc now takes a single doc object
371
+ expect(mockDb.updateDoc).toHaveBeenCalledWith(
372
+ expect.objectContaining({
373
+ doctype: "TestDoc",
374
+ name: "test-doc",
375
+ }),
376
+ );
377
+ });
378
+
379
+ it("should fetch document for version when not cached", async () => {
380
+ mockNormalizedCache.get.mockReturnValue(undefined as any);
381
+ mockDb.getDoc.mockResolvedValue({
382
+ doctype: "TestDoc",
383
+ name: "test-doc",
384
+ title: "Original Title",
385
+ modified: "2024-01-01T10:00:00",
386
+ });
387
+
388
+ const mutation: UpdateMutation<FrappeDocData> = {
389
+ mutationId: "mut8",
390
+ operation: Operation.Update,
391
+ doc: {
392
+ doctype: "TestDoc",
393
+ name: "test-doc",
394
+ title: "Updated Title",
395
+ },
396
+ version: undefined,
397
+ affected: [],
398
+ };
399
+
400
+ await mutationEngine.mutate(mutation);
401
+
402
+ expect(mockDb.getDoc).toHaveBeenCalledWith("TestDoc", "test-doc");
403
+ expect(mockNormalizedCache.upsert).toHaveBeenCalled();
404
+ });
405
+
406
+ it("should handle preflight fetch failures gracefully", async () => {
407
+ mockNormalizedCache.get.mockReturnValue(undefined as any);
408
+ mockDb.getDoc.mockRejectedValue(new Error("Network error"));
409
+
410
+ const mutation: UpdateMutation<FrappeDocData> = {
411
+ mutationId: "mut9",
412
+ operation: Operation.Update,
413
+ doc: {
414
+ doctype: "TestDoc",
415
+ name: "test-doc",
416
+ title: "Updated Title",
417
+ },
418
+ version: undefined,
419
+ affected: [],
420
+ };
421
+
422
+ await mutationEngine.mutate(mutation);
423
+
424
+ // updateDoc now takes a single doc object
425
+ expect(mockDb.updateDoc).toHaveBeenCalledWith(
426
+ expect.objectContaining({
427
+ doctype: "TestDoc",
428
+ name: "test-doc",
429
+ }),
430
+ );
431
+ });
432
+ });
433
+
434
+ describe("delete mutations", () => {
435
+ it("should handle delete mutations successfully", async () => {
436
+ const mutation: DeleteMutation<FrappeDocData> = {
437
+ mutationId: "mut10",
438
+ operation: Operation.Delete,
439
+ doc: {
440
+ doctype: "TestDoc",
441
+ name: "test-doc",
442
+ },
443
+ version: "2024-01-01T10:00:00",
444
+ affected: [],
445
+ };
446
+
447
+ await mutationEngine.mutate(mutation);
448
+
449
+ expect(mockNormalizedCache.remove).toHaveBeenCalledWith({
450
+ doctype: "TestDoc",
451
+ name: "test-doc",
452
+ });
453
+ expect(mockSubscriptionManager.removeFromAllSubscriptions).toHaveBeenCalledWith(
454
+ "TestDoc",
455
+ "test-doc",
456
+ );
457
+ // deleteDoc now takes a single doc object
458
+ expect(mockDb.deleteDoc).toHaveBeenCalledWith(
459
+ expect.objectContaining({
460
+ doctype: "TestDoc",
461
+ name: "test-doc",
462
+ }),
463
+ );
464
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut10");
465
+ });
466
+
467
+ it("should schedule rehydration for affected subscriptions", async () => {
468
+ const mockSubscription: Subscription = {
469
+ id: "sub1",
470
+ query: {
471
+ doctype: "TestDoc",
472
+ fields: ["*"],
473
+ filters: {},
474
+ limit: 20,
475
+ },
476
+ docIds: ["test-doc", "doc2"],
477
+ checkpoint: "checkpoint1",
478
+ metadata: {},
479
+ };
480
+ mockSubscriptionManager.getAllSubscriptions.mockReturnValue(
481
+ new Map([["sub1", mockSubscription]]),
482
+ );
483
+
484
+ const mutation: DeleteMutation<FrappeDocData> = {
485
+ mutationId: "mut11",
486
+ operation: Operation.Delete,
487
+ doc: {
488
+ doctype: "TestDoc",
489
+ name: "test-doc",
490
+ },
491
+ version: undefined,
492
+ affected: [],
493
+ };
494
+
495
+ await mutationEngine.mutate(mutation);
496
+
497
+ expect(mockRehydrationManager.scheduleRehydration).toHaveBeenCalledWith(
498
+ expect.any(Object),
499
+ RehydrationReason.Delete,
500
+ );
501
+ });
502
+
503
+ it("should throw error for delete without docname", async () => {
504
+ const mutation: DeleteMutation<FrappeDocData> = {
505
+ mutationId: "mut12",
506
+ operation: Operation.Delete,
507
+ doc: {
508
+ doctype: "TestDoc",
509
+ } as any,
510
+ version: undefined,
511
+ affected: [],
512
+ };
513
+
514
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow(
515
+ "docname is required for update/delete operations",
516
+ );
517
+ });
518
+ });
519
+
520
+ describe("callback integration", () => {
521
+ it("should call mutation lifecycle callbacks", async () => {
522
+ const callbacks = {
523
+ onMutationStart: vi.fn(),
524
+ onMutationSuccess: vi.fn(),
525
+ onOptimisticApply: vi.fn(),
526
+ };
527
+
528
+ mutationEngine = new MutationEngine(
529
+ mockDb,
530
+ mockRealtime,
531
+ mockNormalizedCache,
532
+ mockMutationJournal,
533
+ mockSubscriptionManager,
534
+ mockRehydrationManager,
535
+ callbacks,
536
+ );
537
+
538
+ const mutation: UpdateMutation<FrappeDocData> = {
539
+ mutationId: "mut13",
540
+ operation: Operation.Update,
541
+ doc: {
542
+ doctype: "TestDoc",
543
+ name: "test-doc",
544
+ title: "Updated",
545
+ },
546
+ version: undefined,
547
+ affected: [],
548
+ };
549
+
550
+ await mutationEngine.mutate(mutation);
551
+
552
+ expect(callbacks.onMutationStart).toHaveBeenCalledWith(mutation);
553
+ expect(callbacks.onMutationSuccess).toHaveBeenCalledWith(mutation);
554
+ expect(callbacks.onOptimisticApply).toHaveBeenCalledWith({
555
+ doctype: "TestDoc",
556
+ name: "test-doc",
557
+ operation: Operation.Update,
558
+ });
559
+ });
560
+ });
561
+
562
+ describe("realtime integration", () => {
563
+ it("should refetch documents when realtime is unhealthy", async () => {
564
+ mockRealtime.isHealthy.mockReturnValue(false);
565
+
566
+ const mutation: UpdateMutation<FrappeDocData> = {
567
+ mutationId: "mut14",
568
+ operation: Operation.Update,
569
+ doc: {
570
+ doctype: "TestDoc",
571
+ name: "test-doc",
572
+ title: "Updated",
573
+ },
574
+ version: undefined,
575
+ affected: [{ doctype: "RelatedDoc", name: "related-doc" }],
576
+ };
577
+
578
+ await mutationEngine.mutate(mutation);
579
+
580
+ // Should refetch the main document and affected documents
581
+ expect(mockDb.getDoc).toHaveBeenCalledWith("TestDoc", "test-doc");
582
+ expect(mockDb.getDoc).toHaveBeenCalledWith("RelatedDoc", "related-doc");
583
+ });
584
+
585
+ it("should not refetch when realtime is healthy", async () => {
586
+ mockRealtime.isHealthy.mockReturnValue(true);
587
+ mockDb.getDoc.mockClear(); // Clear any preflight calls
588
+
589
+ const mutation: UpdateMutation<FrappeDocData> = {
590
+ mutationId: "mut15",
591
+ operation: Operation.Update,
592
+ doc: {
593
+ doctype: "TestDoc",
594
+ name: "test-doc",
595
+ title: "Updated",
596
+ },
597
+ version: "2024-01-01T10:00:00",
598
+ affected: [],
599
+ };
600
+
601
+ await mutationEngine.mutate(mutation);
602
+
603
+ // Should not fetch the document after successful mutation
604
+ expect(mockDb.getDoc).not.toHaveBeenCalled();
605
+ });
606
+ });
607
+ });
608
+
609
+ describe("error handling", () => {
610
+ beforeEach(() => {
611
+ mutationEngine = new MutationEngine(
612
+ mockDb,
613
+ mockRealtime,
614
+ mockNormalizedCache,
615
+ mockMutationJournal,
616
+ mockSubscriptionManager,
617
+ mockRehydrationManager,
618
+ {
619
+ onMutationError: vi.fn(),
620
+ onConflict: vi.fn(),
621
+ },
622
+ );
623
+ });
624
+
625
+ it("should handle server errors", async () => {
626
+ const error = new Error("Server error");
627
+ mockDb.createDoc.mockRejectedValue(error);
628
+
629
+ const mutation: CreateMutation<FrappeDocData> = {
630
+ mutationId: "mut16",
631
+ operation: Operation.Create,
632
+ doc: {
633
+ doctype: "TestDoc",
634
+ name: "temp-doc",
635
+ title: "New Document",
636
+ },
637
+ version: undefined,
638
+ affected: [],
639
+ };
640
+
641
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow("Server error");
642
+ expect(mockMutationJournal.fail).toHaveBeenCalledWith("mut16");
643
+ });
644
+
645
+ it("should handle conflict errors (409)", async () => {
646
+ const error = { status: 409 };
647
+ mockDb.updateDoc.mockRejectedValue(error);
648
+
649
+ const mutation: UpdateMutation<FrappeDocData> = {
650
+ mutationId: "mut17",
651
+ operation: Operation.Update,
652
+ doc: {
653
+ doctype: "TestDoc",
654
+ name: "test-doc",
655
+ title: "Updated",
656
+ },
657
+ version: "2024-01-01T09:00:00",
658
+ affected: [],
659
+ };
660
+
661
+ await expect(mutationEngine.mutate(mutation)).rejects.toEqual(error);
662
+ expect(mockDb.getDoc).toHaveBeenCalledWith("TestDoc", "test-doc");
663
+ });
664
+
665
+ it("should handle snapshot rollback on error", async () => {
666
+ const error = new Error("Update failed");
667
+ mockDb.updateDoc.mockRejectedValue(error);
668
+
669
+ const mutation: UpdateMutation<FrappeDocData> = {
670
+ mutationId: "mut18",
671
+ operation: Operation.Update,
672
+ doc: {
673
+ doctype: "TestDoc",
674
+ name: "test-doc",
675
+ title: "Updated",
676
+ },
677
+ version: undefined,
678
+ affected: [],
679
+ };
680
+
681
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow("Update failed");
682
+
683
+ // Should restore snapshot
684
+ expect(mockNormalizedCache.upsert).toHaveBeenCalledWith(mockDoc);
685
+ });
686
+
687
+ it("should not rollback when offline", async () => {
688
+ Object.defineProperty(global, "navigator", {
689
+ value: { onLine: false },
690
+ writable: true,
691
+ });
692
+
693
+ const error = new Error("Network error");
694
+ mockDb.updateDoc.mockRejectedValue(error);
695
+
696
+ const mutation: UpdateMutation<FrappeDocData> = {
697
+ mutationId: "mut19",
698
+ operation: Operation.Update,
699
+ doc: {
700
+ doctype: "TestDoc",
701
+ name: "test-doc",
702
+ title: "Updated",
703
+ },
704
+ version: undefined,
705
+ affected: [],
706
+ };
707
+
708
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow("Network error");
709
+
710
+ // Should not restore snapshot when offline
711
+ const upsertCalls = mockNormalizedCache.upsert.mock.calls;
712
+ const rollbackCall = upsertCalls.find(
713
+ (call) => call[0] === mockDoc && upsertCalls.indexOf(call) > 0, // Not the initial optimistic update
714
+ );
715
+ expect(rollbackCall).toBeUndefined();
716
+ });
717
+
718
+ it("should handle failed create mutations", async () => {
719
+ const error = new Error("Create failed");
720
+ mockDb.createDoc.mockRejectedValue(error);
721
+
722
+ const subscription = {
723
+ id: "sub1",
724
+ query: { doctype: "TestDoc" },
725
+ docIds: ["temp-doc", "other-doc"],
726
+ } as any;
727
+
728
+ mockSubscriptionManager.getAllSubscriptions.mockReturnValue(
729
+ new Map([["sub1", subscription]]),
730
+ );
731
+
732
+ const mutation: CreateMutation<FrappeDocData> = {
733
+ mutationId: "mut20",
734
+ operation: Operation.Create,
735
+ doc: {
736
+ doctype: "TestDoc",
737
+ name: "temp-doc",
738
+ title: "New Document",
739
+ },
740
+ version: undefined,
741
+ affected: [],
742
+ };
743
+
744
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow("Create failed");
745
+
746
+ // Should remove temporary document from subscriptions
747
+ expect(subscription.docIds).toEqual(["other-doc"]);
748
+ });
749
+ });
750
+
751
+ describe("replayPending", () => {
752
+ beforeEach(() => {
753
+ mutationEngine = new MutationEngine(
754
+ mockDb,
755
+ mockRealtime,
756
+ mockNormalizedCache,
757
+ mockMutationJournal,
758
+ mockSubscriptionManager,
759
+ mockRehydrationManager,
760
+ {
761
+ onReplayStart: vi.fn(),
762
+ onReplayComplete: vi.fn(),
763
+ onConflict: vi.fn(),
764
+ onMutationError: vi.fn(),
765
+ },
766
+ );
767
+ });
768
+
769
+ it("should replay pending create mutations", async () => {
770
+ const pendingEntry: Pending = {
771
+ doctype: "TestDoc",
772
+ docname: "test-doc",
773
+ values: { doctype: "TestDoc", name: "test-doc", title: "Test" },
774
+ operation: Operation.Create,
775
+ version: undefined,
776
+ status: Status.Pending,
777
+ };
778
+
779
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut21", entry: pendingEntry }]);
780
+
781
+ await mutationEngine.replayPending();
782
+
783
+ // createDoc now takes a single doc object with client_mutation_id
784
+ expect(mockDb.createDoc).toHaveBeenCalledWith(
785
+ expect.objectContaining({
786
+ doctype: "TestDoc",
787
+ client_mutation_id: "mut21",
788
+ }),
789
+ );
790
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut21");
791
+ });
792
+
793
+ it("should replay pending update mutations", async () => {
794
+ const pendingEntry: Pending = {
795
+ doctype: "TestDoc",
796
+ docname: "test-doc",
797
+ values: { doctype: "TestDoc", name: "test-doc", title: "Updated" },
798
+ operation: Operation.Update,
799
+ version: "2024-01-01T10:00:00",
800
+ status: Status.Pending,
801
+ };
802
+
803
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut22", entry: pendingEntry }]);
804
+
805
+ await mutationEngine.replayPending();
806
+
807
+ // updateDoc now takes a single doc object with client_mutation_id and base_modified
808
+ expect(mockDb.updateDoc).toHaveBeenCalledWith(
809
+ expect.objectContaining({
810
+ doctype: "TestDoc",
811
+ name: "test-doc",
812
+ client_mutation_id: "mut22",
813
+ base_modified: "2024-01-01T10:00:00",
814
+ }),
815
+ );
816
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut22");
817
+ });
818
+
819
+ it("should replay pending delete mutations", async () => {
820
+ const pendingEntry: Pending = {
821
+ doctype: "TestDoc",
822
+ docname: "test-doc",
823
+ values: undefined,
824
+ operation: Operation.Delete,
825
+ version: undefined,
826
+ status: Status.Pending,
827
+ };
828
+
829
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut23", entry: pendingEntry }]);
830
+
831
+ await mutationEngine.replayPending();
832
+
833
+ // deleteDoc now takes a single doc object with client_mutation_id
834
+ expect(mockDb.deleteDoc).toHaveBeenCalledWith(
835
+ expect.objectContaining({
836
+ doctype: "TestDoc",
837
+ name: "test-doc",
838
+ client_mutation_id: "mut23",
839
+ }),
840
+ );
841
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("mut23");
842
+ });
843
+
844
+ it("should handle replay conflicts", async () => {
845
+ const pendingEntry: Pending = {
846
+ doctype: "TestDoc",
847
+ docname: "test-doc",
848
+ values: { doctype: "TestDoc", name: "test-doc", title: "Updated" },
849
+ operation: Operation.Update,
850
+ version: "2024-01-01T10:00:00",
851
+ status: Status.Pending,
852
+ };
853
+
854
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut24", entry: pendingEntry }]);
855
+
856
+ mockDb.updateDoc.mockRejectedValue({ status: 409 });
857
+
858
+ await mutationEngine.replayPending();
859
+
860
+ const options = mutationEngine as any;
861
+ expect(options.options.onConflict).toHaveBeenCalledWith({
862
+ doctype: "TestDoc",
863
+ name: "test-doc",
864
+ });
865
+ });
866
+
867
+ it("should handle replay auth errors", async () => {
868
+ const pendingEntry: Pending = {
869
+ doctype: "TestDoc",
870
+ docname: "test-doc",
871
+ values: { doctype: "TestDoc", name: "test-doc", title: "Updated" },
872
+ operation: Operation.Update,
873
+ version: undefined,
874
+ status: Status.Pending,
875
+ };
876
+
877
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut25", entry: pendingEntry }]);
878
+
879
+ mockDb.updateDoc.mockRejectedValue({ status: 401 });
880
+
881
+ await mutationEngine.replayPending();
882
+
883
+ // Should not confirm or fail, keeping in journal
884
+ expect(mockMutationJournal.confirm).not.toHaveBeenCalledWith("mut25");
885
+ expect(mockMutationJournal.fail).not.toHaveBeenCalledWith("mut25");
886
+ });
887
+
888
+ it("should handle permanent replay errors", async () => {
889
+ const pendingEntry: Pending = {
890
+ doctype: "TestDoc",
891
+ docname: "test-doc",
892
+ values: { doctype: "TestDoc", name: "test-doc", title: "Updated" },
893
+ operation: Operation.Update,
894
+ version: undefined,
895
+ status: Status.Pending,
896
+ };
897
+
898
+ mockMutationJournal.listPending.mockReturnValue([{ id: "mut26", entry: pendingEntry }]);
899
+
900
+ mockDb.updateDoc.mockRejectedValue({ status: 400 });
901
+
902
+ await mutationEngine.replayPending();
903
+
904
+ expect(mockMutationJournal.fail).toHaveBeenCalledWith("mut26");
905
+ });
906
+
907
+ it("should call replay callbacks", async () => {
908
+ mockMutationJournal.listPending.mockReturnValue([]);
909
+
910
+ await mutationEngine.replayPending();
911
+
912
+ const options = mutationEngine as any;
913
+ expect(options.options.onReplayStart).toHaveBeenCalled();
914
+ expect(options.options.onReplayComplete).toHaveBeenCalledWith({
915
+ anyFailed: false,
916
+ });
917
+ });
918
+ });
919
+
920
+ describe("temporary ID management", () => {
921
+ beforeEach(() => {
922
+ mutationEngine = new MutationEngine(
923
+ mockDb,
924
+ mockRealtime,
925
+ mockNormalizedCache,
926
+ mockMutationJournal,
927
+ mockSubscriptionManager,
928
+ mockRehydrationManager,
929
+ {},
930
+ );
931
+ });
932
+
933
+ it("should return temporary ID mappings", () => {
934
+ const tempMap = mutationEngine.getTemporaryIdToRealNameMap();
935
+ expect(tempMap).toBeInstanceOf(Map);
936
+ });
937
+
938
+ it("should clear temporary mappings", () => {
939
+ // Add some mappings first
940
+ const tempMap = mutationEngine.getTemporaryIdToRealNameMap();
941
+ tempMap.set("temp1", "real1");
942
+
943
+ mutationEngine.clearTemporaryMappings();
944
+
945
+ expect(tempMap.size).toBe(0);
946
+ });
947
+ });
948
+
949
+ describe("cleanup and disposal", () => {
950
+ beforeEach(() => {
951
+ mutationEngine = new MutationEngine(
952
+ mockDb,
953
+ mockRealtime,
954
+ mockNormalizedCache,
955
+ mockMutationJournal,
956
+ mockSubscriptionManager,
957
+ mockRehydrationManager,
958
+ {},
959
+ );
960
+ });
961
+
962
+ it("should dispose of resources", () => {
963
+ const tempMap = mutationEngine.getTemporaryIdToRealNameMap();
964
+ tempMap.set("temp1", "real1");
965
+
966
+ mutationEngine.dispose();
967
+
968
+ expect(tempMap.size).toBe(0);
969
+ });
970
+ });
971
+
972
+ describe("edge cases", () => {
973
+ beforeEach(() => {
974
+ mutationEngine = new MutationEngine(
975
+ mockDb,
976
+ mockRealtime,
977
+ mockNormalizedCache,
978
+ mockMutationJournal,
979
+ mockSubscriptionManager,
980
+ mockRehydrationManager,
981
+ {},
982
+ );
983
+ });
984
+
985
+ it("should handle missing crypto.randomUUID", async () => {
986
+ (global as any).crypto = undefined;
987
+
988
+ const mutation: CreateMutation<FrappeDocData> = {
989
+ mutationId: "no-crypto",
990
+ operation: Operation.Create,
991
+ doc: {
992
+ doctype: "TestDoc",
993
+ title: "New Document",
994
+ } as any,
995
+ version: undefined,
996
+ affected: [],
997
+ };
998
+
999
+ mockSubscriptionManager.getAllSubscriptions = vi.fn().mockReturnValue(new Map());
1000
+ await expect(mutationEngine.mutate(mutation)).resolves.not.toThrow();
1001
+ });
1002
+
1003
+ it("should handle undefined navigator", async () => {
1004
+ (global as any).navigator = undefined;
1005
+
1006
+ const error = new Error("Update failed");
1007
+ mockDb.updateDoc.mockRejectedValue(error);
1008
+
1009
+ const mutation: UpdateMutation<FrappeDocData> = {
1010
+ mutationId: "no-navigator",
1011
+ operation: Operation.Update,
1012
+ doc: {
1013
+ doctype: "TestDoc",
1014
+ name: "test-doc",
1015
+ title: "Updated",
1016
+ },
1017
+ version: undefined,
1018
+ affected: [],
1019
+ };
1020
+
1021
+ await expect(mutationEngine.mutate(mutation)).rejects.toThrow("Update failed");
1022
+
1023
+ // Should still attempt snapshot rollback (upsert is called twice: once for optimistic update, once for rollback)
1024
+ expect(mockNormalizedCache.upsert).toHaveBeenCalledTimes(2);
1025
+ // Last call should be the rollback with original snapshot
1026
+ expect(mockNormalizedCache.upsert).toHaveBeenLastCalledWith(mockDoc);
1027
+ });
1028
+
1029
+ it("should handle custom version fields", async () => {
1030
+ mutationEngine = new MutationEngine(
1031
+ mockDb,
1032
+ mockRealtime,
1033
+ mockNormalizedCache,
1034
+ mockMutationJournal,
1035
+ mockSubscriptionManager,
1036
+ mockRehydrationManager,
1037
+ {
1038
+ versionFields: {
1039
+ TestDoc: "timestamp",
1040
+ },
1041
+ },
1042
+ );
1043
+
1044
+ const docWithTimestamp = {
1045
+ ...mockDoc,
1046
+ timestamp: "2024-01-01T12:00:00",
1047
+ };
1048
+
1049
+ mockNormalizedCache.get.mockReturnValue(docWithTimestamp);
1050
+
1051
+ const mutation: UpdateMutation<FrappeDocData> = {
1052
+ mutationId: "custom-version",
1053
+ operation: Operation.Update,
1054
+ doc: {
1055
+ doctype: "TestDoc",
1056
+ name: "test-doc",
1057
+ title: "Updated",
1058
+ },
1059
+ version: undefined,
1060
+ affected: [],
1061
+ };
1062
+
1063
+ await mutationEngine.mutate(mutation);
1064
+
1065
+ // updateDoc now takes a single doc object
1066
+ expect(mockDb.updateDoc).toHaveBeenCalledWith(
1067
+ expect.objectContaining({
1068
+ doctype: "TestDoc",
1069
+ name: "test-doc",
1070
+ }),
1071
+ );
1072
+ });
1073
+ });
1074
+
1075
+ describe("buildMergeConfig", () => {
1076
+ it("should apply field merge config via handleConflict auto-merge", async () => {
1077
+ const onConflict = vi.fn();
1078
+ const baseDoc = {
1079
+ ...mockDoc,
1080
+ priority: 5,
1081
+ };
1082
+
1083
+ mutationEngine = new MutationEngine(
1084
+ mockDb,
1085
+ mockRealtime,
1086
+ mockNormalizedCache,
1087
+ mockMutationJournal,
1088
+ mockSubscriptionManager,
1089
+ mockRehydrationManager,
1090
+ {
1091
+ fieldMergeConfig: {
1092
+ TestDoc: { priority: "max" as any },
1093
+ },
1094
+ onConflict,
1095
+ onMutationError: vi.fn(),
1096
+ },
1097
+ );
1098
+
1099
+ mockNormalizedCache.get.mockReturnValue(baseDoc);
1100
+
1101
+ mockMutationJournal.get.mockReturnValue({
1102
+ doctype: "TestDoc",
1103
+ docname: "test-doc",
1104
+ values: { ...baseDoc, priority: 8 },
1105
+ operation: Operation.Update,
1106
+ version: "2024-01-01T10:00:00",
1107
+ status: Status.Pending,
1108
+ snapshot: baseDoc,
1109
+ });
1110
+
1111
+ mockDb.updateDoc
1112
+ .mockRejectedValueOnce({ status: 409 })
1113
+ .mockResolvedValueOnce({ ...baseDoc, priority: 10 });
1114
+
1115
+ const serverDoc = { ...baseDoc, priority: 10, modified: "2024-01-01T11:00:00" };
1116
+ mockDb.getDoc.mockResolvedValue(serverDoc);
1117
+
1118
+ const mutation: UpdateMutation<FrappeDocData> = {
1119
+ mutationId: "merge-test",
1120
+ operation: Operation.Update,
1121
+ doc: {
1122
+ doctype: "TestDoc",
1123
+ name: "test-doc",
1124
+ priority: 8,
1125
+ },
1126
+ version: "2024-01-01T10:00:00",
1127
+ affected: [],
1128
+ };
1129
+
1130
+ await expect(mutationEngine.mutate(mutation)).rejects.toEqual({ status: 409 });
1131
+
1132
+ expect(mockDb.updateDoc).toHaveBeenCalledTimes(2);
1133
+ const retryCall = mockDb.updateDoc.mock.calls[1][0];
1134
+ expect(retryCall.priority).toBe(10); // max(5, 8, 10)
1135
+
1136
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("merge-test");
1137
+ expect(onConflict).not.toHaveBeenCalled();
1138
+ });
1139
+ });
1140
+
1141
+ describe("handleConflict auto-merge", () => {
1142
+ it("should auto-merge disjoint updates", async () => {
1143
+ const onConflict = vi.fn();
1144
+
1145
+ mutationEngine = new MutationEngine(
1146
+ mockDb,
1147
+ mockRealtime,
1148
+ mockNormalizedCache,
1149
+ mockMutationJournal,
1150
+ mockSubscriptionManager,
1151
+ mockRehydrationManager,
1152
+ {
1153
+ onConflict,
1154
+ onMutationError: vi.fn(),
1155
+ },
1156
+ );
1157
+
1158
+ const baseDoc = { ...mockDoc, title: "Original Title", status: "Draft" };
1159
+ mockNormalizedCache.get.mockReturnValue(baseDoc);
1160
+
1161
+ mockMutationJournal.get.mockReturnValue({
1162
+ doctype: "TestDoc",
1163
+ docname: "test-doc",
1164
+ values: { ...baseDoc, title: "Client Title" },
1165
+ operation: Operation.Update,
1166
+ version: "2024-01-01T10:00:00",
1167
+ status: Status.Pending,
1168
+ snapshot: baseDoc,
1169
+ });
1170
+
1171
+ mockDb.updateDoc.mockRejectedValueOnce({ status: 409 }).mockResolvedValueOnce({ ...mockDoc });
1172
+
1173
+ const serverDoc = { ...baseDoc, status: "Approved", modified: "2024-01-01T11:00:00" };
1174
+ mockDb.getDoc.mockResolvedValue(serverDoc);
1175
+
1176
+ const mutation: UpdateMutation<FrappeDocData> = {
1177
+ mutationId: "disjoint-test",
1178
+ operation: Operation.Update,
1179
+ doc: {
1180
+ doctype: "TestDoc",
1181
+ name: "test-doc",
1182
+ title: "Client Title",
1183
+ },
1184
+ version: "2024-01-01T10:00:00",
1185
+ affected: [],
1186
+ };
1187
+
1188
+ await expect(mutationEngine.mutate(mutation)).rejects.toEqual({ status: 409 });
1189
+
1190
+ expect(mockDb.updateDoc).toHaveBeenCalledTimes(2);
1191
+ const retryCall = mockDb.updateDoc.mock.calls[1][0];
1192
+ expect(retryCall.title).toBe("Client Title");
1193
+ expect(retryCall.status).toBe("Approved");
1194
+
1195
+ expect(mockMutationJournal.confirm).toHaveBeenCalledWith("disjoint-test");
1196
+ expect(onConflict).not.toHaveBeenCalled();
1197
+ });
1198
+
1199
+ it("should delegate overlapping conflicts to onConflict", async () => {
1200
+ const onConflict = vi.fn();
1201
+
1202
+ mutationEngine = new MutationEngine(
1203
+ mockDb,
1204
+ mockRealtime,
1205
+ mockNormalizedCache,
1206
+ mockMutationJournal,
1207
+ mockSubscriptionManager,
1208
+ mockRehydrationManager,
1209
+ {
1210
+ onConflict,
1211
+ onMutationError: vi.fn(),
1212
+ },
1213
+ );
1214
+
1215
+ const baseDoc = { ...mockDoc, title: "Original Title" };
1216
+ mockNormalizedCache.get.mockReturnValue(baseDoc);
1217
+
1218
+ mockMutationJournal.get.mockReturnValue({
1219
+ doctype: "TestDoc",
1220
+ docname: "test-doc",
1221
+ values: { ...baseDoc, title: "Client Title" },
1222
+ operation: Operation.Update,
1223
+ version: "2024-01-01T10:00:00",
1224
+ status: Status.Pending,
1225
+ snapshot: baseDoc,
1226
+ });
1227
+
1228
+ mockDb.updateDoc.mockRejectedValue({ status: 409 });
1229
+
1230
+ const serverDoc = { ...baseDoc, title: "Server Title", modified: "2024-01-01T11:00:00" };
1231
+ mockDb.getDoc.mockResolvedValue(serverDoc);
1232
+
1233
+ const mutation: UpdateMutation<FrappeDocData> = {
1234
+ mutationId: "overlap-test",
1235
+ operation: Operation.Update,
1236
+ doc: {
1237
+ doctype: "TestDoc",
1238
+ name: "test-doc",
1239
+ title: "Client Title",
1240
+ },
1241
+ version: "2024-01-01T10:00:00",
1242
+ affected: [],
1243
+ };
1244
+
1245
+ await expect(mutationEngine.mutate(mutation)).rejects.toEqual({ status: 409 });
1246
+
1247
+ expect(onConflict).toHaveBeenCalledWith({
1248
+ doctype: "TestDoc",
1249
+ name: "test-doc",
1250
+ });
1251
+
1252
+ expect(mockDb.updateDoc).toHaveBeenCalledTimes(1);
1253
+ });
1254
+ });
1255
+
1256
+ describe("onRetry callback", () => {
1257
+ it("should call onRetry for each pending mutation during replay", async () => {
1258
+ const onRetry = vi.fn();
1259
+
1260
+ mutationEngine = new MutationEngine(
1261
+ mockDb,
1262
+ mockRealtime,
1263
+ mockNormalizedCache,
1264
+ mockMutationJournal,
1265
+ mockSubscriptionManager,
1266
+ mockRehydrationManager,
1267
+ {
1268
+ onRetry,
1269
+ onReplayStart: vi.fn(),
1270
+ onReplayComplete: vi.fn(),
1271
+ },
1272
+ );
1273
+
1274
+ const pendingEntries = [
1275
+ {
1276
+ id: "retry-1",
1277
+ entry: {
1278
+ doctype: "TestDoc",
1279
+ docname: "doc-1",
1280
+ values: { doctype: "TestDoc", name: "doc-1", title: "Doc 1" },
1281
+ operation: Operation.Update,
1282
+ version: "2024-01-01T10:00:00",
1283
+ status: Status.Pending,
1284
+ } as Pending,
1285
+ },
1286
+ {
1287
+ id: "retry-2",
1288
+ entry: {
1289
+ doctype: "TestDoc",
1290
+ docname: "doc-2",
1291
+ values: { doctype: "TestDoc", name: "doc-2", title: "Doc 2" },
1292
+ operation: Operation.Create,
1293
+ version: undefined,
1294
+ status: Status.Pending,
1295
+ } as Pending,
1296
+ },
1297
+ ];
1298
+
1299
+ mockMutationJournal.listPending.mockReturnValue(pendingEntries);
1300
+
1301
+ const replayPromise = mutationEngine.replayPending();
1302
+ await vi.advanceTimersByTimeAsync(1000);
1303
+ await replayPromise;
1304
+
1305
+ expect(onRetry).toHaveBeenCalledTimes(2);
1306
+ expect(onRetry).toHaveBeenCalledWith({ id: "retry-1", operation: Operation.Update });
1307
+ expect(onRetry).toHaveBeenCalledWith({ id: "retry-2", operation: Operation.Create });
1308
+ });
1309
+ });
1310
+
1311
+ describe("batch mutations", () => {
1312
+ it("should batch mutations and flush after batchDelay", async () => {
1313
+ (mockDb as any).batchMutations = vi.fn().mockResolvedValue([
1314
+ { success: true, data: { ...mockDoc, name: "test-doc-1", title: "Title 1" } },
1315
+ { success: true, data: { ...mockDoc, name: "test-doc-2", title: "Title 2" } },
1316
+ { success: true, data: { ...mockDoc, name: "test-doc-3", title: "Title 3" } },
1317
+ ]);
1318
+
1319
+ mutationEngine = new MutationEngine(
1320
+ mockDb,
1321
+ mockRealtime,
1322
+ mockNormalizedCache,
1323
+ mockMutationJournal,
1324
+ mockSubscriptionManager,
1325
+ mockRehydrationManager,
1326
+ {
1327
+ enableBatching: true,
1328
+ batchDelay: 50,
1329
+ batchMaxSize: 10,
1330
+ },
1331
+ );
1332
+
1333
+ mockNormalizedCache.get.mockImplementation((ref: any) => ({
1334
+ ...mockDoc,
1335
+ name: ref.name,
1336
+ modified: "2024-01-01T10:00:00",
1337
+ }));
1338
+
1339
+ const mutations: UpdateMutation<FrappeDocData>[] = [1, 2, 3].map((i) => ({
1340
+ mutationId: `batch-${i}`,
1341
+ operation: Operation.Update,
1342
+ doc: {
1343
+ doctype: "TestDoc",
1344
+ name: `test-doc-${i}`,
1345
+ title: `Title ${i}`,
1346
+ },
1347
+ version: "2024-01-01T10:00:00",
1348
+ affected: [],
1349
+ }));
1350
+
1351
+ const promises = mutations.map((m) => mutationEngine.mutate(m));
1352
+ await vi.advanceTimersByTimeAsync(100);
1353
+ await Promise.all(promises);
1354
+
1355
+ expect((mockDb as any).batchMutations).toHaveBeenCalledTimes(1);
1356
+ expect((mockDb as any).batchMutations).toHaveBeenCalledWith(
1357
+ expect.objectContaining({
1358
+ mutations: expect.arrayContaining([
1359
+ expect.objectContaining({ type: "update", doctype: "TestDoc", name: "test-doc-1" }),
1360
+ expect.objectContaining({ type: "update", doctype: "TestDoc", name: "test-doc-2" }),
1361
+ expect.objectContaining({ type: "update", doctype: "TestDoc", name: "test-doc-3" }),
1362
+ ]),
1363
+ }),
1364
+ );
1365
+ });
1366
+
1367
+ it("should flush immediately when batchMaxSize is reached and keep remaining pending", async () => {
1368
+ (mockDb as any).batchMutations = vi
1369
+ .fn()
1370
+ .mockResolvedValueOnce([
1371
+ { success: true, data: { ...mockDoc, name: "test-doc-1" } },
1372
+ { success: true, data: { ...mockDoc, name: "test-doc-2" } },
1373
+ { success: true, data: { ...mockDoc, name: "test-doc-3" } },
1374
+ ])
1375
+ .mockResolvedValueOnce([{ success: true, data: { ...mockDoc, name: "test-doc-4" } }]);
1376
+
1377
+ mutationEngine = new MutationEngine(
1378
+ mockDb,
1379
+ mockRealtime,
1380
+ mockNormalizedCache,
1381
+ mockMutationJournal,
1382
+ mockSubscriptionManager,
1383
+ mockRehydrationManager,
1384
+ {
1385
+ enableBatching: true,
1386
+ batchDelay: 50,
1387
+ batchMaxSize: 3,
1388
+ },
1389
+ );
1390
+
1391
+ mockNormalizedCache.get.mockImplementation((ref: any) => ({
1392
+ ...mockDoc,
1393
+ name: ref.name,
1394
+ modified: "2024-01-01T10:00:00",
1395
+ }));
1396
+
1397
+ const mutations: UpdateMutation<FrappeDocData>[] = [1, 2, 3, 4].map((i) => ({
1398
+ mutationId: `batch-${i}`,
1399
+ operation: Operation.Update,
1400
+ doc: {
1401
+ doctype: "TestDoc",
1402
+ name: `test-doc-${i}`,
1403
+ title: `Title ${i}`,
1404
+ },
1405
+ version: "2024-01-01T10:00:00",
1406
+ affected: [],
1407
+ }));
1408
+
1409
+ const promises = mutations.map((m) => mutationEngine.mutate(m));
1410
+ await vi.advanceTimersByTimeAsync(100);
1411
+ await Promise.all(promises);
1412
+
1413
+ expect((mockDb as any).batchMutations).toHaveBeenCalledTimes(2);
1414
+ expect((mockDb as any).batchMutations.mock.calls[0][0].mutations).toHaveLength(3);
1415
+ expect((mockDb as any).batchMutations.mock.calls[1][0].mutations).toHaveLength(1);
1416
+ });
1417
+ });
1418
+ });