@multiplatform.one/frappe 6.1.0 → 6.3.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/README.md +38 -12
- package/dist/cjs/auth.cjs +1 -1
- package/dist/cjs/auth.native.js.map +1 -1
- package/dist/cjs/call.cjs +1 -1
- package/dist/cjs/call.native.js.map +1 -1
- package/dist/cjs/collection.cjs +18 -4
- package/dist/cjs/collection.native.js +18 -4
- package/dist/cjs/collection.native.js.map +1 -1
- package/dist/cjs/core/http.cjs +92 -9
- package/dist/cjs/core/http.native.js +96 -7
- package/dist/cjs/core/http.native.js.map +1 -1
- package/dist/cjs/db.cjs +7 -7
- package/dist/cjs/db.native.js +4 -4
- package/dist/cjs/db.native.js.map +1 -1
- package/dist/cjs/doctype.cjs +97 -39
- package/dist/cjs/doctype.native.js +116 -46
- package/dist/cjs/doctype.native.js.map +1 -1
- package/dist/cjs/file.cjs +1 -1
- package/dist/cjs/file.native.js.map +1 -1
- package/dist/cjs/fixtureDb.cjs +3 -2
- package/dist/cjs/fixtureDb.native.js +2 -1
- package/dist/cjs/fixtureDb.native.js.map +1 -1
- package/dist/cjs/frappeApp.cjs +15 -14
- package/dist/cjs/frappeApp.native.js +16 -14
- package/dist/cjs/frappeApp.native.js.map +1 -1
- package/dist/cjs/frappeReact.cjs +198 -18
- package/dist/cjs/frappeReact.native.js +241 -18
- package/dist/cjs/frappeReact.native.js.map +1 -1
- package/dist/cjs/index.cjs +9 -4
- package/dist/cjs/index.native.js +9 -4
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/pacer.cjs +2 -2
- package/dist/cjs/pacer.native.js +2 -2
- package/dist/cjs/pacer.native.js.map +1 -1
- package/dist/cjs/realtime.cjs +186 -46
- package/dist/cjs/realtime.native.js +268 -48
- package/dist/cjs/realtime.native.js.map +1 -1
- package/dist/cjs/sync/backfill.cjs +32 -27
- package/dist/cjs/sync/backfill.native.js +49 -43
- package/dist/cjs/sync/backfill.native.js.map +1 -1
- package/dist/cjs/sync/index.cjs +90 -24
- package/dist/cjs/sync/index.native.js +112 -10
- package/dist/cjs/sync/index.native.js.map +1 -1
- package/dist/cjs/sync/merge.cjs +0 -3
- package/dist/cjs/sync/merge.native.js +0 -3
- package/dist/cjs/sync/merge.native.js.map +1 -1
- package/dist/cjs/sync/mutationEngine.cjs +25 -19
- package/dist/cjs/sync/mutationEngine.native.js +16 -13
- package/dist/cjs/sync/mutationEngine.native.js.map +1 -1
- package/dist/cjs/sync/mutationJournal.cjs +9 -4
- package/dist/cjs/sync/mutationJournal.native.js +7 -4
- package/dist/cjs/sync/mutationJournal.native.js.map +1 -1
- package/dist/cjs/sync/normalizedCache.cjs +7 -3
- package/dist/cjs/sync/normalizedCache.native.js +7 -2
- package/dist/cjs/sync/normalizedCache.native.js.map +1 -1
- package/dist/cjs/sync/persistence.cjs +63 -31
- package/dist/cjs/sync/persistence.native.js +120 -63
- package/dist/cjs/sync/persistence.native.js.map +1 -1
- package/dist/cjs/sync/realtime.cjs +124 -13
- package/dist/cjs/sync/realtime.native.js +163 -20
- package/dist/cjs/sync/realtime.native.js.map +1 -1
- package/dist/cjs/sync/rehydration.cjs +16 -10
- package/dist/cjs/sync/rehydration.native.js +16 -14
- package/dist/cjs/sync/rehydration.native.js.map +1 -1
- package/dist/cjs/sync/storage.cjs +3 -3
- package/dist/cjs/sync/storage.native.js +1 -1
- package/dist/cjs/sync/storage.native.js.map +1 -1
- package/dist/cjs/sync/subscriptionManager.cjs +255 -65
- package/dist/cjs/sync/subscriptionManager.native.js +330 -93
- package/dist/cjs/sync/subscriptionManager.native.js.map +1 -1
- package/dist/cjs/sync/types.cjs +2 -0
- package/dist/cjs/sync/types.native.js +2 -0
- package/dist/cjs/sync/types.native.js.map +1 -1
- package/dist/cjs/sync/virtualPageManager.cjs +9 -9
- package/dist/cjs/sync/virtualPageManager.native.js +5 -9
- package/dist/cjs/sync/virtualPageManager.native.js.map +1 -1
- package/dist/cjs/useLiveQuery.cjs +52 -0
- package/dist/cjs/useLiveQuery.native.js +59 -0
- package/dist/cjs/useLiveQuery.native.js.map +1 -0
- package/dist/cjs/util.cjs +11 -2
- package/dist/cjs/util.native.js +16 -2
- package/dist/cjs/util.native.js.map +1 -1
- package/dist/esm/auth.mjs +1 -1
- package/dist/esm/auth.mjs.map +1 -1
- package/dist/esm/auth.native.js.map +1 -1
- package/dist/esm/call.mjs +1 -1
- package/dist/esm/call.mjs.map +1 -1
- package/dist/esm/call.native.js.map +1 -1
- package/dist/esm/collection.mjs +18 -4
- package/dist/esm/collection.mjs.map +1 -1
- package/dist/esm/collection.native.js +18 -4
- package/dist/esm/collection.native.js.map +1 -1
- package/dist/esm/core/http.mjs +93 -10
- package/dist/esm/core/http.mjs.map +1 -1
- package/dist/esm/core/http.native.js +97 -8
- package/dist/esm/core/http.native.js.map +1 -1
- package/dist/esm/db.mjs +7 -7
- package/dist/esm/db.mjs.map +1 -1
- package/dist/esm/db.native.js +4 -4
- package/dist/esm/db.native.js.map +1 -1
- package/dist/esm/doctype.mjs +96 -39
- package/dist/esm/doctype.mjs.map +1 -1
- package/dist/esm/doctype.native.js +115 -46
- package/dist/esm/doctype.native.js.map +1 -1
- package/dist/esm/file.mjs +1 -1
- package/dist/esm/file.mjs.map +1 -1
- package/dist/esm/file.native.js.map +1 -1
- package/dist/esm/fixtureDb.mjs +3 -2
- package/dist/esm/fixtureDb.mjs.map +1 -1
- package/dist/esm/fixtureDb.native.js +2 -1
- package/dist/esm/fixtureDb.native.js.map +1 -1
- package/dist/esm/frappeApp.mjs +15 -14
- package/dist/esm/frappeApp.mjs.map +1 -1
- package/dist/esm/frappeApp.native.js +16 -14
- package/dist/esm/frappeApp.native.js.map +1 -1
- package/dist/esm/frappeReact.mjs +197 -19
- package/dist/esm/frappeReact.mjs.map +1 -1
- package/dist/esm/frappeReact.native.js +240 -19
- package/dist/esm/frappeReact.native.js.map +1 -1
- package/dist/esm/index.mjs +5 -4
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +5 -4
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/pacer.mjs +1 -1
- package/dist/esm/pacer.native.js +1 -1
- package/dist/esm/realtime.mjs +187 -47
- package/dist/esm/realtime.mjs.map +1 -1
- package/dist/esm/realtime.native.js +269 -49
- package/dist/esm/realtime.native.js.map +1 -1
- package/dist/esm/sync/backfill.mjs +32 -27
- package/dist/esm/sync/backfill.mjs.map +1 -1
- package/dist/esm/sync/backfill.native.js +49 -43
- package/dist/esm/sync/backfill.native.js.map +1 -1
- package/dist/esm/sync/index.mjs +90 -24
- package/dist/esm/sync/index.mjs.map +1 -1
- package/dist/esm/sync/index.native.js +112 -10
- package/dist/esm/sync/index.native.js.map +1 -1
- package/dist/esm/sync/merge.mjs +1 -1
- package/dist/esm/sync/merge.mjs.map +1 -1
- package/dist/esm/sync/merge.native.js +1 -1
- package/dist/esm/sync/merge.native.js.map +1 -1
- package/dist/esm/sync/mutationEngine.mjs +25 -19
- package/dist/esm/sync/mutationEngine.mjs.map +1 -1
- package/dist/esm/sync/mutationEngine.native.js +16 -13
- package/dist/esm/sync/mutationEngine.native.js.map +1 -1
- package/dist/esm/sync/mutationJournal.mjs +9 -4
- package/dist/esm/sync/mutationJournal.mjs.map +1 -1
- package/dist/esm/sync/mutationJournal.native.js +7 -4
- package/dist/esm/sync/mutationJournal.native.js.map +1 -1
- package/dist/esm/sync/normalizedCache.mjs +7 -3
- package/dist/esm/sync/normalizedCache.mjs.map +1 -1
- package/dist/esm/sync/normalizedCache.native.js +7 -2
- package/dist/esm/sync/normalizedCache.native.js.map +1 -1
- package/dist/esm/sync/persistence.mjs +64 -32
- package/dist/esm/sync/persistence.mjs.map +1 -1
- package/dist/esm/sync/persistence.native.js +121 -64
- package/dist/esm/sync/persistence.native.js.map +1 -1
- package/dist/esm/sync/realtime.mjs +124 -13
- package/dist/esm/sync/realtime.mjs.map +1 -1
- package/dist/esm/sync/realtime.native.js +163 -20
- package/dist/esm/sync/realtime.native.js.map +1 -1
- package/dist/esm/sync/rehydration.mjs +16 -10
- package/dist/esm/sync/rehydration.mjs.map +1 -1
- package/dist/esm/sync/rehydration.native.js +16 -14
- package/dist/esm/sync/rehydration.native.js.map +1 -1
- package/dist/esm/sync/storage.mjs +3 -3
- package/dist/esm/sync/storage.mjs.map +1 -1
- package/dist/esm/sync/storage.native.js +1 -1
- package/dist/esm/sync/storage.native.js.map +1 -1
- package/dist/esm/sync/subscriptionManager.mjs +255 -65
- package/dist/esm/sync/subscriptionManager.mjs.map +1 -1
- package/dist/esm/sync/subscriptionManager.native.js +330 -93
- package/dist/esm/sync/subscriptionManager.native.js.map +1 -1
- package/dist/esm/sync/types.mjs +2 -0
- package/dist/esm/sync/types.mjs.map +1 -1
- package/dist/esm/sync/types.native.js +2 -0
- package/dist/esm/sync/types.native.js.map +1 -1
- package/dist/esm/sync/virtualPageManager.mjs +9 -9
- package/dist/esm/sync/virtualPageManager.mjs.map +1 -1
- package/dist/esm/sync/virtualPageManager.native.js +5 -9
- package/dist/esm/sync/virtualPageManager.native.js.map +1 -1
- package/dist/esm/useLiveQuery.mjs +26 -0
- package/dist/esm/useLiveQuery.mjs.map +1 -0
- package/dist/esm/useLiveQuery.native.js +30 -0
- package/dist/esm/useLiveQuery.native.js.map +1 -0
- package/dist/esm/util.mjs +10 -2
- package/dist/esm/util.mjs.map +1 -1
- package/dist/esm/util.native.js +15 -2
- package/dist/esm/util.native.js.map +1 -1
- package/dist/jsx/auth.mjs +1 -1
- package/dist/jsx/auth.mjs.map +1 -1
- package/dist/jsx/auth.native.js.map +1 -1
- package/dist/jsx/call.mjs +1 -1
- package/dist/jsx/call.mjs.map +1 -1
- package/dist/jsx/call.native.js.map +1 -1
- package/dist/jsx/collection.mjs +18 -4
- package/dist/jsx/collection.mjs.map +1 -1
- package/dist/jsx/collection.native.js +18 -4
- package/dist/jsx/collection.native.js.map +1 -1
- package/dist/jsx/core/http.mjs +93 -10
- package/dist/jsx/core/http.mjs.map +1 -1
- package/dist/jsx/core/http.native.js +96 -7
- package/dist/jsx/core/http.native.js.map +1 -1
- package/dist/jsx/db.mjs +7 -7
- package/dist/jsx/db.mjs.map +1 -1
- package/dist/jsx/db.native.js +4 -4
- package/dist/jsx/db.native.js.map +1 -1
- package/dist/jsx/doctype.mjs +96 -39
- package/dist/jsx/doctype.mjs.map +1 -1
- package/dist/jsx/doctype.native.js +116 -46
- package/dist/jsx/doctype.native.js.map +1 -1
- package/dist/jsx/file.mjs +1 -1
- package/dist/jsx/file.mjs.map +1 -1
- package/dist/jsx/file.native.js.map +1 -1
- package/dist/jsx/fixtureDb.mjs +3 -2
- package/dist/jsx/fixtureDb.mjs.map +1 -1
- package/dist/jsx/fixtureDb.native.js +2 -1
- package/dist/jsx/fixtureDb.native.js.map +1 -1
- package/dist/jsx/frappeApp.mjs +15 -14
- package/dist/jsx/frappeApp.mjs.map +1 -1
- package/dist/jsx/frappeApp.native.js +16 -14
- package/dist/jsx/frappeApp.native.js.map +1 -1
- package/dist/jsx/frappeReact.mjs +197 -19
- package/dist/jsx/frappeReact.mjs.map +1 -1
- package/dist/jsx/frappeReact.native.js +241 -18
- package/dist/jsx/frappeReact.native.js.map +1 -1
- package/dist/jsx/index.js +5 -4
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +5 -4
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +9 -4
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/pacer.mjs +1 -1
- package/dist/jsx/pacer.native.js +2 -2
- package/dist/jsx/pacer.native.js.map +1 -1
- package/dist/jsx/realtime.mjs +187 -47
- package/dist/jsx/realtime.mjs.map +1 -1
- package/dist/jsx/realtime.native.js +268 -48
- package/dist/jsx/realtime.native.js.map +1 -1
- package/dist/jsx/sync/backfill.mjs +32 -27
- package/dist/jsx/sync/backfill.mjs.map +1 -1
- package/dist/jsx/sync/backfill.native.js +49 -43
- package/dist/jsx/sync/backfill.native.js.map +1 -1
- package/dist/jsx/sync/index.mjs +90 -24
- package/dist/jsx/sync/index.mjs.map +1 -1
- package/dist/jsx/sync/index.native.js +112 -10
- package/dist/jsx/sync/index.native.js.map +1 -1
- package/dist/jsx/sync/merge.mjs +1 -1
- package/dist/jsx/sync/merge.mjs.map +1 -1
- package/dist/jsx/sync/merge.native.js +0 -3
- package/dist/jsx/sync/merge.native.js.map +1 -1
- package/dist/jsx/sync/mutationEngine.mjs +25 -19
- package/dist/jsx/sync/mutationEngine.mjs.map +1 -1
- package/dist/jsx/sync/mutationEngine.native.js +16 -13
- package/dist/jsx/sync/mutationEngine.native.js.map +1 -1
- package/dist/jsx/sync/mutationJournal.mjs +9 -4
- package/dist/jsx/sync/mutationJournal.mjs.map +1 -1
- package/dist/jsx/sync/mutationJournal.native.js +7 -4
- package/dist/jsx/sync/mutationJournal.native.js.map +1 -1
- package/dist/jsx/sync/normalizedCache.mjs +7 -3
- package/dist/jsx/sync/normalizedCache.mjs.map +1 -1
- package/dist/jsx/sync/normalizedCache.native.js +7 -2
- package/dist/jsx/sync/normalizedCache.native.js.map +1 -1
- package/dist/jsx/sync/persistence.mjs +64 -32
- package/dist/jsx/sync/persistence.mjs.map +1 -1
- package/dist/jsx/sync/persistence.native.js +120 -63
- package/dist/jsx/sync/persistence.native.js.map +1 -1
- package/dist/jsx/sync/realtime.mjs +124 -13
- package/dist/jsx/sync/realtime.mjs.map +1 -1
- package/dist/jsx/sync/realtime.native.js +163 -20
- package/dist/jsx/sync/realtime.native.js.map +1 -1
- package/dist/jsx/sync/rehydration.mjs +16 -10
- package/dist/jsx/sync/rehydration.mjs.map +1 -1
- package/dist/jsx/sync/rehydration.native.js +16 -14
- package/dist/jsx/sync/rehydration.native.js.map +1 -1
- package/dist/jsx/sync/storage.mjs +3 -3
- package/dist/jsx/sync/storage.mjs.map +1 -1
- package/dist/jsx/sync/storage.native.js +1 -1
- package/dist/jsx/sync/storage.native.js.map +1 -1
- package/dist/jsx/sync/subscriptionManager.mjs +255 -65
- package/dist/jsx/sync/subscriptionManager.mjs.map +1 -1
- package/dist/jsx/sync/subscriptionManager.native.js +330 -93
- package/dist/jsx/sync/subscriptionManager.native.js.map +1 -1
- package/dist/jsx/sync/types.mjs +2 -0
- package/dist/jsx/sync/types.mjs.map +1 -1
- package/dist/jsx/sync/types.native.js +2 -0
- package/dist/jsx/sync/types.native.js.map +1 -1
- package/dist/jsx/sync/virtualPageManager.mjs +9 -9
- package/dist/jsx/sync/virtualPageManager.mjs.map +1 -1
- package/dist/jsx/sync/virtualPageManager.native.js +5 -9
- package/dist/jsx/sync/virtualPageManager.native.js.map +1 -1
- package/dist/jsx/useLiveQuery.mjs +26 -0
- package/dist/jsx/useLiveQuery.mjs.map +1 -0
- package/dist/jsx/useLiveQuery.native.js +59 -0
- package/dist/jsx/useLiveQuery.native.js.map +1 -0
- package/dist/jsx/util.mjs +10 -2
- package/dist/jsx/util.mjs.map +1 -1
- package/dist/jsx/util.native.js +16 -2
- package/dist/jsx/util.native.js.map +1 -1
- package/package.json +20 -17
- package/src/auth.ts +5 -1
- package/src/call.ts +5 -1
- package/src/collection.spec.ts +55 -0
- package/src/collection.ts +33 -4
- package/src/core/http.spec.ts +66 -0
- package/src/core/http.ts +115 -9
- package/src/core/httpNative.spec.ts +234 -0
- package/src/core/types.ts +2 -0
- package/src/db.spec.ts +91 -51
- package/src/db.ts +27 -8
- package/src/doctype.spec.ts +247 -39
- package/src/doctype.ts +203 -53
- package/src/file.ts +5 -1
- package/src/fixtureDb.ts +13 -2
- package/src/frappeApp.ts +18 -19
- package/src/frappeReact.spec.ts +565 -1
- package/src/frappeReact.ts +445 -37
- package/src/frappeReactLiveQueryGc.spec.ts +320 -0
- package/src/frappeReactNative.spec.ts +65 -0
- package/src/index.ts +10 -9
- package/src/pacer.ts +1 -1
- package/src/realtime.spec.ts +464 -20
- package/src/realtime.ts +284 -46
- package/src/sync/backfill.spec.ts +43 -0
- package/src/sync/backfill.ts +59 -39
- package/src/sync/debug.spec.ts +36 -0
- package/src/sync/filterValidation.spec.ts +197 -0
- package/src/sync/index.ts +98 -7
- package/src/sync/merge.ts +3 -3
- package/src/sync/mutationEngine.spec.ts +36 -4
- package/src/sync/mutationEngine.ts +30 -11
- package/src/sync/mutationJournal.ts +18 -4
- package/src/sync/normalizedCache.spec.ts +102 -0
- package/src/sync/normalizedCache.ts +19 -3
- package/src/sync/offlineRestore.spec.ts +423 -0
- package/src/sync/persistence.spec.ts +39 -7
- package/src/sync/persistence.ts +90 -18
- package/src/sync/persistenceGjs.spec.ts +143 -0
- package/src/sync/persistenceNative.spec.ts +139 -0
- package/src/sync/realtime.spec.ts +18 -1
- package/src/sync/realtime.ts +161 -13
- package/src/sync/rehydration.ts +23 -5
- package/src/sync/storage.ts +5 -4
- package/src/sync/subscriptionManager.spec.ts +94 -0
- package/src/sync/subscriptionManager.ts +304 -82
- package/src/sync/syncModule.spec.ts +137 -0
- package/src/sync/types.ts +39 -0
- package/src/sync/virtualPageManager.ts +8 -4
- package/src/useLiveQuery.ts +58 -0
- package/src/util.spec.ts +35 -4
- package/src/util.ts +34 -7
- package/types/auth.d.ts +13 -0
- package/types/auth.d.ts.map +1 -0
- package/types/call.d.ts +10 -0
- package/types/call.d.ts.map +1 -0
- package/types/collection.d.ts +127 -0
- package/types/collection.d.ts.map +1 -0
- package/types/common/types.d.ts +35 -0
- package/types/common/types.d.ts.map +1 -0
- package/types/core/http.d.ts +86 -0
- package/types/core/http.d.ts.map +1 -0
- package/types/core/types.d.ts +74 -0
- package/types/core/types.d.ts.map +1 -0
- package/types/core/wire.d.ts +405 -0
- package/types/core/wire.d.ts.map +1 -0
- package/types/db.d.ts +244 -0
- package/types/db.d.ts.map +1 -0
- package/types/devtools/FrappeDevtoolsPanel.d.ts +7 -0
- package/types/devtools/FrappeDevtoolsPanel.d.ts.map +1 -0
- package/types/devtools/index.d.ts +4 -0
- package/types/devtools/index.d.ts.map +1 -0
- package/types/devtools/plugin.d.ts +9 -0
- package/types/devtools/plugin.d.ts.map +1 -0
- package/types/devtools/theme.d.ts +15 -0
- package/types/devtools/theme.d.ts.map +1 -0
- package/types/doctype.d.ts +256 -0
- package/types/doctype.d.ts.map +1 -0
- package/types/file.d.ts +15 -0
- package/types/file.d.ts.map +1 -0
- package/types/fixtureDb.d.ts +76 -0
- package/types/fixtureDb.d.ts.map +1 -0
- package/types/fixtures.d.ts +55 -0
- package/types/fixtures.d.ts.map +1 -0
- package/types/frappeApp.d.ts +45 -0
- package/types/frappeApp.d.ts.map +1 -0
- package/types/frappeReact.d.ts +270 -0
- package/types/frappeReact.d.ts.map +1 -0
- package/types/index.d.ts +44 -0
- package/types/index.d.ts.map +1 -0
- package/types/optimistic.d.ts +38 -0
- package/types/optimistic.d.ts.map +1 -0
- package/types/pacer.d.ts +193 -0
- package/types/pacer.d.ts.map +1 -0
- package/types/plugin/index.d.ts +11 -0
- package/types/plugin/index.d.ts.map +1 -0
- package/types/realtime.d.ts +121 -0
- package/types/realtime.d.ts.map +1 -0
- package/types/sync/backfill.d.ts +60 -0
- package/types/sync/backfill.d.ts.map +1 -0
- package/types/sync/filters.d.ts +101 -0
- package/types/sync/filters.d.ts.map +1 -0
- package/types/sync/index.d.ts +181 -0
- package/types/sync/index.d.ts.map +1 -0
- package/types/sync/merge.d.ts +59 -0
- package/types/sync/merge.d.ts.map +1 -0
- package/types/sync/mutationEngine.d.ts +117 -0
- package/types/sync/mutationEngine.d.ts.map +1 -0
- package/types/sync/mutationJournal.d.ts +60 -0
- package/types/sync/mutationJournal.d.ts.map +1 -0
- package/types/sync/normalizedCache.d.ts +59 -0
- package/types/sync/normalizedCache.d.ts.map +1 -0
- package/types/sync/persistence.d.ts +185 -0
- package/types/sync/persistence.d.ts.map +1 -0
- package/types/sync/realtime.d.ts +121 -0
- package/types/sync/realtime.d.ts.map +1 -0
- package/types/sync/rehydration.d.ts +109 -0
- package/types/sync/rehydration.d.ts.map +1 -0
- package/types/sync/storage.d.ts +65 -0
- package/types/sync/storage.d.ts.map +1 -0
- package/types/sync/subscriptionManager.d.ts +161 -0
- package/types/sync/subscriptionManager.d.ts.map +1 -0
- package/types/sync/types.d.ts +435 -0
- package/types/sync/types.d.ts.map +1 -0
- package/types/sync/virtualPageManager.d.ts +190 -0
- package/types/sync/virtualPageManager.d.ts.map +1 -0
- package/types/useLiveQuery.d.ts +16 -0
- package/types/useLiveQuery.d.ts.map +1 -0
- package/types/util.d.ts +24 -0
- package/types/util.d.ts.map +1 -0
- package/dist/cjs/auth.spec.cjs +0 -322
- package/dist/cjs/auth.spec.native.js +0 -329
- package/dist/cjs/auth.spec.native.js.map +0 -1
- package/dist/cjs/call.spec.cjs +0 -427
- package/dist/cjs/call.spec.native.js +0 -430
- package/dist/cjs/call.spec.native.js.map +0 -1
- package/dist/cjs/collection.spec.cjs +0 -186
- package/dist/cjs/collection.spec.native.js +0 -216
- package/dist/cjs/collection.spec.native.js.map +0 -1
- package/dist/cjs/common/types.spec.cjs +0 -266
- package/dist/cjs/common/types.spec.native.js +0 -271
- package/dist/cjs/common/types.spec.native.js.map +0 -1
- package/dist/cjs/core/http.spec.cjs +0 -1259
- package/dist/cjs/core/http.spec.native.js +0 -1401
- package/dist/cjs/core/http.spec.native.js.map +0 -1
- package/dist/cjs/core/wire.spec.cjs +0 -478
- package/dist/cjs/core/wire.spec.native.js +0 -482
- package/dist/cjs/core/wire.spec.native.js.map +0 -1
- package/dist/cjs/db.spec.cjs +0 -862
- package/dist/cjs/db.spec.native.js +0 -865
- package/dist/cjs/db.spec.native.js.map +0 -1
- package/dist/cjs/doctype.spec.cjs +0 -460
- package/dist/cjs/doctype.spec.native.js +0 -538
- package/dist/cjs/doctype.spec.native.js.map +0 -1
- package/dist/cjs/file.spec.cjs +0 -315
- package/dist/cjs/file.spec.native.js +0 -342
- package/dist/cjs/file.spec.native.js.map +0 -1
- package/dist/cjs/frappeApp.spec.cjs +0 -306
- package/dist/cjs/frappeApp.spec.native.js +0 -308
- package/dist/cjs/frappeApp.spec.native.js.map +0 -1
- package/dist/cjs/frappeReact.spec.cjs +0 -531
- package/dist/cjs/frappeReact.spec.native.js +0 -670
- package/dist/cjs/frappeReact.spec.native.js.map +0 -1
- package/dist/cjs/optimistic.spec.cjs +0 -11
- package/dist/cjs/optimistic.spec.native.js +0 -19
- package/dist/cjs/optimistic.spec.native.js.map +0 -1
- package/dist/cjs/pacer.spec.cjs +0 -470
- package/dist/cjs/pacer.spec.native.js +0 -537
- package/dist/cjs/pacer.spec.native.js.map +0 -1
- package/dist/cjs/realtime.spec.cjs +0 -326
- package/dist/cjs/realtime.spec.native.js +0 -392
- package/dist/cjs/realtime.spec.native.js.map +0 -1
- package/dist/cjs/sync/backfill.spec.cjs +0 -586
- package/dist/cjs/sync/backfill.spec.native.js +0 -712
- package/dist/cjs/sync/backfill.spec.native.js.map +0 -1
- package/dist/cjs/sync/debug.spec.cjs +0 -125
- package/dist/cjs/sync/debug.spec.native.js +0 -133
- package/dist/cjs/sync/debug.spec.native.js.map +0 -1
- package/dist/cjs/sync/filters.spec.cjs +0 -168
- package/dist/cjs/sync/filters.spec.native.js +0 -173
- package/dist/cjs/sync/filters.spec.native.js.map +0 -1
- package/dist/cjs/sync/merge.spec.cjs +0 -908
- package/dist/cjs/sync/merge.spec.native.js +0 -937
- package/dist/cjs/sync/merge.spec.native.js.map +0 -1
- package/dist/cjs/sync/multiOrderBy.spec.cjs +0 -165
- package/dist/cjs/sync/multiOrderBy.spec.native.js +0 -229
- package/dist/cjs/sync/multiOrderBy.spec.native.js.map +0 -1
- package/dist/cjs/sync/mutationEngine.spec.cjs +0 -1158
- package/dist/cjs/sync/mutationEngine.spec.native.js +0 -1173
- package/dist/cjs/sync/mutationEngine.spec.native.js.map +0 -1
- package/dist/cjs/sync/mutationJournal.spec.cjs +0 -330
- package/dist/cjs/sync/mutationJournal.spec.native.js +0 -333
- package/dist/cjs/sync/mutationJournal.spec.native.js.map +0 -1
- package/dist/cjs/sync/mutations.spec.cjs +0 -327
- package/dist/cjs/sync/mutations.spec.native.js +0 -330
- package/dist/cjs/sync/mutations.spec.native.js.map +0 -1
- package/dist/cjs/sync/normalizedCache.spec.cjs +0 -302
- package/dist/cjs/sync/normalizedCache.spec.native.js +0 -311
- package/dist/cjs/sync/normalizedCache.spec.native.js.map +0 -1
- package/dist/cjs/sync/persistence.spec.cjs +0 -562
- package/dist/cjs/sync/persistence.spec.native.js +0 -572
- package/dist/cjs/sync/persistence.spec.native.js.map +0 -1
- package/dist/cjs/sync/realtime.spec.cjs +0 -499
- package/dist/cjs/sync/realtime.spec.native.js +0 -512
- package/dist/cjs/sync/realtime.spec.native.js.map +0 -1
- package/dist/cjs/sync/rehydration.spec.cjs +0 -731
- package/dist/cjs/sync/rehydration.spec.native.js +0 -736
- package/dist/cjs/sync/rehydration.spec.native.js.map +0 -1
- package/dist/cjs/sync/storage.spec.cjs +0 -446
- package/dist/cjs/sync/storage.spec.native.js +0 -470
- package/dist/cjs/sync/storage.spec.native.js.map +0 -1
- package/dist/cjs/sync/subscriptionManager.spec.cjs +0 -1102
- package/dist/cjs/sync/subscriptionManager.spec.native.js +0 -1136
- package/dist/cjs/sync/subscriptionManager.spec.native.js.map +0 -1
- package/dist/cjs/sync/subscriptions.spec.cjs +0 -937
- package/dist/cjs/sync/subscriptions.spec.native.js +0 -969
- package/dist/cjs/sync/subscriptions.spec.native.js.map +0 -1
- package/dist/cjs/sync/syncModule.spec.cjs +0 -206
- package/dist/cjs/sync/syncModule.spec.native.js +0 -222
- package/dist/cjs/sync/syncModule.spec.native.js.map +0 -1
- package/dist/cjs/sync/types.spec.cjs +0 -246
- package/dist/cjs/sync/types.spec.native.js +0 -256
- package/dist/cjs/sync/types.spec.native.js.map +0 -1
- package/dist/cjs/sync/virtualPageManager.spec.cjs +0 -801
- package/dist/cjs/sync/virtualPageManager.spec.native.js +0 -873
- package/dist/cjs/sync/virtualPageManager.spec.native.js.map +0 -1
- package/dist/cjs/util.spec.cjs +0 -43
- package/dist/cjs/util.spec.native.js +0 -48
- package/dist/cjs/util.spec.native.js.map +0 -1
- package/dist/esm/auth.spec.mjs +0 -323
- package/dist/esm/auth.spec.mjs.map +0 -1
- package/dist/esm/auth.spec.native.js +0 -327
- package/dist/esm/auth.spec.native.js.map +0 -1
- package/dist/esm/call.spec.mjs +0 -428
- package/dist/esm/call.spec.mjs.map +0 -1
- package/dist/esm/call.spec.native.js +0 -428
- package/dist/esm/call.spec.native.js.map +0 -1
- package/dist/esm/collection.spec.mjs +0 -187
- package/dist/esm/collection.spec.mjs.map +0 -1
- package/dist/esm/collection.spec.native.js +0 -214
- package/dist/esm/collection.spec.native.js.map +0 -1
- package/dist/esm/common/types.spec.mjs +0 -267
- package/dist/esm/common/types.spec.mjs.map +0 -1
- package/dist/esm/common/types.spec.native.js +0 -269
- package/dist/esm/common/types.spec.native.js.map +0 -1
- package/dist/esm/core/http.spec.mjs +0 -1260
- package/dist/esm/core/http.spec.mjs.map +0 -1
- package/dist/esm/core/http.spec.native.js +0 -1399
- package/dist/esm/core/http.spec.native.js.map +0 -1
- package/dist/esm/core/wire.spec.mjs +0 -479
- package/dist/esm/core/wire.spec.mjs.map +0 -1
- package/dist/esm/core/wire.spec.native.js +0 -480
- package/dist/esm/core/wire.spec.native.js.map +0 -1
- package/dist/esm/db.spec.mjs +0 -863
- package/dist/esm/db.spec.mjs.map +0 -1
- package/dist/esm/db.spec.native.js +0 -863
- package/dist/esm/db.spec.native.js.map +0 -1
- package/dist/esm/doctype.spec.mjs +0 -461
- package/dist/esm/doctype.spec.mjs.map +0 -1
- package/dist/esm/doctype.spec.native.js +0 -536
- package/dist/esm/doctype.spec.native.js.map +0 -1
- package/dist/esm/file.spec.mjs +0 -316
- package/dist/esm/file.spec.mjs.map +0 -1
- package/dist/esm/file.spec.native.js +0 -340
- package/dist/esm/file.spec.native.js.map +0 -1
- package/dist/esm/frappeApp.spec.mjs +0 -307
- package/dist/esm/frappeApp.spec.mjs.map +0 -1
- package/dist/esm/frappeApp.spec.native.js +0 -306
- package/dist/esm/frappeApp.spec.native.js.map +0 -1
- package/dist/esm/frappeReact.spec.mjs +0 -532
- package/dist/esm/frappeReact.spec.mjs.map +0 -1
- package/dist/esm/frappeReact.spec.native.js +0 -668
- package/dist/esm/frappeReact.spec.native.js.map +0 -1
- package/dist/esm/optimistic.spec.mjs +0 -12
- package/dist/esm/optimistic.spec.mjs.map +0 -1
- package/dist/esm/optimistic.spec.native.js +0 -17
- package/dist/esm/optimistic.spec.native.js.map +0 -1
- package/dist/esm/pacer.spec.mjs +0 -447
- package/dist/esm/pacer.spec.mjs.map +0 -1
- package/dist/esm/pacer.spec.native.js +0 -511
- package/dist/esm/pacer.spec.native.js.map +0 -1
- package/dist/esm/realtime.spec.mjs +0 -327
- package/dist/esm/realtime.spec.mjs.map +0 -1
- package/dist/esm/realtime.spec.native.js +0 -390
- package/dist/esm/realtime.spec.native.js.map +0 -1
- package/dist/esm/sync/backfill.spec.mjs +0 -563
- package/dist/esm/sync/backfill.spec.mjs.map +0 -1
- package/dist/esm/sync/backfill.spec.native.js +0 -686
- package/dist/esm/sync/backfill.spec.native.js.map +0 -1
- package/dist/esm/sync/debug.spec.mjs +0 -102
- package/dist/esm/sync/debug.spec.mjs.map +0 -1
- package/dist/esm/sync/debug.spec.native.js +0 -107
- package/dist/esm/sync/debug.spec.native.js.map +0 -1
- package/dist/esm/sync/filters.spec.mjs +0 -169
- package/dist/esm/sync/filters.spec.mjs.map +0 -1
- package/dist/esm/sync/filters.spec.native.js +0 -171
- package/dist/esm/sync/filters.spec.native.js.map +0 -1
- package/dist/esm/sync/merge.spec.mjs +0 -909
- package/dist/esm/sync/merge.spec.mjs.map +0 -1
- package/dist/esm/sync/merge.spec.native.js +0 -935
- package/dist/esm/sync/merge.spec.native.js.map +0 -1
- package/dist/esm/sync/multiOrderBy.spec.mjs +0 -166
- package/dist/esm/sync/multiOrderBy.spec.mjs.map +0 -1
- package/dist/esm/sync/multiOrderBy.spec.native.js +0 -227
- package/dist/esm/sync/multiOrderBy.spec.native.js.map +0 -1
- package/dist/esm/sync/mutationEngine.spec.mjs +0 -1159
- package/dist/esm/sync/mutationEngine.spec.mjs.map +0 -1
- package/dist/esm/sync/mutationEngine.spec.native.js +0 -1171
- package/dist/esm/sync/mutationEngine.spec.native.js.map +0 -1
- package/dist/esm/sync/mutationJournal.spec.mjs +0 -331
- package/dist/esm/sync/mutationJournal.spec.mjs.map +0 -1
- package/dist/esm/sync/mutationJournal.spec.native.js +0 -331
- package/dist/esm/sync/mutationJournal.spec.native.js.map +0 -1
- package/dist/esm/sync/mutations.spec.mjs +0 -328
- package/dist/esm/sync/mutations.spec.mjs.map +0 -1
- package/dist/esm/sync/mutations.spec.native.js +0 -328
- package/dist/esm/sync/mutations.spec.native.js.map +0 -1
- package/dist/esm/sync/normalizedCache.spec.mjs +0 -303
- package/dist/esm/sync/normalizedCache.spec.mjs.map +0 -1
- package/dist/esm/sync/normalizedCache.spec.native.js +0 -309
- package/dist/esm/sync/normalizedCache.spec.native.js.map +0 -1
- package/dist/esm/sync/persistence.spec.mjs +0 -563
- package/dist/esm/sync/persistence.spec.mjs.map +0 -1
- package/dist/esm/sync/persistence.spec.native.js +0 -570
- package/dist/esm/sync/persistence.spec.native.js.map +0 -1
- package/dist/esm/sync/realtime.spec.mjs +0 -500
- package/dist/esm/sync/realtime.spec.mjs.map +0 -1
- package/dist/esm/sync/realtime.spec.native.js +0 -510
- package/dist/esm/sync/realtime.spec.native.js.map +0 -1
- package/dist/esm/sync/rehydration.spec.mjs +0 -732
- package/dist/esm/sync/rehydration.spec.mjs.map +0 -1
- package/dist/esm/sync/rehydration.spec.native.js +0 -734
- package/dist/esm/sync/rehydration.spec.native.js.map +0 -1
- package/dist/esm/sync/storage.spec.mjs +0 -423
- package/dist/esm/sync/storage.spec.mjs.map +0 -1
- package/dist/esm/sync/storage.spec.native.js +0 -444
- package/dist/esm/sync/storage.spec.native.js.map +0 -1
- package/dist/esm/sync/subscriptionManager.spec.mjs +0 -1079
- package/dist/esm/sync/subscriptionManager.spec.mjs.map +0 -1
- package/dist/esm/sync/subscriptionManager.spec.native.js +0 -1110
- package/dist/esm/sync/subscriptionManager.spec.native.js.map +0 -1
- package/dist/esm/sync/subscriptions.spec.mjs +0 -914
- package/dist/esm/sync/subscriptions.spec.mjs.map +0 -1
- package/dist/esm/sync/subscriptions.spec.native.js +0 -943
- package/dist/esm/sync/subscriptions.spec.native.js.map +0 -1
- package/dist/esm/sync/syncModule.spec.mjs +0 -207
- package/dist/esm/sync/syncModule.spec.mjs.map +0 -1
- package/dist/esm/sync/syncModule.spec.native.js +0 -220
- package/dist/esm/sync/syncModule.spec.native.js.map +0 -1
- package/dist/esm/sync/types.spec.mjs +0 -247
- package/dist/esm/sync/types.spec.mjs.map +0 -1
- package/dist/esm/sync/types.spec.native.js +0 -254
- package/dist/esm/sync/types.spec.native.js.map +0 -1
- package/dist/esm/sync/virtualPageManager.spec.mjs +0 -802
- package/dist/esm/sync/virtualPageManager.spec.mjs.map +0 -1
- package/dist/esm/sync/virtualPageManager.spec.native.js +0 -871
- package/dist/esm/sync/virtualPageManager.spec.native.js.map +0 -1
- package/dist/esm/util.spec.mjs +0 -20
- package/dist/esm/util.spec.mjs.map +0 -1
- package/dist/esm/util.spec.native.js +0 -22
- package/dist/esm/util.spec.native.js.map +0 -1
- package/dist/jsx/auth.spec.mjs +0 -323
- package/dist/jsx/auth.spec.mjs.map +0 -1
- package/dist/jsx/auth.spec.native.js +0 -329
- package/dist/jsx/auth.spec.native.js.map +0 -1
- package/dist/jsx/call.spec.mjs +0 -428
- package/dist/jsx/call.spec.mjs.map +0 -1
- package/dist/jsx/call.spec.native.js +0 -430
- package/dist/jsx/call.spec.native.js.map +0 -1
- package/dist/jsx/collection.spec.mjs +0 -187
- package/dist/jsx/collection.spec.mjs.map +0 -1
- package/dist/jsx/collection.spec.native.js +0 -216
- package/dist/jsx/collection.spec.native.js.map +0 -1
- package/dist/jsx/common/types.spec.mjs +0 -267
- package/dist/jsx/common/types.spec.mjs.map +0 -1
- package/dist/jsx/common/types.spec.native.js +0 -271
- package/dist/jsx/common/types.spec.native.js.map +0 -1
- package/dist/jsx/core/http.spec.mjs +0 -1260
- package/dist/jsx/core/http.spec.mjs.map +0 -1
- package/dist/jsx/core/http.spec.native.js +0 -1401
- package/dist/jsx/core/http.spec.native.js.map +0 -1
- package/dist/jsx/core/wire.spec.mjs +0 -479
- package/dist/jsx/core/wire.spec.mjs.map +0 -1
- package/dist/jsx/core/wire.spec.native.js +0 -482
- package/dist/jsx/core/wire.spec.native.js.map +0 -1
- package/dist/jsx/db.spec.mjs +0 -863
- package/dist/jsx/db.spec.mjs.map +0 -1
- package/dist/jsx/db.spec.native.js +0 -865
- package/dist/jsx/db.spec.native.js.map +0 -1
- package/dist/jsx/doctype.spec.mjs +0 -461
- package/dist/jsx/doctype.spec.mjs.map +0 -1
- package/dist/jsx/doctype.spec.native.js +0 -538
- package/dist/jsx/doctype.spec.native.js.map +0 -1
- package/dist/jsx/file.spec.mjs +0 -316
- package/dist/jsx/file.spec.mjs.map +0 -1
- package/dist/jsx/file.spec.native.js +0 -342
- package/dist/jsx/file.spec.native.js.map +0 -1
- package/dist/jsx/frappeApp.spec.mjs +0 -307
- package/dist/jsx/frappeApp.spec.mjs.map +0 -1
- package/dist/jsx/frappeApp.spec.native.js +0 -308
- package/dist/jsx/frappeApp.spec.native.js.map +0 -1
- package/dist/jsx/frappeReact.spec.mjs +0 -532
- package/dist/jsx/frappeReact.spec.mjs.map +0 -1
- package/dist/jsx/frappeReact.spec.native.js +0 -670
- package/dist/jsx/frappeReact.spec.native.js.map +0 -1
- package/dist/jsx/optimistic.spec.mjs +0 -12
- package/dist/jsx/optimistic.spec.mjs.map +0 -1
- package/dist/jsx/optimistic.spec.native.js +0 -19
- package/dist/jsx/optimistic.spec.native.js.map +0 -1
- package/dist/jsx/pacer.spec.mjs +0 -447
- package/dist/jsx/pacer.spec.mjs.map +0 -1
- package/dist/jsx/pacer.spec.native.js +0 -537
- package/dist/jsx/pacer.spec.native.js.map +0 -1
- package/dist/jsx/realtime.spec.mjs +0 -327
- package/dist/jsx/realtime.spec.mjs.map +0 -1
- package/dist/jsx/realtime.spec.native.js +0 -392
- package/dist/jsx/realtime.spec.native.js.map +0 -1
- package/dist/jsx/sync/backfill.spec.mjs +0 -563
- package/dist/jsx/sync/backfill.spec.mjs.map +0 -1
- package/dist/jsx/sync/backfill.spec.native.js +0 -712
- package/dist/jsx/sync/backfill.spec.native.js.map +0 -1
- package/dist/jsx/sync/debug.spec.mjs +0 -102
- package/dist/jsx/sync/debug.spec.mjs.map +0 -1
- package/dist/jsx/sync/debug.spec.native.js +0 -133
- package/dist/jsx/sync/debug.spec.native.js.map +0 -1
- package/dist/jsx/sync/filters.spec.mjs +0 -169
- package/dist/jsx/sync/filters.spec.mjs.map +0 -1
- package/dist/jsx/sync/filters.spec.native.js +0 -173
- package/dist/jsx/sync/filters.spec.native.js.map +0 -1
- package/dist/jsx/sync/merge.spec.mjs +0 -909
- package/dist/jsx/sync/merge.spec.mjs.map +0 -1
- package/dist/jsx/sync/merge.spec.native.js +0 -937
- package/dist/jsx/sync/merge.spec.native.js.map +0 -1
- package/dist/jsx/sync/multiOrderBy.spec.mjs +0 -166
- package/dist/jsx/sync/multiOrderBy.spec.mjs.map +0 -1
- package/dist/jsx/sync/multiOrderBy.spec.native.js +0 -229
- package/dist/jsx/sync/multiOrderBy.spec.native.js.map +0 -1
- package/dist/jsx/sync/mutationEngine.spec.mjs +0 -1159
- package/dist/jsx/sync/mutationEngine.spec.mjs.map +0 -1
- package/dist/jsx/sync/mutationEngine.spec.native.js +0 -1173
- package/dist/jsx/sync/mutationEngine.spec.native.js.map +0 -1
- package/dist/jsx/sync/mutationJournal.spec.mjs +0 -331
- package/dist/jsx/sync/mutationJournal.spec.mjs.map +0 -1
- package/dist/jsx/sync/mutationJournal.spec.native.js +0 -333
- package/dist/jsx/sync/mutationJournal.spec.native.js.map +0 -1
- package/dist/jsx/sync/mutations.spec.mjs +0 -328
- package/dist/jsx/sync/mutations.spec.mjs.map +0 -1
- package/dist/jsx/sync/mutations.spec.native.js +0 -330
- package/dist/jsx/sync/mutations.spec.native.js.map +0 -1
- package/dist/jsx/sync/normalizedCache.spec.mjs +0 -303
- package/dist/jsx/sync/normalizedCache.spec.mjs.map +0 -1
- package/dist/jsx/sync/normalizedCache.spec.native.js +0 -311
- package/dist/jsx/sync/normalizedCache.spec.native.js.map +0 -1
- package/dist/jsx/sync/persistence.spec.mjs +0 -563
- package/dist/jsx/sync/persistence.spec.mjs.map +0 -1
- package/dist/jsx/sync/persistence.spec.native.js +0 -572
- package/dist/jsx/sync/persistence.spec.native.js.map +0 -1
- package/dist/jsx/sync/realtime.spec.mjs +0 -500
- package/dist/jsx/sync/realtime.spec.mjs.map +0 -1
- package/dist/jsx/sync/realtime.spec.native.js +0 -512
- package/dist/jsx/sync/realtime.spec.native.js.map +0 -1
- package/dist/jsx/sync/rehydration.spec.mjs +0 -732
- package/dist/jsx/sync/rehydration.spec.mjs.map +0 -1
- package/dist/jsx/sync/rehydration.spec.native.js +0 -736
- package/dist/jsx/sync/rehydration.spec.native.js.map +0 -1
- package/dist/jsx/sync/storage.spec.mjs +0 -423
- package/dist/jsx/sync/storage.spec.mjs.map +0 -1
- package/dist/jsx/sync/storage.spec.native.js +0 -470
- package/dist/jsx/sync/storage.spec.native.js.map +0 -1
- package/dist/jsx/sync/subscriptionManager.spec.mjs +0 -1079
- package/dist/jsx/sync/subscriptionManager.spec.mjs.map +0 -1
- package/dist/jsx/sync/subscriptionManager.spec.native.js +0 -1136
- package/dist/jsx/sync/subscriptionManager.spec.native.js.map +0 -1
- package/dist/jsx/sync/subscriptions.spec.mjs +0 -914
- package/dist/jsx/sync/subscriptions.spec.mjs.map +0 -1
- package/dist/jsx/sync/subscriptions.spec.native.js +0 -969
- package/dist/jsx/sync/subscriptions.spec.native.js.map +0 -1
- package/dist/jsx/sync/syncModule.spec.mjs +0 -207
- package/dist/jsx/sync/syncModule.spec.mjs.map +0 -1
- package/dist/jsx/sync/syncModule.spec.native.js +0 -222
- package/dist/jsx/sync/syncModule.spec.native.js.map +0 -1
- package/dist/jsx/sync/types.spec.mjs +0 -247
- package/dist/jsx/sync/types.spec.mjs.map +0 -1
- package/dist/jsx/sync/types.spec.native.js +0 -256
- package/dist/jsx/sync/types.spec.native.js.map +0 -1
- package/dist/jsx/sync/virtualPageManager.spec.mjs +0 -802
- package/dist/jsx/sync/virtualPageManager.spec.mjs.map +0 -1
- package/dist/jsx/sync/virtualPageManager.spec.native.js +0 -873
- package/dist/jsx/sync/virtualPageManager.spec.native.js.map +0 -1
- package/dist/jsx/util.spec.mjs +0 -20
- package/dist/jsx/util.spec.mjs.map +0 -1
- package/dist/jsx/util.spec.native.js +0 -48
- package/dist/jsx/util.spec.native.js.map +0 -1
package/src/realtime.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { isNative } from "@multiplatform.one/platform";
|
|
1
2
|
import type { ManagerOptions, Socket, SocketOptions } from "socket.io-client";
|
|
2
3
|
import { io } from "socket.io-client";
|
|
3
4
|
import type { AuthConfig } from "./core/types";
|
|
4
|
-
import { isBrowser } from "./util";
|
|
5
|
+
import { isBrowser, isGjs } from "./util";
|
|
5
6
|
|
|
6
7
|
export type Handler = (payload: unknown) => void;
|
|
7
8
|
|
|
@@ -27,10 +28,30 @@ export interface RealtimeConfig {
|
|
|
27
28
|
* `/socket.io` to the SocketIO backend.
|
|
28
29
|
*/
|
|
29
30
|
socketPort?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Frappe site name, used as the Socket.IO namespace.
|
|
33
|
+
*
|
|
34
|
+
* Frappe's realtime server is multi-tenant: every site lives on its own
|
|
35
|
+
* Socket.IO namespace (`/<sitename>`) guarded by the auth middleware, and
|
|
36
|
+
* server events are only emitted to that namespace. Connecting to the
|
|
37
|
+
* root namespace `/` silently succeeds but never receives any events.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to the hostname of `baseURL`, which matches Frappe's own
|
|
40
|
+
* site-name resolution for host-served sites (and the `default_site` of a
|
|
41
|
+
* standard dev bench on localhost).
|
|
42
|
+
*/
|
|
43
|
+
siteName?: string;
|
|
30
44
|
platform?: RealtimePlatform;
|
|
31
45
|
socketOptions?: Partial<ManagerOptions & SocketOptions>;
|
|
32
46
|
}
|
|
33
47
|
|
|
48
|
+
type DocRoom = { doctype: string; name: string };
|
|
49
|
+
type ListRoom = { doctype: string; filters?: Record<string, unknown> };
|
|
50
|
+
|
|
51
|
+
function docRoomKey(doctype: string, name: string) {
|
|
52
|
+
return `${doctype}\0${name}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
export class RealtimeModule {
|
|
35
56
|
private socket: Socket | undefined;
|
|
36
57
|
private handlerMap = new Map<string, Set<Handler>>();
|
|
@@ -38,9 +59,16 @@ export class RealtimeModule {
|
|
|
38
59
|
private attachedSocketEvents = new Set<string>();
|
|
39
60
|
/** Stored socket event handler references for proper cleanup. */
|
|
40
61
|
private socketEventHandlers = new Map<string, (payload: unknown) => void>();
|
|
62
|
+
/** Active doc rooms — re-emitted on every (re)connect. */
|
|
63
|
+
private docRooms = new Map<string, DocRoom>();
|
|
64
|
+
/** Active list rooms — re-emitted on every (re)connect. */
|
|
65
|
+
private listRooms = new Map<string, ListRoom>();
|
|
41
66
|
private socketHealthy = false;
|
|
42
67
|
private isConnecting = false;
|
|
68
|
+
private healthyWaiters = new Set<(ok: boolean) => void>();
|
|
43
69
|
private config: RealtimeConfig;
|
|
70
|
+
/** In-flight credential re-resolution (deduped across triggers). */
|
|
71
|
+
private authRefreshInflight?: Promise<void>;
|
|
44
72
|
|
|
45
73
|
constructor(config: RealtimeConfig) {
|
|
46
74
|
this.config = {
|
|
@@ -50,28 +78,76 @@ export class RealtimeModule {
|
|
|
50
78
|
}
|
|
51
79
|
|
|
52
80
|
private detectPlatform() {
|
|
53
|
-
|
|
54
|
-
|
|
81
|
+
// GJS (GNOME desktop) FIRST: isBrowser() is true there (the sync engine
|
|
82
|
+
// runs), but the Web classification would be wrong — GJS has no XHR for
|
|
83
|
+
// a polling-first handshake, and its WebSocket comes from an
|
|
84
|
+
// entry-installed libsoup polyfill that DOES accept custom handshake
|
|
85
|
+
// headers (RN-style `{ headers }` third argument). Classify like Node:
|
|
86
|
+
// websocket-first transports + the extraHeaders auth path.
|
|
87
|
+
if (isGjs()) return RealtimePlatform.Node;
|
|
88
|
+
// React Native next: RN sets navigator.product === "ReactNative" but no
|
|
89
|
+
// userAgent, so a UA-only sniff misclassifies RN as Web — skipping the
|
|
90
|
+
// extraHeaders auth path (sid cookie / Origin) and connecting cookie-auth
|
|
91
|
+
// sockets as guest. The platform package's static isNative flag is the
|
|
92
|
+
// canonical signal; the navigator checks are runtime fallbacks (kept for
|
|
93
|
+
// environments that fake a RN UA, e.g. tests).
|
|
94
|
+
if (
|
|
95
|
+
isNative ||
|
|
96
|
+
(typeof navigator !== "undefined" &&
|
|
97
|
+
(navigator.product === "ReactNative" || navigator.userAgent?.includes("ReactNative")))
|
|
98
|
+
) {
|
|
55
99
|
return RealtimePlatform.ReactNative;
|
|
56
100
|
}
|
|
101
|
+
if (!isBrowser()) return RealtimePlatform.Node;
|
|
57
102
|
return RealtimePlatform.Web;
|
|
58
103
|
}
|
|
59
104
|
|
|
60
105
|
private getSocketURL(): string {
|
|
61
106
|
const base = this.config.baseURL.replace(/\/$/, "");
|
|
62
|
-
if (!this.config.socketPort) return base;
|
|
63
107
|
try {
|
|
64
108
|
const url = new URL(base);
|
|
65
|
-
url.port
|
|
66
|
-
|
|
109
|
+
// Assemble origin from parts instead of mutating url.port and reading
|
|
110
|
+
// url.origin: minimal URL polyfills (e.g. the GJS/react-gnome shim)
|
|
111
|
+
// parse host at construction and never recompute origin from a port
|
|
112
|
+
// assignment — which silently dropped socketPort and pointed the
|
|
113
|
+
// socket at the web server.
|
|
114
|
+
const port = this.config.socketPort ? String(this.config.socketPort) : url.port;
|
|
115
|
+
const host = port ? `${url.hostname}:${port}` : url.hostname;
|
|
116
|
+
// The path portion of the URL passed to io() is the Socket.IO
|
|
117
|
+
// namespace — frappe requires it to be the site name (see siteName).
|
|
118
|
+
const siteName = this.config.siteName || url.hostname;
|
|
119
|
+
return `${url.protocol}//${host}/${siteName}`;
|
|
67
120
|
} catch {
|
|
68
|
-
// Fallback:
|
|
69
|
-
|
|
121
|
+
// Fallback: string manipulation when URL constructor isn't available
|
|
122
|
+
let result = base;
|
|
123
|
+
if (this.config.socketPort) {
|
|
124
|
+
result = result.replace(/(:\d+)/, `:${this.config.socketPort}`);
|
|
125
|
+
}
|
|
126
|
+
const siteName = this.config.siteName || result.replace(/^https?:\/\//, "").split(/[/:]/)[0];
|
|
127
|
+
return `${result}/${siteName}`;
|
|
70
128
|
}
|
|
71
129
|
}
|
|
72
130
|
|
|
73
131
|
private async getAuthOptions(): Promise<Partial<ManagerOptions & SocketOptions>> {
|
|
74
132
|
const authOptions: Partial<ManagerOptions & SocketOptions> = {};
|
|
133
|
+
if (this.config.platform !== RealtimePlatform.Web) {
|
|
134
|
+
// Non-browser clients must send an Origin header themselves: frappe's
|
|
135
|
+
// socketio middleware rejects mismatched Origin/Host hostnames AND uses
|
|
136
|
+
// the Origin as the base URL for its can_subscribe_* permission
|
|
137
|
+
// callbacks to the web server — so it must point at the WEB base URL
|
|
138
|
+
// (baseURL), not the socketio port. The Web platform must NEVER set it:
|
|
139
|
+
// Origin is a forbidden header in browsers (they always send the page
|
|
140
|
+
// origin themselves), and attempting it makes engine.io's XHR transport
|
|
141
|
+
// log "Attempt to set a forbidden header was denied: Origin" on every
|
|
142
|
+
// polling request. Browser-like TEST runtimes (happy-dom/jsdom over
|
|
143
|
+
// node transports) that need a bench-pointing Origin opt in via
|
|
144
|
+
// `platform: RealtimePlatform.Node` or `socketOptions.extraHeaders`.
|
|
145
|
+
try {
|
|
146
|
+
authOptions.extraHeaders = { Origin: new URL(this.config.baseURL).origin };
|
|
147
|
+
} catch {
|
|
148
|
+
authOptions.extraHeaders = { Origin: this.config.baseURL.replace(/\/$/, "") };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
75
151
|
if (this.config.platform !== RealtimePlatform.Web && this.config.auth?.sessionId) {
|
|
76
152
|
const sessionId =
|
|
77
153
|
typeof this.config.auth.sessionId === "function"
|
|
@@ -79,40 +155,125 @@ export class RealtimeModule {
|
|
|
79
155
|
: this.config.auth.sessionId;
|
|
80
156
|
const Cookie = `sid=${sessionId}`;
|
|
81
157
|
authOptions.extraHeaders = {
|
|
158
|
+
...authOptions.extraHeaders,
|
|
82
159
|
Cookie,
|
|
83
160
|
};
|
|
84
|
-
authOptions.transportOptions = {
|
|
85
|
-
websocket: {
|
|
86
|
-
extraHeaders: {
|
|
87
|
-
Cookie,
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
polling: {
|
|
91
|
-
extraHeaders: {
|
|
92
|
-
Cookie,
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
161
|
}
|
|
97
162
|
if (this.config.auth?.useToken && this.config.auth.token) {
|
|
98
163
|
const token =
|
|
99
164
|
typeof this.config.auth.token === "function"
|
|
100
165
|
? await this.config.auth.token()
|
|
101
166
|
: this.config.auth.token;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
167
|
+
// Skip when the getter resolves empty (signed out): a bare
|
|
168
|
+
// "Bearer " header would fail frappe's auth instead of falling
|
|
169
|
+
// back to guest.
|
|
170
|
+
if (token) {
|
|
171
|
+
const authType = this.config.auth.type || "Bearer";
|
|
172
|
+
if (!authOptions.extraHeaders) authOptions.extraHeaders = {};
|
|
173
|
+
authOptions.extraHeaders.Authorization =
|
|
174
|
+
authType === "Bearer" ? `Bearer ${token}` : `token ${token}`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (authOptions.extraHeaders) {
|
|
178
|
+
// Mirror headers into per-transport options: node websocket reads
|
|
179
|
+
// transportOptions.websocket.extraHeaders, browser polling reads the
|
|
180
|
+
// top-level extraHeaders — covering both keeps every handshake
|
|
181
|
+
// (initial, upgrade, reconnect) authenticated.
|
|
182
|
+
authOptions.transportOptions = {
|
|
183
|
+
websocket: { extraHeaders: { ...authOptions.extraHeaders } },
|
|
184
|
+
polling: { extraHeaders: { ...authOptions.extraHeaders } },
|
|
185
|
+
};
|
|
106
186
|
}
|
|
107
187
|
return authOptions;
|
|
108
188
|
}
|
|
109
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Re-resolve credentials (token function / session id) and swap them into
|
|
192
|
+
* the LIVE manager options in place. Socket.IO constructs a fresh engine
|
|
193
|
+
* from `manager.opts` on every (re)connect attempt, so mutating the opts
|
|
194
|
+
* makes the next handshake carry the refreshed token — without tearing
|
|
195
|
+
* down handlers or room subscriptions. Called on disconnect and before
|
|
196
|
+
* reconnect attempts so a token that rotated mid-session (e.g. Keycloak
|
|
197
|
+
* refresh) never strands the socket in an auth-rejected retry loop.
|
|
198
|
+
*/
|
|
199
|
+
private refreshSocketAuth(): Promise<void> {
|
|
200
|
+
const socket = this.socket;
|
|
201
|
+
if (!socket) return Promise.resolve();
|
|
202
|
+
if (this.authRefreshInflight) return this.authRefreshInflight;
|
|
203
|
+
this.authRefreshInflight = (async () => {
|
|
204
|
+
try {
|
|
205
|
+
const authOptions = await this.getAuthOptions();
|
|
206
|
+
const manager = (socket as { io?: { opts?: Record<string, unknown> } }).io;
|
|
207
|
+
if (manager?.opts) Object.assign(manager.opts, authOptions);
|
|
208
|
+
} catch {
|
|
209
|
+
// Keep the previous credentials; the next disconnect/reconnect
|
|
210
|
+
// cycle retries the refresh.
|
|
211
|
+
} finally {
|
|
212
|
+
this.authRefreshInflight = undefined;
|
|
213
|
+
}
|
|
214
|
+
})();
|
|
215
|
+
return this.authRefreshInflight;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private resolveHealthyWaiters(ok: boolean) {
|
|
219
|
+
for (const resolve of this.healthyWaiters) resolve(ok);
|
|
220
|
+
this.healthyWaiters.clear();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private waitUntilHealthy(timeoutMs = 20_000): Promise<boolean> {
|
|
224
|
+
if (this.socketHealthy && this.socket?.connected) return Promise.resolve(true);
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
const timer = setTimeout(() => {
|
|
227
|
+
this.healthyWaiters.delete(onReady);
|
|
228
|
+
resolve(false);
|
|
229
|
+
}, timeoutMs);
|
|
230
|
+
const onReady = (ok: boolean) => {
|
|
231
|
+
clearTimeout(timer);
|
|
232
|
+
resolve(ok);
|
|
233
|
+
};
|
|
234
|
+
this.healthyWaiters.add(onReady);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async ensureConnected(): Promise<void> {
|
|
239
|
+
if (this.socketHealthy && this.socket?.connected) return;
|
|
240
|
+
if (!this.socket && !this.isConnecting) {
|
|
241
|
+
await this.connect();
|
|
242
|
+
}
|
|
243
|
+
await this.waitUntilHealthy();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private resubscribeRooms(): void {
|
|
247
|
+
if (!this.socket || !this.socketHealthy) return;
|
|
248
|
+
// Frappe's socketio handlers take POSITIONAL args (doctype, docname) —
|
|
249
|
+
// not an object payload — and list-level subscription is doctype_subscribe.
|
|
250
|
+
for (const room of this.docRooms.values()) {
|
|
251
|
+
this.socket.emit("doc_subscribe", room.doctype, room.name);
|
|
252
|
+
}
|
|
253
|
+
for (const room of this.listRooms.values()) {
|
|
254
|
+
this.socket.emit("doctype_subscribe", room.doctype);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
110
258
|
private async connect() {
|
|
111
259
|
if (this.socket || this.isConnecting) return;
|
|
112
260
|
this.isConnecting = true;
|
|
113
261
|
try {
|
|
114
262
|
const authOptions = await this.getAuthOptions();
|
|
115
263
|
const socketURL = this.getSocketURL();
|
|
264
|
+
// Browsers always handshake over polling first (Socket.IO's default
|
|
265
|
+
// ordering): custom headers only attach to the XHR handshake (token
|
|
266
|
+
// auth), session-cookie auth and reverse-proxy-injected Authorization
|
|
267
|
+
// only apply to HTTP requests, and ws-upgrade-hostile dev proxies
|
|
268
|
+
// (e.g. Vite in middleware mode — the Storybook dev server) hang raw
|
|
269
|
+
// websocket upgrades forever, so a websocket-first browser client
|
|
270
|
+
// stalls ~20s per attempt and never connects. Socket.IO upgrades the
|
|
271
|
+
// established session to websocket when the path supports it.
|
|
272
|
+
// Everywhere else (Node, React Native) websocket-first is fine.
|
|
273
|
+
const transports =
|
|
274
|
+
this.config.platform === RealtimePlatform.Web
|
|
275
|
+
? ["polling", "websocket"]
|
|
276
|
+
: ["websocket", "polling"];
|
|
116
277
|
this.socket = io(socketURL, {
|
|
117
278
|
autoConnect: false,
|
|
118
279
|
path: "/socket.io",
|
|
@@ -122,7 +283,7 @@ export class RealtimeModule {
|
|
|
122
283
|
reconnectionDelay: 1000,
|
|
123
284
|
reconnectionDelayMax: 5000,
|
|
124
285
|
timeout: 20000,
|
|
125
|
-
transports
|
|
286
|
+
transports,
|
|
126
287
|
withCredentials: true,
|
|
127
288
|
...authOptions,
|
|
128
289
|
...this.config.socketOptions,
|
|
@@ -131,10 +292,31 @@ export class RealtimeModule {
|
|
|
131
292
|
this.socket.connect();
|
|
132
293
|
} catch (err) {
|
|
133
294
|
this.isConnecting = false;
|
|
295
|
+
this.resolveHealthyWaiters(false);
|
|
134
296
|
throw err;
|
|
135
297
|
}
|
|
136
298
|
}
|
|
137
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Tear down the socket without clearing app handlers or room subscriptions.
|
|
302
|
+
* Used by auth refresh so listeners/rooms survive credential rotation.
|
|
303
|
+
*/
|
|
304
|
+
private softDisconnect(): void {
|
|
305
|
+
if (this.socket) {
|
|
306
|
+
// Prefer removeAllListeners when available (real socket.io); mocks may omit it.
|
|
307
|
+
if (typeof this.socket.removeAllListeners === "function") {
|
|
308
|
+
this.socket.removeAllListeners();
|
|
309
|
+
}
|
|
310
|
+
this.socket.disconnect();
|
|
311
|
+
this.socket = undefined;
|
|
312
|
+
}
|
|
313
|
+
this.attachedSocketEvents.clear();
|
|
314
|
+
this.socketEventHandlers.clear();
|
|
315
|
+
this.socketHealthy = false;
|
|
316
|
+
this.isConnecting = false;
|
|
317
|
+
this.resolveHealthyWaiters(false);
|
|
318
|
+
}
|
|
319
|
+
|
|
138
320
|
private initHandlers(): void {
|
|
139
321
|
if (!this.socket) return;
|
|
140
322
|
// New socket instance: reset attached event tracking so listeners are re-registered fresh
|
|
@@ -143,14 +325,31 @@ export class RealtimeModule {
|
|
|
143
325
|
this.socket.on("connect", () => {
|
|
144
326
|
this.isConnecting = false;
|
|
145
327
|
this.socketHealthy = true;
|
|
328
|
+
this.resubscribeRooms();
|
|
146
329
|
this.emitToHandlers("connected", { sid: this.socket?.id });
|
|
330
|
+
this.resolveHealthyWaiters(true);
|
|
147
331
|
});
|
|
148
332
|
this.socket.on("disconnect", (reason) => {
|
|
149
333
|
this.socketHealthy = false;
|
|
334
|
+
// Refresh credentials right away so the first reconnect attempt
|
|
335
|
+
// (>=1s later) already handshakes with a fresh token.
|
|
336
|
+
void this.refreshSocketAuth();
|
|
150
337
|
this.emitToHandlers("disconnect", { reason });
|
|
151
338
|
});
|
|
152
|
-
this.socket.on("connect_error", (
|
|
339
|
+
this.socket.on("connect_error", (error) => {
|
|
153
340
|
this.isConnecting = false;
|
|
341
|
+
// Auth-rejected handshakes (e.g. expired token) land here rather than
|
|
342
|
+
// in "disconnect" — refresh before the manager's next retry.
|
|
343
|
+
void this.refreshSocketAuth();
|
|
344
|
+
this.emitToHandlers("connect_error", error);
|
|
345
|
+
this.resolveHealthyWaiters(false);
|
|
346
|
+
});
|
|
347
|
+
// Manager-level safety net: re-resolve before every scheduled reconnect
|
|
348
|
+
// attempt (covers timeouts and transport errors that skip the socket
|
|
349
|
+
// level events above). Mock sockets in tests may not expose `.io`.
|
|
350
|
+
const manager = (this.socket as { io?: { on?: (ev: string, fn: () => void) => void } }).io;
|
|
351
|
+
manager?.on?.("reconnect_attempt", () => {
|
|
352
|
+
void this.refreshSocketAuth();
|
|
154
353
|
});
|
|
155
354
|
// Attach listeners for existing handler events, avoiding duplicates
|
|
156
355
|
for (const event of this.handlerMap.keys()) {
|
|
@@ -201,10 +400,14 @@ export class RealtimeModule {
|
|
|
201
400
|
this.handlerMap.set(event, handlerSet);
|
|
202
401
|
}
|
|
203
402
|
handlerSet.add(handler);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
403
|
+
// Registering a handler does NOT open the socket. The socket lifecycle
|
|
404
|
+
// is driven by room membership (subscribeDoc/subscribeList →
|
|
405
|
+
// ensureConnected): handlers registered before the first room join are
|
|
406
|
+
// attached by initHandlers() when that join connects. Connecting here
|
|
407
|
+
// meant merely CONSTRUCTING the sync engine (which registers its event
|
|
408
|
+
// handlers up front) booted a socket that nothing would ever close —
|
|
409
|
+
// against an unreachable host that is an infinite reconnect-poll loop
|
|
410
|
+
// surviving unmount (the fixture/mock-story socket leak).
|
|
208
411
|
this.attachSocketListener(event);
|
|
209
412
|
}
|
|
210
413
|
|
|
@@ -219,28 +422,61 @@ export class RealtimeModule {
|
|
|
219
422
|
}
|
|
220
423
|
|
|
221
424
|
async subscribeDoc(doctype: string, name: string): Promise<void> {
|
|
222
|
-
|
|
425
|
+
this.docRooms.set(docRoomKey(doctype, name), { doctype, name });
|
|
426
|
+
await this.ensureConnected();
|
|
223
427
|
if (this.socket && this.socketHealthy) {
|
|
224
|
-
|
|
428
|
+
// Positional args — frappe's handler signature is (doctype, docname).
|
|
429
|
+
this.socket.emit("doc_subscribe", doctype, name);
|
|
225
430
|
}
|
|
226
431
|
}
|
|
227
432
|
|
|
228
433
|
unsubscribeDoc(doctype: string, name: string): void {
|
|
434
|
+
this.docRooms.delete(docRoomKey(doctype, name));
|
|
229
435
|
if (this.socket && this.socketHealthy) {
|
|
230
|
-
this.socket.emit("doc_unsubscribe",
|
|
436
|
+
this.socket.emit("doc_unsubscribe", doctype, name);
|
|
231
437
|
}
|
|
438
|
+
this.teardownWhenIdle();
|
|
232
439
|
}
|
|
233
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Subscribe to list-level updates for a doctype (`list_update` events).
|
|
443
|
+
*
|
|
444
|
+
* `filters` are tracked client-side only: frappe's realtime server has a
|
|
445
|
+
* single room per doctype (`doctype_subscribe`) and does not support
|
|
446
|
+
* filtered list rooms.
|
|
447
|
+
*/
|
|
234
448
|
async subscribeList(doctype: string, filters?: Record<string, unknown>): Promise<void> {
|
|
235
|
-
|
|
449
|
+
this.listRooms.set(doctype, { doctype, filters });
|
|
450
|
+
await this.ensureConnected();
|
|
236
451
|
if (this.socket && this.socketHealthy) {
|
|
237
|
-
this.socket.emit("
|
|
452
|
+
this.socket.emit("doctype_subscribe", doctype);
|
|
238
453
|
}
|
|
239
454
|
}
|
|
455
|
+
|
|
240
456
|
unsubscribeList(doctype: string): void {
|
|
457
|
+
this.listRooms.delete(doctype);
|
|
241
458
|
if (this.socket && this.socketHealthy) {
|
|
242
|
-
this.socket.emit("
|
|
459
|
+
this.socket.emit("doctype_unsubscribe", doctype);
|
|
243
460
|
}
|
|
461
|
+
this.teardownWhenIdle();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Tears the socket down when the last room is released. Room membership is
|
|
466
|
+
* the socket's reason to exist: while any subscription holds a room, the
|
|
467
|
+
* infinite Socket.IO reconnection loop is the reconnect catch-up protocol
|
|
468
|
+
* working as designed — but once nothing is subscribed, that same loop is
|
|
469
|
+
* a leak (unmounted consumers leaving a dead-host socket reconnect-polling
|
|
470
|
+
* forever). Handlers and any re-added rooms survive: the next
|
|
471
|
+
* subscribeDoc/subscribeList reconnects and re-attaches everything.
|
|
472
|
+
*/
|
|
473
|
+
private teardownWhenIdle(): void {
|
|
474
|
+
if (this.docRooms.size > 0 || this.listRooms.size > 0) return;
|
|
475
|
+
if (!this.socket && !this.isConnecting) return;
|
|
476
|
+
// Surface the teardown to lifecycle listeners (connection indicators)
|
|
477
|
+
// before the listeners are detached from the socket.
|
|
478
|
+
this.emitToHandlers("disconnect", { reason: "idle-teardown" });
|
|
479
|
+
this.softDisconnect();
|
|
244
480
|
}
|
|
245
481
|
|
|
246
482
|
isHealthy() {
|
|
@@ -248,21 +484,23 @@ export class RealtimeModule {
|
|
|
248
484
|
}
|
|
249
485
|
|
|
250
486
|
disconnect(): void {
|
|
251
|
-
|
|
252
|
-
this.socket.disconnect();
|
|
253
|
-
this.socket = undefined;
|
|
254
|
-
}
|
|
487
|
+
this.softDisconnect();
|
|
255
488
|
this.handlerMap.clear();
|
|
256
|
-
this.
|
|
257
|
-
this.
|
|
258
|
-
this.socketHealthy = false;
|
|
259
|
-
this.isConnecting = false;
|
|
489
|
+
this.docRooms.clear();
|
|
490
|
+
this.listRooms.clear();
|
|
260
491
|
}
|
|
261
492
|
|
|
262
493
|
async updateAuth(auth: AuthConfig) {
|
|
263
494
|
this.config.auth = auth;
|
|
264
|
-
|
|
265
|
-
|
|
495
|
+
// Reconnect only when room subscriptions exist — rooms are what the
|
|
496
|
+
// socket serves. Handlers alone (registered eagerly by the sync engine)
|
|
497
|
+
// don't warrant a socket: an idle module stays idle across credential
|
|
498
|
+
// rotation, and the next room join connects with the fresh auth.
|
|
499
|
+
const shouldReconnect = this.docRooms.size > 0 || this.listRooms.size > 0;
|
|
500
|
+
// Soft disconnect preserves handlers + rooms; hard disconnect() would wipe them
|
|
501
|
+
// and make the post-check always false (previous production bug).
|
|
502
|
+
this.softDisconnect();
|
|
503
|
+
if (shouldReconnect) {
|
|
266
504
|
await this.connect();
|
|
267
505
|
}
|
|
268
506
|
}
|
|
@@ -532,6 +532,49 @@ describe("BackfillManager", () => {
|
|
|
532
532
|
expect(mockDb.getDocList).toHaveBeenCalled();
|
|
533
533
|
expect(subscription.testProgressiveCache.docIds.length).toBeGreaterThan(0);
|
|
534
534
|
});
|
|
535
|
+
|
|
536
|
+
const mockEndlessCdcPages = () => {
|
|
537
|
+
// Server always reports truncation with an advancing checkpoint, so
|
|
538
|
+
// only the page cap can stop the drain.
|
|
539
|
+
let page = 0;
|
|
540
|
+
(mockDb.backfill as any).mockImplementation(async () => {
|
|
541
|
+
page += 1;
|
|
542
|
+
return {
|
|
543
|
+
rows: [
|
|
544
|
+
{ name: `cdc-doc-${page}`, modified: new Date(2024, 0, 2, 0, 0, page).toISOString() },
|
|
545
|
+
],
|
|
546
|
+
deleted: [],
|
|
547
|
+
checkpoint: `2024-01-02 00:00:${String(page).padStart(2, "0")}.000000|cdc-doc-${page}`,
|
|
548
|
+
has_more: true,
|
|
549
|
+
};
|
|
550
|
+
});
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
it("should stop draining at the configured maxPages cap", async () => {
|
|
554
|
+
const cappedManager = new BackfillManager(mockDb, mockNormalizedCache, {
|
|
555
|
+
pageSize: 50,
|
|
556
|
+
maxPages: 2,
|
|
557
|
+
});
|
|
558
|
+
const subscription = createMockSubscription({
|
|
559
|
+
checkpoint: new Date(2024, 0, 1).toISOString(),
|
|
560
|
+
});
|
|
561
|
+
mockEndlessCdcPages();
|
|
562
|
+
|
|
563
|
+
await cappedManager.backfill(subscription, BackfillReason.Periodic);
|
|
564
|
+
|
|
565
|
+
expect(mockDb.backfill).toHaveBeenCalledTimes(2);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it("should default the drain cap to 20 pages when maxPages is not set", async () => {
|
|
569
|
+
const subscription = createMockSubscription({
|
|
570
|
+
checkpoint: new Date(2024, 0, 1).toISOString(),
|
|
571
|
+
});
|
|
572
|
+
mockEndlessCdcPages();
|
|
573
|
+
|
|
574
|
+
await backfillManager.backfill(subscription, BackfillReason.Periodic);
|
|
575
|
+
|
|
576
|
+
expect(mockDb.backfill).toHaveBeenCalledTimes(20);
|
|
577
|
+
});
|
|
535
578
|
});
|
|
536
579
|
|
|
537
580
|
describe("cursor-based fetching", () => {
|
package/src/sync/backfill.ts
CHANGED
|
@@ -63,17 +63,26 @@ export interface BackfillOptions {
|
|
|
63
63
|
pageSize?: number;
|
|
64
64
|
retryDelay?: number;
|
|
65
65
|
batchSize?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Max CDC pages drained per incremental backfill when the server reports
|
|
68
|
+
* has_more (default: 20). Fed from SyncOptions.backfillMaxPages so one
|
|
69
|
+
* knob bounds both drain paths.
|
|
70
|
+
*/
|
|
71
|
+
maxPages?: number;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
export class BackfillManager {
|
|
69
75
|
private activeBackfills = new Map<string, Promise<void>>();
|
|
70
76
|
private backfillQueue = new Map<string, BackfillReason[]>();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
private db: DbModule;
|
|
78
|
+
private normalizedCache: NormalizedCache;
|
|
79
|
+
private options: BackfillOptions;
|
|
80
|
+
|
|
81
|
+
constructor(db: DbModule, normalizedCache: NormalizedCache, options: BackfillOptions = {}) {
|
|
82
|
+
this.db = db;
|
|
83
|
+
this.normalizedCache = normalizedCache;
|
|
84
|
+
this.options = options;
|
|
85
|
+
}
|
|
77
86
|
|
|
78
87
|
async backfill(subscription: VirtualSubscription, reason: BackfillReason): Promise<void> {
|
|
79
88
|
const subscriptionId = subscription.id;
|
|
@@ -250,45 +259,56 @@ export class BackfillManager {
|
|
|
250
259
|
}
|
|
251
260
|
|
|
252
261
|
try {
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
// Drain CDC pages while the server reports truncation (bounded) so
|
|
263
|
+
// catch-up after a long gap is complete, not one page deep.
|
|
264
|
+
const maxPages = this.options.maxPages ?? 20;
|
|
265
|
+
for (let page = 0; page < maxPages; page++) {
|
|
266
|
+
const response = await this.db.backfill({
|
|
267
|
+
doctype: subscription.query.doctype,
|
|
268
|
+
fields: subscription.query.fields,
|
|
269
|
+
filters: subscription.query.filters,
|
|
270
|
+
modifiedAfter: subscription.checkpoint,
|
|
271
|
+
limit: this.options.pageSize || 200,
|
|
272
|
+
});
|
|
261
273
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
// Process deletions using shared utility
|
|
275
|
+
if ("deleted" in response && Array.isArray(response.deleted)) {
|
|
276
|
+
const deletedDocIds = (response.deleted as any[])
|
|
277
|
+
.filter((deletion) => deletion?.name)
|
|
278
|
+
.map((deletion) => deletion.name);
|
|
267
279
|
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
if (deletedDocIds.length > 0) {
|
|
281
|
+
subscription.removeDocuments(deletedDocIds, this.normalizedCache);
|
|
282
|
+
}
|
|
270
283
|
}
|
|
271
|
-
}
|
|
272
284
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
// Process updates and creates
|
|
286
|
+
for (const doc of response.rows) {
|
|
287
|
+
const exists = subscription.getProgressiveCache().docIds.includes(doc.name);
|
|
288
|
+
const matches = matchesFilters(
|
|
289
|
+
doc as FrappeDoc<FrappeDocData>,
|
|
290
|
+
subscription.query.filters,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
if (exists && !matches) {
|
|
294
|
+
// Remove - no longer matches
|
|
295
|
+
subscription.removeDocuments([doc.name], this.normalizedCache);
|
|
296
|
+
} else if (!exists && matches) {
|
|
297
|
+
// Add - now matches (but we don't know where)
|
|
298
|
+
subscription.needsRehydration = true;
|
|
299
|
+
} else if (exists) {
|
|
300
|
+
// Update existing
|
|
301
|
+
this.normalizedCache.upsert(doc);
|
|
302
|
+
}
|
|
287
303
|
}
|
|
288
|
-
}
|
|
289
304
|
|
|
290
|
-
|
|
291
|
-
|
|
305
|
+
// Advance the checkpoint: prefer the server's exact resume cursor
|
|
306
|
+
// ("ts|name"); fall back to the max modified for older servers.
|
|
307
|
+
subscription.checkpoint = response.checkpoint ?? calculateCheckpointFromDocs(response.rows);
|
|
308
|
+
|
|
309
|
+
const madeProgress = response.rows.length > 0 || (response.deleted?.length ?? 0) > 0;
|
|
310
|
+
if (!response.has_more || !response.checkpoint || !madeProgress) break;
|
|
311
|
+
}
|
|
292
312
|
} catch (error) {
|
|
293
313
|
console.error("Incremental backfill failed:", error);
|
|
294
314
|
// Fall back to full refresh
|