@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.
- package/LICENSE +201 -0
- package/README.md +241 -0
- package/dist/cjs/auth.cjs +50 -0
- package/dist/cjs/auth.native.js +100 -0
- package/dist/cjs/auth.native.js.map +1 -0
- package/dist/cjs/auth.spec.cjs +322 -0
- package/dist/cjs/auth.spec.native.js +329 -0
- package/dist/cjs/auth.spec.native.js.map +1 -0
- package/dist/cjs/call.cjs +45 -0
- package/dist/cjs/call.native.js +93 -0
- package/dist/cjs/call.native.js.map +1 -0
- package/dist/cjs/call.spec.cjs +427 -0
- package/dist/cjs/call.spec.native.js +430 -0
- package/dist/cjs/call.spec.native.js.map +1 -0
- package/dist/cjs/collection.cjs +278 -0
- package/dist/cjs/collection.native.js +494 -0
- package/dist/cjs/collection.native.js.map +1 -0
- package/dist/cjs/collection.spec.cjs +186 -0
- package/dist/cjs/collection.spec.native.js +216 -0
- package/dist/cjs/collection.spec.native.js.map +1 -0
- package/dist/cjs/common/types.cjs +18 -0
- package/dist/cjs/common/types.native.js +21 -0
- package/dist/cjs/common/types.native.js.map +1 -0
- package/dist/cjs/common/types.spec.cjs +266 -0
- package/dist/cjs/common/types.spec.native.js +271 -0
- package/dist/cjs/common/types.spec.native.js.map +1 -0
- package/dist/cjs/core/http.cjs +289 -0
- package/dist/cjs/core/http.native.js +484 -0
- package/dist/cjs/core/http.native.js.map +1 -0
- package/dist/cjs/core/http.spec.cjs +1259 -0
- package/dist/cjs/core/http.spec.native.js +1401 -0
- package/dist/cjs/core/http.spec.native.js.map +1 -0
- package/dist/cjs/core/types.cjs +53 -0
- package/dist/cjs/core/types.native.js +178 -0
- package/dist/cjs/core/types.native.js.map +1 -0
- package/dist/cjs/core/wire.cjs +53 -0
- package/dist/cjs/core/wire.native.js +56 -0
- package/dist/cjs/core/wire.native.js.map +1 -0
- package/dist/cjs/core/wire.spec.cjs +478 -0
- package/dist/cjs/core/wire.spec.native.js +482 -0
- package/dist/cjs/core/wire.spec.native.js.map +1 -0
- package/dist/cjs/db.cjs +355 -0
- package/dist/cjs/db.native.js +469 -0
- package/dist/cjs/db.native.js.map +1 -0
- package/dist/cjs/db.spec.cjs +862 -0
- package/dist/cjs/db.spec.native.js +865 -0
- package/dist/cjs/db.spec.native.js.map +1 -0
- package/dist/cjs/devtools/FrappeDevtoolsPanel.cjs +838 -0
- package/dist/cjs/devtools/FrappeDevtoolsPanel.native.js +919 -0
- package/dist/cjs/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
- package/dist/cjs/devtools/index.cjs +30 -0
- package/dist/cjs/devtools/index.native.js +33 -0
- package/dist/cjs/devtools/index.native.js.map +1 -0
- package/dist/cjs/devtools/plugin.cjs +39 -0
- package/dist/cjs/devtools/plugin.native.js +56 -0
- package/dist/cjs/devtools/plugin.native.js.map +1 -0
- package/dist/cjs/devtools/theme.cjs +55 -0
- package/dist/cjs/devtools/theme.native.js +58 -0
- package/dist/cjs/devtools/theme.native.js.map +1 -0
- package/dist/cjs/doctype.cjs +410 -0
- package/dist/cjs/doctype.native.js +521 -0
- package/dist/cjs/doctype.native.js.map +1 -0
- package/dist/cjs/doctype.spec.cjs +460 -0
- package/dist/cjs/doctype.spec.native.js +538 -0
- package/dist/cjs/doctype.spec.native.js.map +1 -0
- package/dist/cjs/file.cjs +41 -0
- package/dist/cjs/file.native.js +84 -0
- package/dist/cjs/file.native.js.map +1 -0
- package/dist/cjs/file.spec.cjs +315 -0
- package/dist/cjs/file.spec.native.js +342 -0
- package/dist/cjs/file.spec.native.js.map +1 -0
- package/dist/cjs/fixtureDb.cjs +212 -0
- package/dist/cjs/fixtureDb.native.js +313 -0
- package/dist/cjs/fixtureDb.native.js.map +1 -0
- package/dist/cjs/fixtures.cjs +199 -0
- package/dist/cjs/fixtures.native.js +400 -0
- package/dist/cjs/fixtures.native.js.map +1 -0
- package/dist/cjs/frappeApp.cjs +117 -0
- package/dist/cjs/frappeApp.native.js +179 -0
- package/dist/cjs/frappeApp.native.js.map +1 -0
- package/dist/cjs/frappeApp.spec.cjs +306 -0
- package/dist/cjs/frappeApp.spec.native.js +308 -0
- package/dist/cjs/frappeApp.spec.native.js.map +1 -0
- package/dist/cjs/frappeReact.cjs +314 -0
- package/dist/cjs/frappeReact.native.js +351 -0
- package/dist/cjs/frappeReact.native.js.map +1 -0
- package/dist/cjs/frappeReact.spec.cjs +531 -0
- package/dist/cjs/frappeReact.spec.native.js +670 -0
- package/dist/cjs/frappeReact.spec.native.js.map +1 -0
- package/dist/cjs/index.cjs +149 -0
- package/dist/cjs/index.native.js +152 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/optimistic.cjs +28 -0
- package/dist/cjs/optimistic.native.js +31 -0
- package/dist/cjs/optimistic.native.js.map +1 -0
- package/dist/cjs/optimistic.spec.cjs +11 -0
- package/dist/cjs/optimistic.spec.native.js +19 -0
- package/dist/cjs/optimistic.spec.native.js.map +1 -0
- package/dist/cjs/pacer.cjs +200 -0
- package/dist/cjs/pacer.native.js +274 -0
- package/dist/cjs/pacer.native.js.map +1 -0
- package/dist/cjs/pacer.spec.cjs +470 -0
- package/dist/cjs/pacer.spec.native.js +537 -0
- package/dist/cjs/pacer.spec.native.js.map +1 -0
- package/dist/cjs/plugin/index.cjs +124 -0
- package/dist/cjs/plugin/index.native.js +193 -0
- package/dist/cjs/plugin/index.native.js.map +1 -0
- package/dist/cjs/realtime.cjs +265 -0
- package/dist/cjs/realtime.native.js +387 -0
- package/dist/cjs/realtime.native.js.map +1 -0
- package/dist/cjs/realtime.spec.cjs +326 -0
- package/dist/cjs/realtime.spec.native.js +392 -0
- package/dist/cjs/realtime.spec.native.js.map +1 -0
- package/dist/cjs/sync/backfill.cjs +314 -0
- package/dist/cjs/sync/backfill.native.js +487 -0
- package/dist/cjs/sync/backfill.native.js.map +1 -0
- package/dist/cjs/sync/backfill.spec.cjs +586 -0
- package/dist/cjs/sync/backfill.spec.native.js +712 -0
- package/dist/cjs/sync/backfill.spec.native.js.map +1 -0
- package/dist/cjs/sync/debug.spec.cjs +125 -0
- package/dist/cjs/sync/debug.spec.native.js +133 -0
- package/dist/cjs/sync/debug.spec.native.js.map +1 -0
- package/dist/cjs/sync/filters.cjs +311 -0
- package/dist/cjs/sync/filters.native.js +446 -0
- package/dist/cjs/sync/filters.native.js.map +1 -0
- package/dist/cjs/sync/filters.spec.cjs +168 -0
- package/dist/cjs/sync/filters.spec.native.js +173 -0
- package/dist/cjs/sync/filters.spec.native.js.map +1 -0
- package/dist/cjs/sync/index.cjs +303 -0
- package/dist/cjs/sync/index.native.js +462 -0
- package/dist/cjs/sync/index.native.js.map +1 -0
- package/dist/cjs/sync/merge.cjs +435 -0
- package/dist/cjs/sync/merge.native.js +717 -0
- package/dist/cjs/sync/merge.native.js.map +1 -0
- package/dist/cjs/sync/merge.spec.cjs +908 -0
- package/dist/cjs/sync/merge.spec.native.js +937 -0
- package/dist/cjs/sync/merge.spec.native.js.map +1 -0
- package/dist/cjs/sync/multiOrderBy.spec.cjs +165 -0
- package/dist/cjs/sync/multiOrderBy.spec.native.js +229 -0
- package/dist/cjs/sync/multiOrderBy.spec.native.js.map +1 -0
- package/dist/cjs/sync/mutationEngine.cjs +781 -0
- package/dist/cjs/sync/mutationEngine.native.js +1159 -0
- package/dist/cjs/sync/mutationEngine.native.js.map +1 -0
- package/dist/cjs/sync/mutationEngine.spec.cjs +1158 -0
- package/dist/cjs/sync/mutationEngine.spec.native.js +1173 -0
- package/dist/cjs/sync/mutationEngine.spec.native.js.map +1 -0
- package/dist/cjs/sync/mutationJournal.cjs +150 -0
- package/dist/cjs/sync/mutationJournal.native.js +282 -0
- package/dist/cjs/sync/mutationJournal.native.js.map +1 -0
- package/dist/cjs/sync/mutationJournal.spec.cjs +330 -0
- package/dist/cjs/sync/mutationJournal.spec.native.js +333 -0
- package/dist/cjs/sync/mutationJournal.spec.native.js.map +1 -0
- package/dist/cjs/sync/mutations.spec.cjs +327 -0
- package/dist/cjs/sync/mutations.spec.native.js +330 -0
- package/dist/cjs/sync/mutations.spec.native.js.map +1 -0
- package/dist/cjs/sync/normalizedCache.cjs +140 -0
- package/dist/cjs/sync/normalizedCache.native.js +229 -0
- package/dist/cjs/sync/normalizedCache.native.js.map +1 -0
- package/dist/cjs/sync/normalizedCache.spec.cjs +302 -0
- package/dist/cjs/sync/normalizedCache.spec.native.js +311 -0
- package/dist/cjs/sync/normalizedCache.spec.native.js.map +1 -0
- package/dist/cjs/sync/persistence.cjs +627 -0
- package/dist/cjs/sync/persistence.native.js +911 -0
- package/dist/cjs/sync/persistence.native.js.map +1 -0
- package/dist/cjs/sync/persistence.spec.cjs +562 -0
- package/dist/cjs/sync/persistence.spec.native.js +572 -0
- package/dist/cjs/sync/persistence.spec.native.js.map +1 -0
- package/dist/cjs/sync/realtime.cjs +214 -0
- package/dist/cjs/sync/realtime.native.js +340 -0
- package/dist/cjs/sync/realtime.native.js.map +1 -0
- package/dist/cjs/sync/realtime.spec.cjs +499 -0
- package/dist/cjs/sync/realtime.spec.native.js +512 -0
- package/dist/cjs/sync/realtime.spec.native.js.map +1 -0
- package/dist/cjs/sync/rehydration.cjs +323 -0
- package/dist/cjs/sync/rehydration.native.js +531 -0
- package/dist/cjs/sync/rehydration.native.js.map +1 -0
- package/dist/cjs/sync/rehydration.spec.cjs +731 -0
- package/dist/cjs/sync/rehydration.spec.native.js +736 -0
- package/dist/cjs/sync/rehydration.spec.native.js.map +1 -0
- package/dist/cjs/sync/storage.cjs +142 -0
- package/dist/cjs/sync/storage.native.js +209 -0
- package/dist/cjs/sync/storage.native.js.map +1 -0
- package/dist/cjs/sync/storage.spec.cjs +446 -0
- package/dist/cjs/sync/storage.spec.native.js +470 -0
- package/dist/cjs/sync/storage.spec.native.js.map +1 -0
- package/dist/cjs/sync/subscriptionManager.cjs +487 -0
- package/dist/cjs/sync/subscriptionManager.native.js +948 -0
- package/dist/cjs/sync/subscriptionManager.native.js.map +1 -0
- package/dist/cjs/sync/subscriptionManager.spec.cjs +1102 -0
- package/dist/cjs/sync/subscriptionManager.spec.native.js +1136 -0
- package/dist/cjs/sync/subscriptionManager.spec.native.js.map +1 -0
- package/dist/cjs/sync/subscriptions.spec.cjs +937 -0
- package/dist/cjs/sync/subscriptions.spec.native.js +969 -0
- package/dist/cjs/sync/subscriptions.spec.native.js.map +1 -0
- package/dist/cjs/sync/syncModule.spec.cjs +206 -0
- package/dist/cjs/sync/syncModule.spec.native.js +222 -0
- package/dist/cjs/sync/syncModule.spec.native.js.map +1 -0
- package/dist/cjs/sync/types.cjs +99 -0
- package/dist/cjs/sync/types.native.js +102 -0
- package/dist/cjs/sync/types.native.js.map +1 -0
- package/dist/cjs/sync/types.spec.cjs +246 -0
- package/dist/cjs/sync/types.spec.native.js +256 -0
- package/dist/cjs/sync/types.spec.native.js.map +1 -0
- package/dist/cjs/sync/virtualPageManager.cjs +821 -0
- package/dist/cjs/sync/virtualPageManager.native.js +1125 -0
- package/dist/cjs/sync/virtualPageManager.native.js.map +1 -0
- package/dist/cjs/sync/virtualPageManager.spec.cjs +801 -0
- package/dist/cjs/sync/virtualPageManager.spec.native.js +873 -0
- package/dist/cjs/sync/virtualPageManager.spec.native.js.map +1 -0
- package/dist/cjs/util.cjs +31 -0
- package/dist/cjs/util.native.js +34 -0
- package/dist/cjs/util.native.js.map +1 -0
- package/dist/cjs/util.spec.cjs +43 -0
- package/dist/cjs/util.spec.native.js +48 -0
- package/dist/cjs/util.spec.native.js.map +1 -0
- package/dist/esm/auth.mjs +25 -0
- package/dist/esm/auth.mjs.map +1 -0
- package/dist/esm/auth.native.js +72 -0
- package/dist/esm/auth.native.js.map +1 -0
- package/dist/esm/auth.spec.mjs +323 -0
- package/dist/esm/auth.spec.mjs.map +1 -0
- package/dist/esm/auth.spec.native.js +327 -0
- package/dist/esm/auth.spec.native.js.map +1 -0
- package/dist/esm/call.mjs +20 -0
- package/dist/esm/call.mjs.map +1 -0
- package/dist/esm/call.native.js +65 -0
- package/dist/esm/call.native.js.map +1 -0
- package/dist/esm/call.spec.mjs +428 -0
- package/dist/esm/call.spec.mjs.map +1 -0
- package/dist/esm/call.spec.native.js +428 -0
- package/dist/esm/call.spec.native.js.map +1 -0
- package/dist/esm/collection.mjs +250 -0
- package/dist/esm/collection.mjs.map +1 -0
- package/dist/esm/collection.native.js +463 -0
- package/dist/esm/collection.native.js.map +1 -0
- package/dist/esm/collection.spec.mjs +187 -0
- package/dist/esm/collection.spec.mjs.map +1 -0
- package/dist/esm/collection.spec.native.js +214 -0
- package/dist/esm/collection.spec.native.js.map +1 -0
- package/dist/esm/common/types.mjs +2 -0
- package/dist/esm/common/types.mjs.map +1 -0
- package/dist/esm/common/types.native.js +2 -0
- package/dist/esm/common/types.native.js.map +1 -0
- package/dist/esm/common/types.spec.mjs +267 -0
- package/dist/esm/common/types.spec.mjs.map +1 -0
- package/dist/esm/common/types.spec.native.js +269 -0
- package/dist/esm/common/types.spec.native.js.map +1 -0
- package/dist/esm/core/http.mjs +258 -0
- package/dist/esm/core/http.mjs.map +1 -0
- package/dist/esm/core/http.native.js +450 -0
- package/dist/esm/core/http.native.js.map +1 -0
- package/dist/esm/core/http.spec.mjs +1260 -0
- package/dist/esm/core/http.spec.mjs.map +1 -0
- package/dist/esm/core/http.spec.native.js +1399 -0
- package/dist/esm/core/http.spec.native.js.map +1 -0
- package/dist/esm/core/types.mjs +26 -0
- package/dist/esm/core/types.mjs.map +1 -0
- package/dist/esm/core/types.native.js +148 -0
- package/dist/esm/core/types.native.js.map +1 -0
- package/dist/esm/core/wire.mjs +23 -0
- package/dist/esm/core/wire.mjs.map +1 -0
- package/dist/esm/core/wire.native.js +23 -0
- package/dist/esm/core/wire.native.js.map +1 -0
- package/dist/esm/core/wire.spec.mjs +479 -0
- package/dist/esm/core/wire.spec.mjs.map +1 -0
- package/dist/esm/core/wire.spec.native.js +480 -0
- package/dist/esm/core/wire.spec.native.js.map +1 -0
- package/dist/esm/db.mjs +329 -0
- package/dist/esm/db.mjs.map +1 -0
- package/dist/esm/db.native.js +440 -0
- package/dist/esm/db.native.js.map +1 -0
- package/dist/esm/db.spec.mjs +863 -0
- package/dist/esm/db.spec.mjs.map +1 -0
- package/dist/esm/db.spec.native.js +863 -0
- package/dist/esm/db.spec.native.js.map +1 -0
- package/dist/esm/devtools/FrappeDevtoolsPanel.mjs +813 -0
- package/dist/esm/devtools/FrappeDevtoolsPanel.mjs.map +1 -0
- package/dist/esm/devtools/FrappeDevtoolsPanel.native.js +880 -0
- package/dist/esm/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
- package/dist/esm/devtools/index.mjs +4 -0
- package/dist/esm/devtools/index.mjs.map +1 -0
- package/dist/esm/devtools/index.native.js +4 -0
- package/dist/esm/devtools/index.native.js.map +1 -0
- package/dist/esm/devtools/plugin.mjs +14 -0
- package/dist/esm/devtools/plugin.mjs.map +1 -0
- package/dist/esm/devtools/plugin.native.js +17 -0
- package/dist/esm/devtools/plugin.native.js.map +1 -0
- package/dist/esm/devtools/theme.mjs +30 -0
- package/dist/esm/devtools/theme.mjs.map +1 -0
- package/dist/esm/devtools/theme.native.js +30 -0
- package/dist/esm/devtools/theme.native.js.map +1 -0
- package/dist/esm/doctype.mjs +381 -0
- package/dist/esm/doctype.mjs.map +1 -0
- package/dist/esm/doctype.native.js +489 -0
- package/dist/esm/doctype.native.js.map +1 -0
- package/dist/esm/doctype.spec.mjs +461 -0
- package/dist/esm/doctype.spec.mjs.map +1 -0
- package/dist/esm/doctype.spec.native.js +536 -0
- package/dist/esm/doctype.spec.native.js.map +1 -0
- package/dist/esm/file.mjs +16 -0
- package/dist/esm/file.mjs.map +1 -0
- package/dist/esm/file.native.js +56 -0
- package/dist/esm/file.native.js.map +1 -0
- package/dist/esm/file.spec.mjs +316 -0
- package/dist/esm/file.spec.mjs.map +1 -0
- package/dist/esm/file.spec.native.js +340 -0
- package/dist/esm/file.spec.native.js.map +1 -0
- package/dist/esm/fixtureDb.mjs +187 -0
- package/dist/esm/fixtureDb.mjs.map +1 -0
- package/dist/esm/fixtureDb.native.js +285 -0
- package/dist/esm/fixtureDb.native.js.map +1 -0
- package/dist/esm/fixtures.mjs +174 -0
- package/dist/esm/fixtures.mjs.map +1 -0
- package/dist/esm/fixtures.native.js +372 -0
- package/dist/esm/fixtures.native.js.map +1 -0
- package/dist/esm/frappeApp.mjs +92 -0
- package/dist/esm/frappeApp.mjs.map +1 -0
- package/dist/esm/frappeApp.native.js +151 -0
- package/dist/esm/frappeApp.native.js.map +1 -0
- package/dist/esm/frappeApp.spec.mjs +307 -0
- package/dist/esm/frappeApp.spec.mjs.map +1 -0
- package/dist/esm/frappeApp.spec.native.js +306 -0
- package/dist/esm/frappeApp.spec.native.js.map +1 -0
- package/dist/esm/frappeReact.mjs +286 -0
- package/dist/esm/frappeReact.mjs.map +1 -0
- package/dist/esm/frappeReact.native.js +320 -0
- package/dist/esm/frappeReact.native.js.map +1 -0
- package/dist/esm/frappeReact.spec.mjs +532 -0
- package/dist/esm/frappeReact.spec.mjs.map +1 -0
- package/dist/esm/frappeReact.spec.native.js +668 -0
- package/dist/esm/frappeReact.spec.native.js.map +1 -0
- package/dist/esm/index.mjs +25 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +25 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/optimistic.mjs +3 -0
- package/dist/esm/optimistic.mjs.map +1 -0
- package/dist/esm/optimistic.native.js +3 -0
- package/dist/esm/optimistic.native.js.map +1 -0
- package/dist/esm/optimistic.spec.mjs +12 -0
- package/dist/esm/optimistic.spec.mjs.map +1 -0
- package/dist/esm/optimistic.spec.native.js +17 -0
- package/dist/esm/optimistic.spec.native.js.map +1 -0
- package/dist/esm/pacer.mjs +173 -0
- package/dist/esm/pacer.mjs.map +1 -0
- package/dist/esm/pacer.native.js +244 -0
- package/dist/esm/pacer.native.js.map +1 -0
- package/dist/esm/pacer.spec.mjs +447 -0
- package/dist/esm/pacer.spec.mjs.map +1 -0
- package/dist/esm/pacer.spec.native.js +511 -0
- package/dist/esm/pacer.spec.native.js.map +1 -0
- package/dist/esm/plugin/index.mjs +99 -0
- package/dist/esm/plugin/index.mjs.map +1 -0
- package/dist/esm/plugin/index.native.js +165 -0
- package/dist/esm/plugin/index.native.js.map +1 -0
- package/dist/esm/realtime.mjs +239 -0
- package/dist/esm/realtime.mjs.map +1 -0
- package/dist/esm/realtime.native.js +358 -0
- package/dist/esm/realtime.native.js.map +1 -0
- package/dist/esm/realtime.spec.mjs +327 -0
- package/dist/esm/realtime.spec.mjs.map +1 -0
- package/dist/esm/realtime.spec.native.js +390 -0
- package/dist/esm/realtime.spec.native.js.map +1 -0
- package/dist/esm/sync/backfill.mjs +287 -0
- package/dist/esm/sync/backfill.mjs.map +1 -0
- package/dist/esm/sync/backfill.native.js +457 -0
- package/dist/esm/sync/backfill.native.js.map +1 -0
- package/dist/esm/sync/backfill.spec.mjs +563 -0
- package/dist/esm/sync/backfill.spec.mjs.map +1 -0
- package/dist/esm/sync/backfill.spec.native.js +686 -0
- package/dist/esm/sync/backfill.spec.native.js.map +1 -0
- package/dist/esm/sync/debug.spec.mjs +102 -0
- package/dist/esm/sync/debug.spec.mjs.map +1 -0
- package/dist/esm/sync/debug.spec.native.js +107 -0
- package/dist/esm/sync/debug.spec.native.js.map +1 -0
- package/dist/esm/sync/filters.mjs +276 -0
- package/dist/esm/sync/filters.mjs.map +1 -0
- package/dist/esm/sync/filters.native.js +408 -0
- package/dist/esm/sync/filters.native.js.map +1 -0
- package/dist/esm/sync/filters.spec.mjs +169 -0
- package/dist/esm/sync/filters.spec.mjs.map +1 -0
- package/dist/esm/sync/filters.spec.native.js +171 -0
- package/dist/esm/sync/filters.spec.native.js.map +1 -0
- package/dist/esm/sync/index.mjs +278 -0
- package/dist/esm/sync/index.mjs.map +1 -0
- package/dist/esm/sync/index.native.js +434 -0
- package/dist/esm/sync/index.native.js.map +1 -0
- package/dist/esm/sync/merge.mjs +396 -0
- package/dist/esm/sync/merge.mjs.map +1 -0
- package/dist/esm/sync/merge.native.js +675 -0
- package/dist/esm/sync/merge.native.js.map +1 -0
- package/dist/esm/sync/merge.spec.mjs +909 -0
- package/dist/esm/sync/merge.spec.mjs.map +1 -0
- package/dist/esm/sync/merge.spec.native.js +935 -0
- package/dist/esm/sync/merge.spec.native.js.map +1 -0
- package/dist/esm/sync/multiOrderBy.spec.mjs +166 -0
- package/dist/esm/sync/multiOrderBy.spec.mjs.map +1 -0
- package/dist/esm/sync/multiOrderBy.spec.native.js +227 -0
- package/dist/esm/sync/multiOrderBy.spec.native.js.map +1 -0
- package/dist/esm/sync/mutationEngine.mjs +756 -0
- package/dist/esm/sync/mutationEngine.mjs.map +1 -0
- package/dist/esm/sync/mutationEngine.native.js +1131 -0
- package/dist/esm/sync/mutationEngine.native.js.map +1 -0
- package/dist/esm/sync/mutationEngine.spec.mjs +1159 -0
- package/dist/esm/sync/mutationEngine.spec.mjs.map +1 -0
- package/dist/esm/sync/mutationEngine.spec.native.js +1171 -0
- package/dist/esm/sync/mutationEngine.spec.native.js.map +1 -0
- package/dist/esm/sync/mutationJournal.mjs +124 -0
- package/dist/esm/sync/mutationJournal.mjs.map +1 -0
- package/dist/esm/sync/mutationJournal.native.js +253 -0
- package/dist/esm/sync/mutationJournal.native.js.map +1 -0
- package/dist/esm/sync/mutationJournal.spec.mjs +331 -0
- package/dist/esm/sync/mutationJournal.spec.mjs.map +1 -0
- package/dist/esm/sync/mutationJournal.spec.native.js +331 -0
- package/dist/esm/sync/mutationJournal.spec.native.js.map +1 -0
- package/dist/esm/sync/mutations.spec.mjs +328 -0
- package/dist/esm/sync/mutations.spec.mjs.map +1 -0
- package/dist/esm/sync/mutations.spec.native.js +328 -0
- package/dist/esm/sync/mutations.spec.native.js.map +1 -0
- package/dist/esm/sync/normalizedCache.mjs +115 -0
- package/dist/esm/sync/normalizedCache.mjs.map +1 -0
- package/dist/esm/sync/normalizedCache.native.js +201 -0
- package/dist/esm/sync/normalizedCache.native.js.map +1 -0
- package/dist/esm/sync/normalizedCache.spec.mjs +303 -0
- package/dist/esm/sync/normalizedCache.spec.mjs.map +1 -0
- package/dist/esm/sync/normalizedCache.spec.native.js +309 -0
- package/dist/esm/sync/normalizedCache.spec.native.js.map +1 -0
- package/dist/esm/sync/persistence.mjs +602 -0
- package/dist/esm/sync/persistence.mjs.map +1 -0
- package/dist/esm/sync/persistence.native.js +883 -0
- package/dist/esm/sync/persistence.native.js.map +1 -0
- package/dist/esm/sync/persistence.spec.mjs +563 -0
- package/dist/esm/sync/persistence.spec.mjs.map +1 -0
- package/dist/esm/sync/persistence.spec.native.js +570 -0
- package/dist/esm/sync/persistence.spec.native.js.map +1 -0
- package/dist/esm/sync/realtime.mjs +189 -0
- package/dist/esm/sync/realtime.mjs.map +1 -0
- package/dist/esm/sync/realtime.native.js +312 -0
- package/dist/esm/sync/realtime.native.js.map +1 -0
- package/dist/esm/sync/realtime.spec.mjs +500 -0
- package/dist/esm/sync/realtime.spec.mjs.map +1 -0
- package/dist/esm/sync/realtime.spec.native.js +510 -0
- package/dist/esm/sync/realtime.spec.native.js.map +1 -0
- package/dist/esm/sync/rehydration.mjs +298 -0
- package/dist/esm/sync/rehydration.mjs.map +1 -0
- package/dist/esm/sync/rehydration.native.js +503 -0
- package/dist/esm/sync/rehydration.native.js.map +1 -0
- package/dist/esm/sync/rehydration.spec.mjs +732 -0
- package/dist/esm/sync/rehydration.spec.mjs.map +1 -0
- package/dist/esm/sync/rehydration.spec.native.js +734 -0
- package/dist/esm/sync/rehydration.spec.native.js.map +1 -0
- package/dist/esm/sync/storage.mjs +116 -0
- package/dist/esm/sync/storage.mjs.map +1 -0
- package/dist/esm/sync/storage.native.js +180 -0
- package/dist/esm/sync/storage.native.js.map +1 -0
- package/dist/esm/sync/storage.spec.mjs +423 -0
- package/dist/esm/sync/storage.spec.mjs.map +1 -0
- package/dist/esm/sync/storage.spec.native.js +444 -0
- package/dist/esm/sync/storage.spec.native.js.map +1 -0
- package/dist/esm/sync/subscriptionManager.mjs +462 -0
- package/dist/esm/sync/subscriptionManager.mjs.map +1 -0
- package/dist/esm/sync/subscriptionManager.native.js +920 -0
- package/dist/esm/sync/subscriptionManager.native.js.map +1 -0
- package/dist/esm/sync/subscriptionManager.spec.mjs +1079 -0
- package/dist/esm/sync/subscriptionManager.spec.mjs.map +1 -0
- package/dist/esm/sync/subscriptionManager.spec.native.js +1110 -0
- package/dist/esm/sync/subscriptionManager.spec.native.js.map +1 -0
- package/dist/esm/sync/subscriptions.spec.mjs +914 -0
- package/dist/esm/sync/subscriptions.spec.mjs.map +1 -0
- package/dist/esm/sync/subscriptions.spec.native.js +943 -0
- package/dist/esm/sync/subscriptions.spec.native.js.map +1 -0
- package/dist/esm/sync/syncModule.spec.mjs +207 -0
- package/dist/esm/sync/syncModule.spec.mjs.map +1 -0
- package/dist/esm/sync/syncModule.spec.native.js +220 -0
- package/dist/esm/sync/syncModule.spec.native.js.map +1 -0
- package/dist/esm/sync/types.mjs +65 -0
- package/dist/esm/sync/types.mjs.map +1 -0
- package/dist/esm/sync/types.native.js +65 -0
- package/dist/esm/sync/types.native.js.map +1 -0
- package/dist/esm/sync/types.spec.mjs +247 -0
- package/dist/esm/sync/types.spec.mjs.map +1 -0
- package/dist/esm/sync/types.spec.native.js +254 -0
- package/dist/esm/sync/types.spec.native.js.map +1 -0
- package/dist/esm/sync/virtualPageManager.mjs +794 -0
- package/dist/esm/sync/virtualPageManager.mjs.map +1 -0
- package/dist/esm/sync/virtualPageManager.native.js +1095 -0
- package/dist/esm/sync/virtualPageManager.native.js.map +1 -0
- package/dist/esm/sync/virtualPageManager.spec.mjs +802 -0
- package/dist/esm/sync/virtualPageManager.spec.mjs.map +1 -0
- package/dist/esm/sync/virtualPageManager.spec.native.js +871 -0
- package/dist/esm/sync/virtualPageManager.spec.native.js.map +1 -0
- package/dist/esm/util.mjs +6 -0
- package/dist/esm/util.mjs.map +1 -0
- package/dist/esm/util.native.js +6 -0
- package/dist/esm/util.native.js.map +1 -0
- package/dist/esm/util.spec.mjs +20 -0
- package/dist/esm/util.spec.mjs.map +1 -0
- package/dist/esm/util.spec.native.js +22 -0
- package/dist/esm/util.spec.native.js.map +1 -0
- package/dist/jsx/auth.mjs +25 -0
- package/dist/jsx/auth.mjs.map +1 -0
- package/dist/jsx/auth.native.js +100 -0
- package/dist/jsx/auth.native.js.map +1 -0
- package/dist/jsx/auth.spec.mjs +323 -0
- package/dist/jsx/auth.spec.mjs.map +1 -0
- package/dist/jsx/auth.spec.native.js +329 -0
- package/dist/jsx/auth.spec.native.js.map +1 -0
- package/dist/jsx/call.mjs +20 -0
- package/dist/jsx/call.mjs.map +1 -0
- package/dist/jsx/call.native.js +93 -0
- package/dist/jsx/call.native.js.map +1 -0
- package/dist/jsx/call.spec.mjs +428 -0
- package/dist/jsx/call.spec.mjs.map +1 -0
- package/dist/jsx/call.spec.native.js +430 -0
- package/dist/jsx/call.spec.native.js.map +1 -0
- package/dist/jsx/collection.mjs +250 -0
- package/dist/jsx/collection.mjs.map +1 -0
- package/dist/jsx/collection.native.js +494 -0
- package/dist/jsx/collection.native.js.map +1 -0
- package/dist/jsx/collection.spec.mjs +187 -0
- package/dist/jsx/collection.spec.mjs.map +1 -0
- package/dist/jsx/collection.spec.native.js +216 -0
- package/dist/jsx/collection.spec.native.js.map +1 -0
- package/dist/jsx/common/types.mjs +2 -0
- package/dist/jsx/common/types.mjs.map +1 -0
- package/dist/jsx/common/types.native.js +21 -0
- package/dist/jsx/common/types.native.js.map +1 -0
- package/dist/jsx/common/types.spec.mjs +267 -0
- package/dist/jsx/common/types.spec.mjs.map +1 -0
- package/dist/jsx/common/types.spec.native.js +271 -0
- package/dist/jsx/common/types.spec.native.js.map +1 -0
- package/dist/jsx/core/http.mjs +258 -0
- package/dist/jsx/core/http.mjs.map +1 -0
- package/dist/jsx/core/http.native.js +484 -0
- package/dist/jsx/core/http.native.js.map +1 -0
- package/dist/jsx/core/http.spec.mjs +1260 -0
- package/dist/jsx/core/http.spec.mjs.map +1 -0
- package/dist/jsx/core/http.spec.native.js +1401 -0
- package/dist/jsx/core/http.spec.native.js.map +1 -0
- package/dist/jsx/core/types.mjs +26 -0
- package/dist/jsx/core/types.mjs.map +1 -0
- package/dist/jsx/core/types.native.js +178 -0
- package/dist/jsx/core/types.native.js.map +1 -0
- package/dist/jsx/core/wire.mjs +23 -0
- package/dist/jsx/core/wire.mjs.map +1 -0
- package/dist/jsx/core/wire.native.js +56 -0
- package/dist/jsx/core/wire.native.js.map +1 -0
- package/dist/jsx/core/wire.spec.mjs +479 -0
- package/dist/jsx/core/wire.spec.mjs.map +1 -0
- package/dist/jsx/core/wire.spec.native.js +482 -0
- package/dist/jsx/core/wire.spec.native.js.map +1 -0
- package/dist/jsx/db.mjs +329 -0
- package/dist/jsx/db.mjs.map +1 -0
- package/dist/jsx/db.native.js +469 -0
- package/dist/jsx/db.native.js.map +1 -0
- package/dist/jsx/db.spec.mjs +863 -0
- package/dist/jsx/db.spec.mjs.map +1 -0
- package/dist/jsx/db.spec.native.js +865 -0
- package/dist/jsx/db.spec.native.js.map +1 -0
- package/dist/jsx/devtools/FrappeDevtoolsPanel.mjs +813 -0
- package/dist/jsx/devtools/FrappeDevtoolsPanel.mjs.map +1 -0
- package/dist/jsx/devtools/FrappeDevtoolsPanel.native.js +919 -0
- package/dist/jsx/devtools/FrappeDevtoolsPanel.native.js.map +1 -0
- package/dist/jsx/devtools/index.mjs +4 -0
- package/dist/jsx/devtools/index.mjs.map +1 -0
- package/dist/jsx/devtools/index.native.js +33 -0
- package/dist/jsx/devtools/index.native.js.map +1 -0
- package/dist/jsx/devtools/plugin.mjs +14 -0
- package/dist/jsx/devtools/plugin.mjs.map +1 -0
- package/dist/jsx/devtools/plugin.native.js +56 -0
- package/dist/jsx/devtools/plugin.native.js.map +1 -0
- package/dist/jsx/devtools/theme.mjs +30 -0
- package/dist/jsx/devtools/theme.mjs.map +1 -0
- package/dist/jsx/devtools/theme.native.js +58 -0
- package/dist/jsx/devtools/theme.native.js.map +1 -0
- package/dist/jsx/doctype.mjs +381 -0
- package/dist/jsx/doctype.mjs.map +1 -0
- package/dist/jsx/doctype.native.js +521 -0
- package/dist/jsx/doctype.native.js.map +1 -0
- package/dist/jsx/doctype.spec.mjs +461 -0
- package/dist/jsx/doctype.spec.mjs.map +1 -0
- package/dist/jsx/doctype.spec.native.js +538 -0
- package/dist/jsx/doctype.spec.native.js.map +1 -0
- package/dist/jsx/file.mjs +16 -0
- package/dist/jsx/file.mjs.map +1 -0
- package/dist/jsx/file.native.js +84 -0
- package/dist/jsx/file.native.js.map +1 -0
- package/dist/jsx/file.spec.mjs +316 -0
- package/dist/jsx/file.spec.mjs.map +1 -0
- package/dist/jsx/file.spec.native.js +342 -0
- package/dist/jsx/file.spec.native.js.map +1 -0
- package/dist/jsx/fixtureDb.mjs +187 -0
- package/dist/jsx/fixtureDb.mjs.map +1 -0
- package/dist/jsx/fixtureDb.native.js +313 -0
- package/dist/jsx/fixtureDb.native.js.map +1 -0
- package/dist/jsx/fixtures.mjs +174 -0
- package/dist/jsx/fixtures.mjs.map +1 -0
- package/dist/jsx/fixtures.native.js +400 -0
- package/dist/jsx/fixtures.native.js.map +1 -0
- package/dist/jsx/frappeApp.mjs +92 -0
- package/dist/jsx/frappeApp.mjs.map +1 -0
- package/dist/jsx/frappeApp.native.js +179 -0
- package/dist/jsx/frappeApp.native.js.map +1 -0
- package/dist/jsx/frappeApp.spec.mjs +307 -0
- package/dist/jsx/frappeApp.spec.mjs.map +1 -0
- package/dist/jsx/frappeApp.spec.native.js +308 -0
- package/dist/jsx/frappeApp.spec.native.js.map +1 -0
- package/dist/jsx/frappeReact.mjs +286 -0
- package/dist/jsx/frappeReact.mjs.map +1 -0
- package/dist/jsx/frappeReact.native.js +351 -0
- package/dist/jsx/frappeReact.native.js.map +1 -0
- package/dist/jsx/frappeReact.spec.mjs +532 -0
- package/dist/jsx/frappeReact.spec.mjs.map +1 -0
- package/dist/jsx/frappeReact.spec.native.js +670 -0
- package/dist/jsx/frappeReact.spec.native.js.map +1 -0
- package/dist/jsx/index.js +25 -0
- package/dist/jsx/index.js.map +1 -0
- package/dist/jsx/index.mjs +25 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +152 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/dist/jsx/optimistic.mjs +3 -0
- package/dist/jsx/optimistic.mjs.map +1 -0
- package/dist/jsx/optimistic.native.js +31 -0
- package/dist/jsx/optimistic.native.js.map +1 -0
- package/dist/jsx/optimistic.spec.mjs +12 -0
- package/dist/jsx/optimistic.spec.mjs.map +1 -0
- package/dist/jsx/optimistic.spec.native.js +19 -0
- package/dist/jsx/optimistic.spec.native.js.map +1 -0
- package/dist/jsx/pacer.mjs +173 -0
- package/dist/jsx/pacer.mjs.map +1 -0
- package/dist/jsx/pacer.native.js +274 -0
- package/dist/jsx/pacer.native.js.map +1 -0
- package/dist/jsx/pacer.spec.mjs +447 -0
- package/dist/jsx/pacer.spec.mjs.map +1 -0
- package/dist/jsx/pacer.spec.native.js +537 -0
- package/dist/jsx/pacer.spec.native.js.map +1 -0
- package/dist/jsx/plugin/index.mjs +99 -0
- package/dist/jsx/plugin/index.mjs.map +1 -0
- package/dist/jsx/plugin/index.native.js +193 -0
- package/dist/jsx/plugin/index.native.js.map +1 -0
- package/dist/jsx/realtime.mjs +239 -0
- package/dist/jsx/realtime.mjs.map +1 -0
- package/dist/jsx/realtime.native.js +387 -0
- package/dist/jsx/realtime.native.js.map +1 -0
- package/dist/jsx/realtime.spec.mjs +327 -0
- package/dist/jsx/realtime.spec.mjs.map +1 -0
- package/dist/jsx/realtime.spec.native.js +392 -0
- package/dist/jsx/realtime.spec.native.js.map +1 -0
- package/dist/jsx/sync/backfill.mjs +287 -0
- package/dist/jsx/sync/backfill.mjs.map +1 -0
- package/dist/jsx/sync/backfill.native.js +487 -0
- package/dist/jsx/sync/backfill.native.js.map +1 -0
- package/dist/jsx/sync/backfill.spec.mjs +563 -0
- package/dist/jsx/sync/backfill.spec.mjs.map +1 -0
- package/dist/jsx/sync/backfill.spec.native.js +712 -0
- package/dist/jsx/sync/backfill.spec.native.js.map +1 -0
- package/dist/jsx/sync/debug.spec.mjs +102 -0
- package/dist/jsx/sync/debug.spec.mjs.map +1 -0
- package/dist/jsx/sync/debug.spec.native.js +133 -0
- package/dist/jsx/sync/debug.spec.native.js.map +1 -0
- package/dist/jsx/sync/filters.mjs +276 -0
- package/dist/jsx/sync/filters.mjs.map +1 -0
- package/dist/jsx/sync/filters.native.js +446 -0
- package/dist/jsx/sync/filters.native.js.map +1 -0
- package/dist/jsx/sync/filters.spec.mjs +169 -0
- package/dist/jsx/sync/filters.spec.mjs.map +1 -0
- package/dist/jsx/sync/filters.spec.native.js +173 -0
- package/dist/jsx/sync/filters.spec.native.js.map +1 -0
- package/dist/jsx/sync/index.mjs +278 -0
- package/dist/jsx/sync/index.mjs.map +1 -0
- package/dist/jsx/sync/index.native.js +462 -0
- package/dist/jsx/sync/index.native.js.map +1 -0
- package/dist/jsx/sync/merge.mjs +396 -0
- package/dist/jsx/sync/merge.mjs.map +1 -0
- package/dist/jsx/sync/merge.native.js +717 -0
- package/dist/jsx/sync/merge.native.js.map +1 -0
- package/dist/jsx/sync/merge.spec.mjs +909 -0
- package/dist/jsx/sync/merge.spec.mjs.map +1 -0
- package/dist/jsx/sync/merge.spec.native.js +937 -0
- package/dist/jsx/sync/merge.spec.native.js.map +1 -0
- package/dist/jsx/sync/multiOrderBy.spec.mjs +166 -0
- package/dist/jsx/sync/multiOrderBy.spec.mjs.map +1 -0
- package/dist/jsx/sync/multiOrderBy.spec.native.js +229 -0
- package/dist/jsx/sync/multiOrderBy.spec.native.js.map +1 -0
- package/dist/jsx/sync/mutationEngine.mjs +756 -0
- package/dist/jsx/sync/mutationEngine.mjs.map +1 -0
- package/dist/jsx/sync/mutationEngine.native.js +1159 -0
- package/dist/jsx/sync/mutationEngine.native.js.map +1 -0
- package/dist/jsx/sync/mutationEngine.spec.mjs +1159 -0
- package/dist/jsx/sync/mutationEngine.spec.mjs.map +1 -0
- package/dist/jsx/sync/mutationEngine.spec.native.js +1173 -0
- package/dist/jsx/sync/mutationEngine.spec.native.js.map +1 -0
- package/dist/jsx/sync/mutationJournal.mjs +124 -0
- package/dist/jsx/sync/mutationJournal.mjs.map +1 -0
- package/dist/jsx/sync/mutationJournal.native.js +282 -0
- package/dist/jsx/sync/mutationJournal.native.js.map +1 -0
- package/dist/jsx/sync/mutationJournal.spec.mjs +331 -0
- package/dist/jsx/sync/mutationJournal.spec.mjs.map +1 -0
- package/dist/jsx/sync/mutationJournal.spec.native.js +333 -0
- package/dist/jsx/sync/mutationJournal.spec.native.js.map +1 -0
- package/dist/jsx/sync/mutations.spec.mjs +328 -0
- package/dist/jsx/sync/mutations.spec.mjs.map +1 -0
- package/dist/jsx/sync/mutations.spec.native.js +330 -0
- package/dist/jsx/sync/mutations.spec.native.js.map +1 -0
- package/dist/jsx/sync/normalizedCache.mjs +115 -0
- package/dist/jsx/sync/normalizedCache.mjs.map +1 -0
- package/dist/jsx/sync/normalizedCache.native.js +229 -0
- package/dist/jsx/sync/normalizedCache.native.js.map +1 -0
- package/dist/jsx/sync/normalizedCache.spec.mjs +303 -0
- package/dist/jsx/sync/normalizedCache.spec.mjs.map +1 -0
- package/dist/jsx/sync/normalizedCache.spec.native.js +311 -0
- package/dist/jsx/sync/normalizedCache.spec.native.js.map +1 -0
- package/dist/jsx/sync/persistence.mjs +602 -0
- package/dist/jsx/sync/persistence.mjs.map +1 -0
- package/dist/jsx/sync/persistence.native.js +911 -0
- package/dist/jsx/sync/persistence.native.js.map +1 -0
- package/dist/jsx/sync/persistence.spec.mjs +563 -0
- package/dist/jsx/sync/persistence.spec.mjs.map +1 -0
- package/dist/jsx/sync/persistence.spec.native.js +572 -0
- package/dist/jsx/sync/persistence.spec.native.js.map +1 -0
- package/dist/jsx/sync/realtime.mjs +189 -0
- package/dist/jsx/sync/realtime.mjs.map +1 -0
- package/dist/jsx/sync/realtime.native.js +340 -0
- package/dist/jsx/sync/realtime.native.js.map +1 -0
- package/dist/jsx/sync/realtime.spec.mjs +500 -0
- package/dist/jsx/sync/realtime.spec.mjs.map +1 -0
- package/dist/jsx/sync/realtime.spec.native.js +512 -0
- package/dist/jsx/sync/realtime.spec.native.js.map +1 -0
- package/dist/jsx/sync/rehydration.mjs +298 -0
- package/dist/jsx/sync/rehydration.mjs.map +1 -0
- package/dist/jsx/sync/rehydration.native.js +531 -0
- package/dist/jsx/sync/rehydration.native.js.map +1 -0
- package/dist/jsx/sync/rehydration.spec.mjs +732 -0
- package/dist/jsx/sync/rehydration.spec.mjs.map +1 -0
- package/dist/jsx/sync/rehydration.spec.native.js +736 -0
- package/dist/jsx/sync/rehydration.spec.native.js.map +1 -0
- package/dist/jsx/sync/storage.mjs +116 -0
- package/dist/jsx/sync/storage.mjs.map +1 -0
- package/dist/jsx/sync/storage.native.js +209 -0
- package/dist/jsx/sync/storage.native.js.map +1 -0
- package/dist/jsx/sync/storage.spec.mjs +423 -0
- package/dist/jsx/sync/storage.spec.mjs.map +1 -0
- package/dist/jsx/sync/storage.spec.native.js +470 -0
- package/dist/jsx/sync/storage.spec.native.js.map +1 -0
- package/dist/jsx/sync/subscriptionManager.mjs +462 -0
- package/dist/jsx/sync/subscriptionManager.mjs.map +1 -0
- package/dist/jsx/sync/subscriptionManager.native.js +948 -0
- package/dist/jsx/sync/subscriptionManager.native.js.map +1 -0
- package/dist/jsx/sync/subscriptionManager.spec.mjs +1079 -0
- package/dist/jsx/sync/subscriptionManager.spec.mjs.map +1 -0
- package/dist/jsx/sync/subscriptionManager.spec.native.js +1136 -0
- package/dist/jsx/sync/subscriptionManager.spec.native.js.map +1 -0
- package/dist/jsx/sync/subscriptions.spec.mjs +914 -0
- package/dist/jsx/sync/subscriptions.spec.mjs.map +1 -0
- package/dist/jsx/sync/subscriptions.spec.native.js +969 -0
- package/dist/jsx/sync/subscriptions.spec.native.js.map +1 -0
- package/dist/jsx/sync/syncModule.spec.mjs +207 -0
- package/dist/jsx/sync/syncModule.spec.mjs.map +1 -0
- package/dist/jsx/sync/syncModule.spec.native.js +222 -0
- package/dist/jsx/sync/syncModule.spec.native.js.map +1 -0
- package/dist/jsx/sync/types.mjs +65 -0
- package/dist/jsx/sync/types.mjs.map +1 -0
- package/dist/jsx/sync/types.native.js +102 -0
- package/dist/jsx/sync/types.native.js.map +1 -0
- package/dist/jsx/sync/types.spec.mjs +247 -0
- package/dist/jsx/sync/types.spec.mjs.map +1 -0
- package/dist/jsx/sync/types.spec.native.js +256 -0
- package/dist/jsx/sync/types.spec.native.js.map +1 -0
- package/dist/jsx/sync/virtualPageManager.mjs +794 -0
- package/dist/jsx/sync/virtualPageManager.mjs.map +1 -0
- package/dist/jsx/sync/virtualPageManager.native.js +1125 -0
- package/dist/jsx/sync/virtualPageManager.native.js.map +1 -0
- package/dist/jsx/sync/virtualPageManager.spec.mjs +802 -0
- package/dist/jsx/sync/virtualPageManager.spec.mjs.map +1 -0
- package/dist/jsx/sync/virtualPageManager.spec.native.js +873 -0
- package/dist/jsx/sync/virtualPageManager.spec.native.js.map +1 -0
- package/dist/jsx/util.mjs +6 -0
- package/dist/jsx/util.mjs.map +1 -0
- package/dist/jsx/util.native.js +34 -0
- package/dist/jsx/util.native.js.map +1 -0
- package/dist/jsx/util.spec.mjs +20 -0
- package/dist/jsx/util.spec.mjs.map +1 -0
- package/dist/jsx/util.spec.native.js +48 -0
- package/dist/jsx/util.spec.native.js.map +1 -0
- package/package.json +96 -0
- package/src/auth.spec.ts +363 -0
- package/src/auth.ts +29 -0
- package/src/call.spec.ts +541 -0
- package/src/call.ts +21 -0
- package/src/collection.spec.ts +213 -0
- package/src/collection.ts +328 -0
- package/src/common/types.spec.ts +294 -0
- package/src/common/types.ts +41 -0
- package/src/core/http.spec.ts +1128 -0
- package/src/core/http.ts +324 -0
- package/src/core/types.ts +85 -0
- package/src/core/wire.spec.ts +570 -0
- package/src/core/wire.ts +487 -0
- package/src/db.spec.ts +1004 -0
- package/src/db.ts +589 -0
- package/src/devtools/FrappeDevtoolsPanel.tsx +873 -0
- package/src/devtools/index.ts +3 -0
- package/src/devtools/plugin.tsx +13 -0
- package/src/devtools/theme.ts +42 -0
- package/src/doctype.spec.ts +451 -0
- package/src/doctype.ts +828 -0
- package/src/file.spec.ts +347 -0
- package/src/file.ts +27 -0
- package/src/fixtureDb.ts +208 -0
- package/src/fixtures.ts +268 -0
- package/src/frappeApp.spec.ts +382 -0
- package/src/frappeApp.ts +125 -0
- package/src/frappeReact.spec.ts +505 -0
- package/src/frappeReact.ts +598 -0
- package/src/index.ts +215 -0
- package/src/optimistic.spec.ts +13 -0
- package/src/optimistic.ts +39 -0
- package/src/pacer.spec.ts +493 -0
- package/src/pacer.ts +426 -0
- package/src/plugin/README.md +162 -0
- package/src/plugin/index.ts +141 -0
- package/src/realtime.spec.ts +453 -0
- package/src/realtime.ts +279 -0
- package/src/sync/backfill.spec.ts +714 -0
- package/src/sync/backfill.ts +378 -0
- package/src/sync/debug.spec.ts +110 -0
- package/src/sync/filters.spec.ts +223 -0
- package/src/sync/filters.ts +513 -0
- package/src/sync/index.ts +412 -0
- package/src/sync/merge.spec.ts +812 -0
- package/src/sync/merge.ts +532 -0
- package/src/sync/multiOrderBy.spec.ts +187 -0
- package/src/sync/mutationEngine.spec.ts +1418 -0
- package/src/sync/mutationEngine.ts +957 -0
- package/src/sync/mutationJournal.spec.ts +337 -0
- package/src/sync/mutationJournal.ts +157 -0
- package/src/sync/mutations.spec.ts +358 -0
- package/src/sync/normalizedCache.spec.ts +262 -0
- package/src/sync/normalizedCache.ts +152 -0
- package/src/sync/persistence.spec.ts +908 -0
- package/src/sync/persistence.ts +802 -0
- package/src/sync/realtime.spec.ts +769 -0
- package/src/sync/realtime.ts +259 -0
- package/src/sync/rehydration.spec.ts +923 -0
- package/src/sync/rehydration.ts +362 -0
- package/src/sync/storage.spec.ts +473 -0
- package/src/sync/storage.ts +174 -0
- package/src/sync/subscriptionManager.spec.ts +1226 -0
- package/src/sync/subscriptionManager.ts +577 -0
- package/src/sync/subscriptions.spec.ts +1048 -0
- package/src/sync/syncModule.spec.ts +217 -0
- package/src/sync/types.spec.ts +276 -0
- package/src/sync/types.ts +384 -0
- package/src/sync/virtualPageManager.spec.ts +825 -0
- package/src/sync/virtualPageManager.ts +1069 -0
- package/src/util.spec.ts +17 -0
- package/src/util.ts +5 -0
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
import { Debouncer } from "@tanstack/pacer";
|
|
2
|
+
import type { FrappeDoc, FrappeDocData } from "../common/types";
|
|
3
|
+
import type { BatchMutation, DbModule } from "../db";
|
|
4
|
+
import type { RealtimeModule } from "../realtime";
|
|
5
|
+
import { matchesFilters } from "./filters";
|
|
6
|
+
import { merge, MergeStrategy } from "./merge";
|
|
7
|
+
import type { MergeConfig, DoctypeConfig } from "./merge";
|
|
8
|
+
import type { MutationJournal, Pending } from "./mutationJournal";
|
|
9
|
+
import type { NormalizedCache } from "./normalizedCache";
|
|
10
|
+
import type { RehydrationManager } from "./rehydration";
|
|
11
|
+
import type { SubscriptionManager } from "./subscriptionManager";
|
|
12
|
+
import type { OrderBy } from "./types";
|
|
13
|
+
import { Order } from "./types";
|
|
14
|
+
import {
|
|
15
|
+
Operation,
|
|
16
|
+
PendingRehydrationType,
|
|
17
|
+
PlacementType,
|
|
18
|
+
RehydrationReason,
|
|
19
|
+
Status,
|
|
20
|
+
} from "./types";
|
|
21
|
+
import type {
|
|
22
|
+
CreateMutation,
|
|
23
|
+
DeleteMutation,
|
|
24
|
+
SyncOptions,
|
|
25
|
+
Mutation,
|
|
26
|
+
PageBoundaries,
|
|
27
|
+
Subscription,
|
|
28
|
+
UpdateMutation,
|
|
29
|
+
} from "./types";
|
|
30
|
+
|
|
31
|
+
/** Pending batch mutation entry */
|
|
32
|
+
interface PendingBatchMutation<Data extends FrappeDocData> {
|
|
33
|
+
mutation: Mutation<Data>;
|
|
34
|
+
resolve: (result: FrappeDoc<Data> | void) => void;
|
|
35
|
+
reject: (error: unknown) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SortField {
|
|
39
|
+
field: string;
|
|
40
|
+
isAsc: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class MutationEngine {
|
|
44
|
+
private temporaryIdToRealNameMap = new Map<string, string>();
|
|
45
|
+
private globalOptimisticDocIds = new Map<string, Set<string>>();
|
|
46
|
+
/** Pending mutations waiting to be batched */
|
|
47
|
+
private pendingBatch: PendingBatchMutation<FrappeDocData>[] = [];
|
|
48
|
+
/** Debouncer for batch processing */
|
|
49
|
+
private batchDebouncer: Debouncer<() => Promise<void>>;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
private readonly db: DbModule,
|
|
53
|
+
private readonly realtime: RealtimeModule | undefined,
|
|
54
|
+
private readonly normalizedCache: NormalizedCache,
|
|
55
|
+
private readonly mutationJournal: MutationJournal,
|
|
56
|
+
private readonly subscriptionManager: SubscriptionManager,
|
|
57
|
+
private readonly rehydrationManager: RehydrationManager,
|
|
58
|
+
private readonly options: SyncOptions = {},
|
|
59
|
+
) {
|
|
60
|
+
// Initialize batch debouncer
|
|
61
|
+
const batchDelay = options.batchDelay ?? 100;
|
|
62
|
+
this.batchDebouncer = new Debouncer(() => this.flushBatch(), { wait: batchDelay });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Queue a mutation for batch processing.
|
|
67
|
+
* Returns a promise that resolves when the batch is processed.
|
|
68
|
+
*/
|
|
69
|
+
private queueForBatch<Data extends FrappeDocData>(
|
|
70
|
+
mutation: Mutation<Data>,
|
|
71
|
+
): Promise<FrappeDoc<Data> | void> {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
this.pendingBatch.push({
|
|
74
|
+
mutation: mutation as Mutation<FrappeDocData>,
|
|
75
|
+
resolve: resolve as (result: FrappeDoc<FrappeDocData> | void) => void,
|
|
76
|
+
reject,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Flush immediately if batch is full
|
|
80
|
+
const maxSize = this.options.batchMaxSize ?? 50;
|
|
81
|
+
if (this.pendingBatch.length >= maxSize) {
|
|
82
|
+
this.flushBatch();
|
|
83
|
+
} else {
|
|
84
|
+
// Schedule debounced flush
|
|
85
|
+
this.batchDebouncer.maybeExecute();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Flush pending mutations as a batch.
|
|
92
|
+
*/
|
|
93
|
+
private async flushBatch(): Promise<void> {
|
|
94
|
+
if (this.pendingBatch.length === 0) return;
|
|
95
|
+
|
|
96
|
+
// Take current batch and clear pending
|
|
97
|
+
const batch = [...this.pendingBatch];
|
|
98
|
+
this.pendingBatch = [];
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Convert to BatchMutation format
|
|
102
|
+
const batchMutations: BatchMutation<FrappeDocData>[] = batch.map(({ mutation }) => {
|
|
103
|
+
const operationMap: Record<Operation, "create" | "update" | "delete"> = {
|
|
104
|
+
[Operation.Create]: "create",
|
|
105
|
+
[Operation.Update]: "update",
|
|
106
|
+
[Operation.Delete]: "delete",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
type: operationMap[mutation.operation],
|
|
111
|
+
doctype: mutation.doc.doctype,
|
|
112
|
+
name: mutation.doc.name,
|
|
113
|
+
data: mutation.operation === Operation.Delete ? undefined : mutation.doc,
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Send batch to server
|
|
118
|
+
const results = await this.db.batchMutations({
|
|
119
|
+
mutations: batchMutations,
|
|
120
|
+
atomic: this.options.batchAtomic ?? false,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Process results
|
|
124
|
+
for (let i = 0; i < batch.length; i++) {
|
|
125
|
+
const { mutation, resolve, reject } = batch[i];
|
|
126
|
+
const result = results[i];
|
|
127
|
+
|
|
128
|
+
if (result.success) {
|
|
129
|
+
// Confirm in journal
|
|
130
|
+
this.mutationJournal.confirm(mutation.mutationId);
|
|
131
|
+
|
|
132
|
+
// Update cache with server response
|
|
133
|
+
if (result.data) {
|
|
134
|
+
this.normalizedCache.upsert(result.data);
|
|
135
|
+
|
|
136
|
+
// Handle temp ID mapping for creates
|
|
137
|
+
if (mutation.operation === Operation.Create && mutation.doc.name !== result.data.name) {
|
|
138
|
+
this.temporaryIdToRealNameMap.set(mutation.doc.name!, result.data.name);
|
|
139
|
+
this.rehydrationManager.registerTempIdMapping(mutation.doc.name!, result.data.name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this.options.onMutationSuccess) {
|
|
144
|
+
this.options.onMutationSuccess(mutation);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
resolve(result.data);
|
|
148
|
+
} else {
|
|
149
|
+
// Handle error
|
|
150
|
+
const error = new Error(result.error || "Batch mutation failed");
|
|
151
|
+
this.mutationJournal.fail(mutation.mutationId);
|
|
152
|
+
reject(error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
// Batch request failed - reject all
|
|
157
|
+
for (const { mutation, reject } of batch) {
|
|
158
|
+
this.mutationJournal.fail(mutation.mutationId);
|
|
159
|
+
reject(err);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Force flush any pending batched mutations.
|
|
166
|
+
*/
|
|
167
|
+
async forceBatchFlush(): Promise<void> {
|
|
168
|
+
this.batchDebouncer.cancel();
|
|
169
|
+
await this.flushBatch();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async mutate<Data extends FrappeDocData>(mutation: Mutation<Data>) {
|
|
173
|
+
if (this.options.onMutationStart) this.options.onMutationStart(mutation);
|
|
174
|
+
const { doc } = mutation;
|
|
175
|
+
if (
|
|
176
|
+
(mutation.operation === Operation.Update || mutation.operation === Operation.Delete) &&
|
|
177
|
+
!doc.name
|
|
178
|
+
) {
|
|
179
|
+
throw new Error("docname is required for update/delete operations");
|
|
180
|
+
}
|
|
181
|
+
let version = mutation.version;
|
|
182
|
+
let cachedDoc: FrappeDoc<FrappeDocData> | undefined = undefined;
|
|
183
|
+
if (doc.name) {
|
|
184
|
+
cachedDoc = this.normalizedCache.get({
|
|
185
|
+
doctype: doc.doctype,
|
|
186
|
+
name: doc.name,
|
|
187
|
+
}) as FrappeDoc<FrappeDocData> | undefined;
|
|
188
|
+
}
|
|
189
|
+
if (version === undefined && doc.name) {
|
|
190
|
+
const versionField =
|
|
191
|
+
this.options.versionFields?.[doc.doctype] || this.options.versionField || "modified";
|
|
192
|
+
const cachedVersion = cachedDoc ? cachedDoc[versionField] : undefined;
|
|
193
|
+
if (typeof cachedVersion === "string") version = cachedVersion;
|
|
194
|
+
}
|
|
195
|
+
if (mutation.operation === Operation.Update && doc.name) {
|
|
196
|
+
await this.handleOptimisticUpdate(mutation as UpdateMutation<FrappeDocData>, cachedDoc);
|
|
197
|
+
}
|
|
198
|
+
if (mutation.operation === Operation.Create) {
|
|
199
|
+
await this.handleOptimisticCreate(mutation as CreateMutation<FrappeDocData>);
|
|
200
|
+
}
|
|
201
|
+
if (
|
|
202
|
+
version === undefined &&
|
|
203
|
+
doc.name &&
|
|
204
|
+
(mutation.operation === Operation.Update || mutation.operation === Operation.Delete) &&
|
|
205
|
+
!cachedDoc
|
|
206
|
+
) {
|
|
207
|
+
try {
|
|
208
|
+
cachedDoc = await this.db.getDoc(doc.doctype, doc.name);
|
|
209
|
+
const versionField =
|
|
210
|
+
this.options.versionFields?.[doc.doctype] || this.options.versionField || "modified";
|
|
211
|
+
const cachedVersion = cachedDoc ? cachedDoc[versionField] : undefined;
|
|
212
|
+
if (typeof cachedVersion === "string") version = cachedVersion;
|
|
213
|
+
this.normalizedCache.upsert(cachedDoc);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// Version fetch failed - continue without version check
|
|
216
|
+
// This is acceptable as OCC is optional for mutations
|
|
217
|
+
if (this.options.onTrace) {
|
|
218
|
+
this.options.onTrace({
|
|
219
|
+
event: "version-fetch-failed",
|
|
220
|
+
details: { doctype: doc.doctype, name: doc.name, error: err },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const values =
|
|
226
|
+
mutation.operation === Operation.Delete
|
|
227
|
+
? undefined
|
|
228
|
+
: mutation.operation === Operation.Create
|
|
229
|
+
? mutation.doc
|
|
230
|
+
: mutation.doc;
|
|
231
|
+
this.mutationJournal.add(mutation.mutationId, {
|
|
232
|
+
doctype: doc.doctype,
|
|
233
|
+
docname: doc.name,
|
|
234
|
+
values,
|
|
235
|
+
operation: mutation.operation,
|
|
236
|
+
version,
|
|
237
|
+
status: Status.Pending,
|
|
238
|
+
snapshot: doc.name ? cachedDoc : undefined,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Use batching if enabled
|
|
242
|
+
if (this.options.enableBatching) {
|
|
243
|
+
try {
|
|
244
|
+
await this.queueForBatch(mutation);
|
|
245
|
+
// Schedule rehydration for creates
|
|
246
|
+
if (mutation.operation === Operation.Create) {
|
|
247
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
248
|
+
if (subscription.query.doctype === mutation.doc.doctype) {
|
|
249
|
+
this.rehydrationManager.scheduleRehydration(subscription, RehydrationReason.Create);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} catch (err) {
|
|
254
|
+
await this.handleMutationError(mutation, err);
|
|
255
|
+
throw err;
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Non-batched execution (original behavior)
|
|
261
|
+
try {
|
|
262
|
+
if (mutation.operation === Operation.Create) {
|
|
263
|
+
await this.handleServerCreate(mutation);
|
|
264
|
+
} else if (mutation.operation === Operation.Update && doc.name) {
|
|
265
|
+
await this.handleServerUpdate(mutation, version);
|
|
266
|
+
} else if (mutation.operation === Operation.Delete && doc.name) {
|
|
267
|
+
await this.handleServerDelete(mutation, version);
|
|
268
|
+
}
|
|
269
|
+
this.mutationJournal.confirm(mutation.mutationId);
|
|
270
|
+
if (this.options.onMutationSuccess) {
|
|
271
|
+
this.options.onMutationSuccess(mutation);
|
|
272
|
+
}
|
|
273
|
+
if (mutation.operation === Operation.Create) {
|
|
274
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
275
|
+
if (subscription.query.doctype === mutation.doc.doctype) {
|
|
276
|
+
this.rehydrationManager.scheduleRehydration(subscription, RehydrationReason.Create);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (!this.realtime?.isHealthy()) {
|
|
281
|
+
await this.refetchAffectedDocs(mutation);
|
|
282
|
+
}
|
|
283
|
+
} catch (err) {
|
|
284
|
+
await this.handleMutationError(mutation, err);
|
|
285
|
+
throw err;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private async handleOptimisticUpdate<Data extends FrappeDocData>(
|
|
290
|
+
mutation: UpdateMutation<Data>,
|
|
291
|
+
cachedDoc?: FrappeDoc<Data>,
|
|
292
|
+
) {
|
|
293
|
+
const doc = cachedDoc ? { ...cachedDoc, ...mutation.doc } : mutation.doc;
|
|
294
|
+
this.normalizedCache.upsert(doc);
|
|
295
|
+
if (this.options.onOptimisticApply) {
|
|
296
|
+
this.options.onOptimisticApply({
|
|
297
|
+
doctype: mutation.doc.doctype,
|
|
298
|
+
name: mutation.doc.name,
|
|
299
|
+
operation: mutation.operation,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
this.subscriptionManager.updateSubscriptionMembership(doc);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private async handleOptimisticCreate<Data extends FrappeDocData>(mutation: CreateMutation<Data>) {
|
|
306
|
+
const temporaryName = mutation.doc.name || `tmp_${cryptoRandomId()}`;
|
|
307
|
+
const optimisticDoc = {
|
|
308
|
+
...mutation.doc,
|
|
309
|
+
name: temporaryName,
|
|
310
|
+
} as FrappeDoc<Data>;
|
|
311
|
+
this.normalizedCache.upsert(optimisticDoc);
|
|
312
|
+
if (!this.globalOptimisticDocIds.has(mutation.doc.doctype)) {
|
|
313
|
+
this.globalOptimisticDocIds.set(mutation.doc.doctype, new Set());
|
|
314
|
+
}
|
|
315
|
+
const globalDocIds = this.globalOptimisticDocIds.get(mutation.doc.doctype)!;
|
|
316
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
317
|
+
if (subscription.query.doctype !== mutation.doc.doctype) continue;
|
|
318
|
+
const matches = matchesFilters(optimisticDoc, subscription.query.filters);
|
|
319
|
+
if (!matches) continue;
|
|
320
|
+
if (globalDocIds.has(temporaryName)) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (
|
|
324
|
+
subscription.query.orderBy &&
|
|
325
|
+
typeof subscription.query.limit === "number" &&
|
|
326
|
+
subscription.query.limit > 0 &&
|
|
327
|
+
subscription.docIds.length > 0
|
|
328
|
+
) {
|
|
329
|
+
const placement = this.determineOptimisticPlacement(subscription, optimisticDoc);
|
|
330
|
+
switch (placement) {
|
|
331
|
+
case PlacementType.Before:
|
|
332
|
+
if (subscription.metadata) {
|
|
333
|
+
subscription.metadata.itemsBeforePage =
|
|
334
|
+
(subscription.metadata.itemsBeforePage || 0) + 1;
|
|
335
|
+
}
|
|
336
|
+
continue;
|
|
337
|
+
case PlacementType.After:
|
|
338
|
+
if (subscription.metadata) {
|
|
339
|
+
subscription.metadata.itemsAfterPage =
|
|
340
|
+
(subscription.metadata.itemsAfterPage || 0) + 1;
|
|
341
|
+
}
|
|
342
|
+
continue;
|
|
343
|
+
case PlacementType.Unknown:
|
|
344
|
+
subscription.pendingRehydration = PendingRehydrationType.FullRefresh;
|
|
345
|
+
this.rehydrationManager.scheduleRehydration(subscription, RehydrationReason.Uncertain);
|
|
346
|
+
break;
|
|
347
|
+
case PlacementType.Within:
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (!subscription.docIds.includes(temporaryName)) {
|
|
352
|
+
this.subscriptionManager.insertDocumentIntoSubscription(subscription, optimisticDoc);
|
|
353
|
+
globalDocIds.add(temporaryName);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private async handleServerCreate<Data extends FrappeDocData>(mutation: CreateMutation<Data>) {
|
|
359
|
+
const created = await this.db.createDoc<FrappeDoc<Data>>(mutation.doc);
|
|
360
|
+
// Authoritative name assigned by the backend (may equal the provided temp name)
|
|
361
|
+
const realName = created?.name;
|
|
362
|
+
// The client-synthesized or user-provided temporary name used during optimism
|
|
363
|
+
const temporaryName = mutation.doc.name;
|
|
364
|
+
// If backend assigned a different name, remap temporary → real across subscriptions.
|
|
365
|
+
if (realName && temporaryName && realName !== temporaryName) {
|
|
366
|
+
this.handleTemporaryToRealMapping(mutation.doc.doctype, temporaryName, realName);
|
|
367
|
+
// Register temp ID mapping for rehydration
|
|
368
|
+
this.rehydrationManager.registerTempIdMapping(temporaryName, realName);
|
|
369
|
+
} else if (temporaryName) {
|
|
370
|
+
// Even if name didn't change, remove from optimistic registry since it's now confirmed
|
|
371
|
+
const globalDocs = this.globalOptimisticDocIds.get(mutation.doc.doctype);
|
|
372
|
+
if (globalDocs) {
|
|
373
|
+
globalDocs.delete(temporaryName);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// When realtime is unhealthy, seed cache with authoritative document.
|
|
377
|
+
if (!this.realtime?.isHealthy()) {
|
|
378
|
+
this.normalizedCache.upsert(created);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private async handleServerUpdate<Data extends FrappeDocData>(
|
|
383
|
+
mutation: UpdateMutation<Data>,
|
|
384
|
+
_version: string | undefined,
|
|
385
|
+
) {
|
|
386
|
+
// Send partial update with idempotency and OCC token
|
|
387
|
+
await this.db.updateDoc(mutation.doc);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private async handleServerDelete<Data extends FrappeDocData>(
|
|
391
|
+
mutation: DeleteMutation<Data>,
|
|
392
|
+
_version: string | undefined,
|
|
393
|
+
) {
|
|
394
|
+
// Optimistic delete: evict now; REST confirms or we rollback in catch.
|
|
395
|
+
this.normalizedCache.remove({
|
|
396
|
+
doctype: mutation.doc.doctype,
|
|
397
|
+
name: mutation.doc.name,
|
|
398
|
+
});
|
|
399
|
+
this.subscriptionManager.removeFromAllSubscriptions(mutation.doc.doctype, mutation.doc.name);
|
|
400
|
+
|
|
401
|
+
// Schedule full refresh for subscriptions to pull in items from next page
|
|
402
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
403
|
+
if (
|
|
404
|
+
subscription.query.doctype === mutation.doc.doctype &&
|
|
405
|
+
subscription.docIds.includes(mutation.doc.name)
|
|
406
|
+
) {
|
|
407
|
+
this.rehydrationManager.scheduleRehydration(subscription, RehydrationReason.Delete);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Idempotent delete with mutation id
|
|
411
|
+
await this.db.deleteDoc(mutation.doc);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private handleTemporaryToRealMapping(doctype: string, temporaryName: string, realName: string) {
|
|
415
|
+
this.temporaryIdToRealNameMap.set(temporaryName, realName);
|
|
416
|
+
// Update global registry: remove temporary, add real
|
|
417
|
+
const globalDocs = this.globalOptimisticDocIds.get(doctype);
|
|
418
|
+
if (globalDocs) {
|
|
419
|
+
globalDocs.delete(temporaryName);
|
|
420
|
+
// Real name is no longer optimistic, so we don't add it back
|
|
421
|
+
}
|
|
422
|
+
// replace in subscriptions
|
|
423
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
424
|
+
if (subscription.query.doctype !== doctype) continue;
|
|
425
|
+
subscription.docIds = subscription.docIds.map((x) => (x === temporaryName ? realName : x));
|
|
426
|
+
}
|
|
427
|
+
// Remove the temporary cached row now that the authoritative name exists
|
|
428
|
+
this.normalizedCache.remove({ doctype, name: temporaryName });
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private async refetchAffectedDocs<Data extends FrappeDocData>(mutation: Mutation<Data>) {
|
|
432
|
+
// Target the main document (accounting for temporary→real remap) and any related documents
|
|
433
|
+
const targets = [] as { doctype: string; name: string }[];
|
|
434
|
+
const docname = mutation.doc.name;
|
|
435
|
+
const doctype = mutation.doc.doctype;
|
|
436
|
+
const realName = (docname && this.temporaryIdToRealNameMap.get(docname)) || docname;
|
|
437
|
+
if (realName) targets.push({ doctype, name: realName });
|
|
438
|
+
if (mutation.affected?.length) targets.push(...mutation.affected);
|
|
439
|
+
// Fetch each target with user context to respect permissions and hidden fields
|
|
440
|
+
for (const target of targets) {
|
|
441
|
+
const fresh = await this.db.getDoc(target.doctype, target.name);
|
|
442
|
+
this.normalizedCache.upsert(fresh);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private async handleMutationError<Data extends FrappeDocData>(
|
|
447
|
+
mutation: Mutation<Data>,
|
|
448
|
+
err: unknown,
|
|
449
|
+
) {
|
|
450
|
+
if (this.options.onMutationError) this.options.onMutationError({ mutation, err });
|
|
451
|
+
// Failure path: mark failed and rollback optimistic state where possible.
|
|
452
|
+
this.mutationJournal.fail(mutation.mutationId);
|
|
453
|
+
const entry = this.mutationJournal.get(mutation.mutationId);
|
|
454
|
+
|
|
455
|
+
// Skip rollback when offline — mutation may be retried when back online
|
|
456
|
+
const isOffline = typeof navigator !== "undefined" && navigator.onLine === false;
|
|
457
|
+
if (!isOffline) {
|
|
458
|
+
// Rollback optimistic state based on operation type
|
|
459
|
+
await this.rollbackOptimisticState(mutation, entry);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Conflict handling: on 409 or 412, fetch latest and notify host via onConflict.
|
|
463
|
+
const errorWithStatus = err as { status?: number };
|
|
464
|
+
const docname = mutation.doc.name;
|
|
465
|
+
if (
|
|
466
|
+
errorWithStatus &&
|
|
467
|
+
(errorWithStatus.status === 409 || errorWithStatus.status === 412) &&
|
|
468
|
+
docname
|
|
469
|
+
) {
|
|
470
|
+
await this.handleConflict(mutation, entry);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Rolls back optimistic state when a mutation fails.
|
|
476
|
+
* Handles all operation types: create, update, and delete.
|
|
477
|
+
*/
|
|
478
|
+
private async rollbackOptimisticState<Data extends FrappeDocData>(
|
|
479
|
+
mutation: Mutation<Data>,
|
|
480
|
+
entry: Pending | undefined,
|
|
481
|
+
) {
|
|
482
|
+
const doctype = mutation.doc.doctype;
|
|
483
|
+
const docname = mutation.doc.name;
|
|
484
|
+
|
|
485
|
+
switch (mutation.operation) {
|
|
486
|
+
case Operation.Create:
|
|
487
|
+
// Remove temporary optimistic document from cache and subscriptions
|
|
488
|
+
await this.handleFailedCreate(mutation);
|
|
489
|
+
// Also remove from cache
|
|
490
|
+
if (docname) {
|
|
491
|
+
this.normalizedCache.remove({ doctype, name: docname });
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
|
|
495
|
+
case Operation.Update:
|
|
496
|
+
// Restore the original document from snapshot
|
|
497
|
+
if (entry?.snapshot && docname) {
|
|
498
|
+
this.normalizedCache.upsert(entry.snapshot);
|
|
499
|
+
// Also update subscription memberships in case filters changed
|
|
500
|
+
this.subscriptionManager.updateSubscriptionMembership(
|
|
501
|
+
entry.snapshot as FrappeDoc<FrappeDocData>,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
|
|
506
|
+
case Operation.Delete:
|
|
507
|
+
// Restore the deleted document from snapshot
|
|
508
|
+
if (entry?.snapshot && docname) {
|
|
509
|
+
this.normalizedCache.upsert(entry.snapshot);
|
|
510
|
+
// Re-add to subscription memberships
|
|
511
|
+
const restoredDoc = entry.snapshot as FrappeDoc<FrappeDocData>;
|
|
512
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
513
|
+
if (subscription.query.doctype !== doctype) continue;
|
|
514
|
+
const matches = matchesFilters(restoredDoc, subscription.query.filters);
|
|
515
|
+
if (matches && !subscription.docIds.includes(docname)) {
|
|
516
|
+
this.subscriptionManager.insertDocumentIntoSubscription(subscription, restoredDoc);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private async handleFailedCreate<Data extends FrappeDocData>(mutation: Mutation<Data>) {
|
|
525
|
+
const temporaryName = mutation.doc.name;
|
|
526
|
+
const doctype = mutation.doc.doctype;
|
|
527
|
+
|
|
528
|
+
if (temporaryName) {
|
|
529
|
+
// Remove from global registry
|
|
530
|
+
const globalDocs = this.globalOptimisticDocIds.get(doctype);
|
|
531
|
+
if (globalDocs) {
|
|
532
|
+
globalDocs.delete(temporaryName);
|
|
533
|
+
}
|
|
534
|
+
// Remove temporary optimistic row from subscriptions that may have included it.
|
|
535
|
+
for (const subscription of this.subscriptionManager.getAllSubscriptions().values()) {
|
|
536
|
+
if (subscription.query.doctype !== doctype) continue;
|
|
537
|
+
subscription.docIds = subscription.docIds.filter((id) => id !== temporaryName);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
private async handleConflict<Data extends FrappeDocData>(
|
|
543
|
+
mutation: Mutation<Data>,
|
|
544
|
+
entry: Pending | undefined,
|
|
545
|
+
) {
|
|
546
|
+
const doctype = mutation.doc.doctype;
|
|
547
|
+
const docname = mutation.doc.name;
|
|
548
|
+
if (!docname) return;
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
const latest = await this.db.getDoc(doctype, docname);
|
|
552
|
+
const base = entry?.snapshot as FrappeDoc<FrappeDocData> | undefined;
|
|
553
|
+
const clientPartial =
|
|
554
|
+
(mutation.operation === Operation.Delete ? undefined : mutation.doc) ||
|
|
555
|
+
({} as Record<string, any>);
|
|
556
|
+
// Build the full client document (base + mutation changes) so the merge
|
|
557
|
+
// can correctly distinguish "field unchanged" from "field set to undefined".
|
|
558
|
+
const fullClient = (
|
|
559
|
+
base
|
|
560
|
+
? { ...base, ...clientPartial, doctype, name: docname }
|
|
561
|
+
: { ...clientPartial, doctype, name: docname }
|
|
562
|
+
) as FrappeDoc<FrappeDocData>;
|
|
563
|
+
const mergeConfig = this.buildMergeConfig();
|
|
564
|
+
const nonOverlap = merge(
|
|
565
|
+
base,
|
|
566
|
+
fullClient,
|
|
567
|
+
latest as FrappeDoc<FrappeDocData>,
|
|
568
|
+
mergeConfig,
|
|
569
|
+
doctype,
|
|
570
|
+
);
|
|
571
|
+
if (!nonOverlap.hasConflicts) {
|
|
572
|
+
// Safe auto-merge path: retry with latest version and only disjoint fields
|
|
573
|
+
const versionField =
|
|
574
|
+
this.options.versionFields?.[doctype] || this.options.versionField || "modified";
|
|
575
|
+
if (nonOverlap.merged) {
|
|
576
|
+
const newId = cryptoRandomId();
|
|
577
|
+
await this.db.updateDoc({
|
|
578
|
+
...nonOverlap.merged,
|
|
579
|
+
client_mutation_id: newId,
|
|
580
|
+
base_modified: (latest[versionField] as string) || undefined,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
// Refresh to converge local cache and finalize the original journal entry
|
|
584
|
+
const refreshed = await this.db.getDoc(doctype, docname);
|
|
585
|
+
this.normalizedCache.upsert(refreshed);
|
|
586
|
+
this.mutationJournal.confirm(mutation.mutationId);
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
// Overlap exists: converge to server's latest; delegate resolution to onConflict
|
|
590
|
+
this.normalizedCache.upsert(latest);
|
|
591
|
+
} catch {}
|
|
592
|
+
if (this.options.onConflict)
|
|
593
|
+
this.options.onConflict({
|
|
594
|
+
doctype,
|
|
595
|
+
name: docname,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Determines optimal placement for an optimistic item based on sort fields.
|
|
601
|
+
* Supports multi-column sorting with proper tie-breaking.
|
|
602
|
+
*
|
|
603
|
+
* @param subscription - The subscription to check placement against
|
|
604
|
+
* @param candidate - The document to place
|
|
605
|
+
* @returns Placement relative to current page:
|
|
606
|
+
* - 'before': Item belongs on a previous page
|
|
607
|
+
* - 'within': Item belongs on the current page
|
|
608
|
+
* - 'after': Item belongs on a later page
|
|
609
|
+
* - 'unknown': Cannot determine placement (needs full refresh)
|
|
610
|
+
*
|
|
611
|
+
* @remarks
|
|
612
|
+
* Uses page boundaries when available for O(1) placement checks.
|
|
613
|
+
* Falls back to comparing against first/last items in current page.
|
|
614
|
+
* Supports multi-field sorting with tie-breaking.
|
|
615
|
+
*/
|
|
616
|
+
private determineOptimisticPlacement(
|
|
617
|
+
subscription: Subscription,
|
|
618
|
+
candidate: FrappeDoc<FrappeDocData>,
|
|
619
|
+
): PlacementType {
|
|
620
|
+
const docIds = subscription.docIds;
|
|
621
|
+
if (!docIds.length) return PlacementType.Unknown;
|
|
622
|
+
|
|
623
|
+
// Use boundaries if available for more accurate placement
|
|
624
|
+
if (subscription.boundaries?.firstItem && subscription.boundaries?.lastItem) {
|
|
625
|
+
return this.determinePlacementWithBoundaries(
|
|
626
|
+
subscription.boundaries,
|
|
627
|
+
candidate,
|
|
628
|
+
subscription.query.orderBy,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Fallback: compare against first/last items when boundaries unavailable
|
|
633
|
+
const order = subscription.query.orderBy;
|
|
634
|
+
if (!order) return PlacementType.Unknown;
|
|
635
|
+
const doctype = subscription.query.doctype;
|
|
636
|
+
const first = this.normalizedCache.get({
|
|
637
|
+
doctype,
|
|
638
|
+
name: docIds[0],
|
|
639
|
+
}) as FrappeDoc<FrappeDocData> | undefined;
|
|
640
|
+
const last = this.normalizedCache.get({
|
|
641
|
+
doctype,
|
|
642
|
+
name: docIds[docIds.length - 1],
|
|
643
|
+
}) as FrappeDoc<FrappeDocData> | undefined;
|
|
644
|
+
|
|
645
|
+
if (!first || !last) return PlacementType.Unknown;
|
|
646
|
+
|
|
647
|
+
// Parse sort fields (support multiple columns)
|
|
648
|
+
const sortFields = this.parseSortFields(order);
|
|
649
|
+
|
|
650
|
+
for (const { field, isAsc } of sortFields) {
|
|
651
|
+
const candidateValue = candidate[field];
|
|
652
|
+
const firstValue = first[field];
|
|
653
|
+
const lastValue = last[field];
|
|
654
|
+
|
|
655
|
+
if (candidateValue == null || firstValue == null || lastValue == null) {
|
|
656
|
+
continue; // Skip fields with missing values
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (isAsc) {
|
|
660
|
+
if (candidateValue < firstValue) return PlacementType.Before;
|
|
661
|
+
if (candidateValue > lastValue) return PlacementType.After;
|
|
662
|
+
} else {
|
|
663
|
+
if (candidateValue > firstValue) return PlacementType.Before;
|
|
664
|
+
if (candidateValue < lastValue) return PlacementType.After;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// If values are not equal, we're within bounds
|
|
668
|
+
if (candidateValue !== firstValue && candidateValue !== lastValue) {
|
|
669
|
+
return PlacementType.Within;
|
|
670
|
+
}
|
|
671
|
+
// If equal, continue to next sort field for tie-breaking
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
return PlacementType.Within; // All sort fields match boundaries
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
private determinePlacementWithBoundaries<Data extends FrappeDocData>(
|
|
678
|
+
boundaries: PageBoundaries<FrappeDocData>,
|
|
679
|
+
candidate: FrappeDoc<Data>,
|
|
680
|
+
orderBy?: OrderBy,
|
|
681
|
+
): PlacementType {
|
|
682
|
+
if (!boundaries.firstItem || !boundaries.lastItem) return PlacementType.Unknown;
|
|
683
|
+
|
|
684
|
+
const sortFields = this.parseSortFields(orderBy);
|
|
685
|
+
|
|
686
|
+
for (const { field, isAsc } of sortFields) {
|
|
687
|
+
const candidateValue = candidate[field];
|
|
688
|
+
const firstValue = boundaries.firstItem[field];
|
|
689
|
+
const lastValue = boundaries.lastItem[field];
|
|
690
|
+
|
|
691
|
+
if (candidateValue == null || firstValue == null || lastValue == null) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (isAsc) {
|
|
696
|
+
if (candidateValue < firstValue) return PlacementType.Before;
|
|
697
|
+
if (candidateValue > lastValue) return PlacementType.After;
|
|
698
|
+
} else {
|
|
699
|
+
if (candidateValue > firstValue) return PlacementType.Before;
|
|
700
|
+
if (candidateValue < lastValue) return PlacementType.After;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (candidateValue !== firstValue && candidateValue !== lastValue) {
|
|
704
|
+
return PlacementType.Within;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return PlacementType.Within;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
private parseSortFields(orderBy: OrderBy | undefined): SortField[] {
|
|
712
|
+
if (!orderBy) return [{ field: "name", isAsc: true }];
|
|
713
|
+
|
|
714
|
+
// Handle both single field and array formats
|
|
715
|
+
const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
716
|
+
|
|
717
|
+
const sortFields = orderByArray.map((ob) => ({
|
|
718
|
+
field: ob.field,
|
|
719
|
+
isAsc: ob.order !== Order.Desc,
|
|
720
|
+
}));
|
|
721
|
+
|
|
722
|
+
// Always add name as final tie-breaker if not already included
|
|
723
|
+
const hasNameField = sortFields.some((sf) => sf.field === "name");
|
|
724
|
+
if (!hasNameField) {
|
|
725
|
+
sortFields.push({ field: "name", isAsc: true });
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
return sortFields;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Replays pending mutations with simplified error handling and rate limiting.
|
|
733
|
+
*
|
|
734
|
+
* Since HTTP client now handles retries automatically for transient errors,
|
|
735
|
+
* we only need to handle:
|
|
736
|
+
* - Success → confirm and remove from journal
|
|
737
|
+
* - Conflict (409/412) → attempt merge resolution, then retry
|
|
738
|
+
* - Auth (401/403) → notify user, keep in journal for later retry
|
|
739
|
+
* - Permanent (4xx validation) → fail and remove from journal
|
|
740
|
+
* - Failed (exhausted retries) → keep in journal but notify
|
|
741
|
+
*
|
|
742
|
+
* The replay path uses plain CRUD without optimistic writes,
|
|
743
|
+
* since optimistic state is already reflected in the cache.
|
|
744
|
+
*
|
|
745
|
+
* Rate limiting is applied to prevent hammering the server when replaying
|
|
746
|
+
* large numbers of queued mutations after coming back online.
|
|
747
|
+
*/
|
|
748
|
+
async replayPending() {
|
|
749
|
+
let anyFailed = false;
|
|
750
|
+
|
|
751
|
+
if (this.options.onReplayStart) this.options.onReplayStart();
|
|
752
|
+
|
|
753
|
+
const pendingMutations = this.mutationJournal.listPending();
|
|
754
|
+
if (pendingMutations.length === 0) {
|
|
755
|
+
if (this.options.onReplayComplete) {
|
|
756
|
+
this.options.onReplayComplete({ anyFailed: false });
|
|
757
|
+
}
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Rate limit: default 10 mutations per second to avoid server overload
|
|
762
|
+
const rateLimit = this.options.replayRateLimit ?? 10;
|
|
763
|
+
|
|
764
|
+
// Process mutations with rate limiting
|
|
765
|
+
const processMutation = async (mutation: { id: string; entry: Pending }) => {
|
|
766
|
+
const { id, entry } = mutation;
|
|
767
|
+
if (this.options.onRetry) {
|
|
768
|
+
this.options.onRetry({ id, operation: entry.operation });
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
if (entry.operation === Operation.Create) {
|
|
772
|
+
await this.db.createDoc({
|
|
773
|
+
...entry.values,
|
|
774
|
+
client_mutation_id: id,
|
|
775
|
+
doctype: entry.doctype,
|
|
776
|
+
name: entry.docname || cryptoRandomId(),
|
|
777
|
+
});
|
|
778
|
+
} else if (entry.operation === Operation.Update && entry.docname) {
|
|
779
|
+
await this.db.updateDoc({
|
|
780
|
+
...entry.values,
|
|
781
|
+
client_mutation_id: id,
|
|
782
|
+
base_modified: entry.version,
|
|
783
|
+
doctype: entry.doctype,
|
|
784
|
+
name: entry.docname,
|
|
785
|
+
});
|
|
786
|
+
} else if (entry.operation === Operation.Delete && entry.docname) {
|
|
787
|
+
await this.db.deleteDoc({
|
|
788
|
+
doctype: entry.doctype,
|
|
789
|
+
name: entry.docname,
|
|
790
|
+
client_mutation_id: id,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Success - confirm and remove from journal
|
|
795
|
+
this.mutationJournal.confirm(id);
|
|
796
|
+
} catch (err) {
|
|
797
|
+
const errorClass = this.classifyError(err);
|
|
798
|
+
anyFailed = true;
|
|
799
|
+
|
|
800
|
+
if (errorClass === "conflict") {
|
|
801
|
+
// Conflict - try to handle with merge logic in future replay
|
|
802
|
+
// Keep in journal for retry after conflict resolution
|
|
803
|
+
if (this.options.onConflict) {
|
|
804
|
+
this.options.onConflict({
|
|
805
|
+
doctype: entry.doctype,
|
|
806
|
+
name: entry.docname,
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
} else if (errorClass === "auth") {
|
|
810
|
+
// Auth error - keep in journal, user needs to refresh auth
|
|
811
|
+
// Don't mark as failed since this might resolve later
|
|
812
|
+
} else if (errorClass === "permanent") {
|
|
813
|
+
// Permanent validation error - remove from journal
|
|
814
|
+
this.mutationJournal.fail(id);
|
|
815
|
+
this.notifyMutationError(id, entry, err);
|
|
816
|
+
} else if (errorClass === "failed") {
|
|
817
|
+
// Retries exhausted - keep in journal but notify
|
|
818
|
+
this.mutationJournal.fail(id);
|
|
819
|
+
this.notifyMutationError(id, entry, err);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// Process mutations with rate limiting to avoid hammering the server
|
|
825
|
+
// Rate limit is mutations per second
|
|
826
|
+
const delayMs = Math.ceil(1000 / rateLimit);
|
|
827
|
+
|
|
828
|
+
for (const mutation of pendingMutations) {
|
|
829
|
+
await processMutation(mutation);
|
|
830
|
+
// Add delay between mutations (except after the last one)
|
|
831
|
+
if (pendingMutations.indexOf(mutation) < pendingMutations.length - 1) {
|
|
832
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (this.options.onReplayComplete) {
|
|
837
|
+
this.options.onReplayComplete({ anyFailed });
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Classifies errors for journal management.
|
|
843
|
+
*/
|
|
844
|
+
private classifyError(err: unknown): "conflict" | "auth" | "permanent" | "failed" {
|
|
845
|
+
const errorWithStatus = err as {
|
|
846
|
+
status?: number;
|
|
847
|
+
response?: { status?: number };
|
|
848
|
+
};
|
|
849
|
+
const status = errorWithStatus?.status || errorWithStatus?.response?.status;
|
|
850
|
+
|
|
851
|
+
// Conflict errors - need special merge handling
|
|
852
|
+
if (status === 409 || status === 412) return "conflict";
|
|
853
|
+
|
|
854
|
+
// Auth errors - need user intervention
|
|
855
|
+
if (status === 401 || status === 403) return "auth";
|
|
856
|
+
|
|
857
|
+
// Client validation errors (4xx) are permanent
|
|
858
|
+
if (status && status >= 400 && status < 500) return "permanent";
|
|
859
|
+
|
|
860
|
+
// All other errors (5xx, network) were already retried by HTTP client
|
|
861
|
+
// If we get here, retries were exhausted - mark as failed
|
|
862
|
+
return "failed";
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Helper to notify about mutation errors with proper typing
|
|
867
|
+
*/
|
|
868
|
+
private notifyMutationError<Data extends FrappeDocData>(
|
|
869
|
+
id: string,
|
|
870
|
+
entry: Pending,
|
|
871
|
+
err: unknown,
|
|
872
|
+
) {
|
|
873
|
+
if (!this.options.onMutationError) return;
|
|
874
|
+
|
|
875
|
+
let mutation: Mutation<Data>;
|
|
876
|
+
if (entry.operation === Operation.Create) {
|
|
877
|
+
const doc = (entry.values as FrappeDoc<FrappeDocData>) || {
|
|
878
|
+
doctype: entry.doctype,
|
|
879
|
+
name: entry.docname,
|
|
880
|
+
};
|
|
881
|
+
mutation = {
|
|
882
|
+
mutationId: id,
|
|
883
|
+
operation: Operation.Create,
|
|
884
|
+
doc,
|
|
885
|
+
version: entry.version,
|
|
886
|
+
affected: [],
|
|
887
|
+
} as unknown as CreateMutation<Data>;
|
|
888
|
+
} else if (entry.operation === Operation.Update) {
|
|
889
|
+
mutation = {
|
|
890
|
+
mutationId: id,
|
|
891
|
+
operation: Operation.Update,
|
|
892
|
+
doc: {
|
|
893
|
+
...entry.values,
|
|
894
|
+
doctype: entry.doctype,
|
|
895
|
+
name: entry.docname!,
|
|
896
|
+
} as FrappeDoc<Data>,
|
|
897
|
+
version: entry.version,
|
|
898
|
+
affected: [],
|
|
899
|
+
} as UpdateMutation<Data>;
|
|
900
|
+
} else {
|
|
901
|
+
mutation = {
|
|
902
|
+
mutationId: id,
|
|
903
|
+
operation: Operation.Delete,
|
|
904
|
+
doc: {
|
|
905
|
+
doctype: entry.doctype,
|
|
906
|
+
name: entry.docname!,
|
|
907
|
+
} as FrappeDoc<Data>,
|
|
908
|
+
version: entry.version,
|
|
909
|
+
affected: [],
|
|
910
|
+
} as DeleteMutation<Data>;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
this.options.onMutationError({ mutation, err });
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Converts SyncOptions.fieldMergeConfig into the MergeConfig format
|
|
918
|
+
* expected by the merge() function.
|
|
919
|
+
*/
|
|
920
|
+
private buildMergeConfig(): MergeConfig | undefined {
|
|
921
|
+
const fmc = this.options.fieldMergeConfig;
|
|
922
|
+
if (!fmc) return undefined;
|
|
923
|
+
|
|
924
|
+
const doctypes: Record<string, DoctypeConfig> = {};
|
|
925
|
+
for (const [dt, fields] of Object.entries(fmc)) {
|
|
926
|
+
doctypes[dt] = { fields };
|
|
927
|
+
}
|
|
928
|
+
return { defaultStrategy: MergeStrategy.ServerWins, doctypes };
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/** Gets temporary to real name mapping. */
|
|
932
|
+
getTemporaryIdToRealNameMap(): Map<string, string> {
|
|
933
|
+
return this.temporaryIdToRealNameMap;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** Clears all temporary mappings. */
|
|
937
|
+
clearTemporaryMappings() {
|
|
938
|
+
this.temporaryIdToRealNameMap.clear();
|
|
939
|
+
this.globalOptimisticDocIds.clear();
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Disposes of resources.
|
|
944
|
+
*/
|
|
945
|
+
dispose() {
|
|
946
|
+
// Cancel any pending batch
|
|
947
|
+
this.batchDebouncer.cancel();
|
|
948
|
+
this.pendingBatch = [];
|
|
949
|
+
this.clearTemporaryMappings();
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** Generates a random id; prefers crypto.randomUUID when available. */
|
|
954
|
+
function cryptoRandomId() {
|
|
955
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
956
|
+
return Math.random().toString(36).slice(2);
|
|
957
|
+
}
|