mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-26 06:34:54 +00:00
Compare commits
2 Commits
codex/add-
...
codex/chan
| Author | SHA1 | Date | |
|---|---|---|---|
| 418e3aa657 | |||
| cac24c0716 |
@@ -24,18 +24,22 @@ export class FlaskBackendAdapter implements StorageAdapter {
|
|||||||
|
|
||||||
clearTimeout(timeoutId)
|
clearTimeout(timeoutId)
|
||||||
|
|
||||||
const contentLength = response.headers.get('content-length')
|
|
||||||
const contentType = response.headers.get('content-type')
|
|
||||||
const hasJsonBody = contentLength !== '0' && contentType?.includes('application/json')
|
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorPayload = hasJsonBody ? await response.json().catch(() => null) : null
|
let errorMessage = response.statusText
|
||||||
const errorMessage = errorPayload?.error || response.statusText || `HTTP ${response.status}`
|
try {
|
||||||
throw new Error(errorMessage)
|
const errorText = await response.text()
|
||||||
}
|
if (errorText) {
|
||||||
|
try {
|
||||||
if (response.status === 204 || !hasJsonBody) {
|
const parsed = JSON.parse(errorText) as { error?: string }
|
||||||
return undefined as T
|
errorMessage = parsed.error || errorText
|
||||||
|
} catch {
|
||||||
|
errorMessage = errorText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore error parsing failures
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage || `HTTP ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseText = await response.text()
|
const responseText = await response.text()
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
||||||
import { AutoSyncManager } from '../autoSyncMiddleware'
|
|
||||||
import { syncToFlaskBulk } from '../../slices/syncSlice'
|
|
||||||
|
|
||||||
vi.mock('../../slices/syncSlice', () => ({
|
|
||||||
syncToFlaskBulk: vi.fn(() => ({ type: 'sync/syncToFlaskBulk' })),
|
|
||||||
checkFlaskConnection: vi.fn(() => ({ type: 'sync/checkConnection' })),
|
|
||||||
}))
|
|
||||||
|
|
||||||
type Deferred<T> = {
|
|
||||||
promise: Promise<T>
|
|
||||||
resolve: (value: T) => void
|
|
||||||
reject: (error?: unknown) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const createDeferred = <T,>(): Deferred<T> => {
|
|
||||||
let resolve!: (value: T) => void
|
|
||||||
let reject!: (error?: unknown) => void
|
|
||||||
const promise = new Promise<T>((res, rej) => {
|
|
||||||
resolve = res
|
|
||||||
reject = rej
|
|
||||||
})
|
|
||||||
|
|
||||||
return { promise, resolve, reject }
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('AutoSyncManager', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.clearAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('serializes syncs and runs one pending sync after completion', async () => {
|
|
||||||
const manager = new AutoSyncManager()
|
|
||||||
const deferreds = [createDeferred<void>(), createDeferred<void>()]
|
|
||||||
const dispatch = vi
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(() => deferreds.shift()?.promise ?? Promise.resolve())
|
|
||||||
|
|
||||||
manager.setDispatch(dispatch)
|
|
||||||
|
|
||||||
const firstSync = manager.syncNow()
|
|
||||||
const secondSync = manager.syncNow()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
|
||||||
|
|
||||||
deferreds[0].resolve()
|
|
||||||
await Promise.resolve()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
|
||||||
|
|
||||||
deferreds[1].resolve()
|
|
||||||
|
|
||||||
await firstSync
|
|
||||||
await secondSync
|
|
||||||
})
|
|
||||||
|
|
||||||
it('resets changeCounter after a successful sync', async () => {
|
|
||||||
const manager = new AutoSyncManager()
|
|
||||||
const dispatch = vi.fn().mockResolvedValue(undefined)
|
|
||||||
|
|
||||||
manager.setDispatch(dispatch)
|
|
||||||
manager.trackChange()
|
|
||||||
manager.trackChange()
|
|
||||||
|
|
||||||
expect(manager.getStatus().changeCounter).toBe(2)
|
|
||||||
|
|
||||||
await manager.syncNow()
|
|
||||||
|
|
||||||
expect(manager.getStatus().changeCounter).toBe(0)
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('coalesces multiple pending sync requests into one run', async () => {
|
|
||||||
const manager = new AutoSyncManager()
|
|
||||||
const deferreds = [createDeferred<void>(), createDeferred<void>()]
|
|
||||||
const dispatch = vi
|
|
||||||
.fn()
|
|
||||||
.mockImplementation(() => deferreds.shift()?.promise ?? Promise.resolve())
|
|
||||||
|
|
||||||
manager.setDispatch(dispatch)
|
|
||||||
|
|
||||||
const firstSync = manager.syncNow()
|
|
||||||
const secondSync = manager.syncNow()
|
|
||||||
const thirdSync = manager.syncNow()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
|
||||||
|
|
||||||
deferreds[0].resolve()
|
|
||||||
await Promise.resolve()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
|
||||||
|
|
||||||
deferreds[1].resolve()
|
|
||||||
|
|
||||||
await firstSync
|
|
||||||
await secondSync
|
|
||||||
await thirdSync
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('dispatches the sync thunk when performing a sync', async () => {
|
|
||||||
const manager = new AutoSyncManager()
|
|
||||||
const dispatch = vi.fn().mockResolvedValue(undefined)
|
|
||||||
|
|
||||||
manager.setDispatch(dispatch)
|
|
||||||
|
|
||||||
await manager.syncNow()
|
|
||||||
|
|
||||||
expect(dispatch).toHaveBeenCalledWith(syncToFlaskBulk())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -9,7 +9,7 @@ interface AutoSyncConfig {
|
|||||||
maxQueueSize: number
|
maxQueueSize: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AutoSyncManager {
|
class AutoSyncManager {
|
||||||
private config: AutoSyncConfig = {
|
private config: AutoSyncConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
intervalMs: 30000,
|
intervalMs: 30000,
|
||||||
@@ -21,8 +21,6 @@ export class AutoSyncManager {
|
|||||||
private lastSyncTime = 0
|
private lastSyncTime = 0
|
||||||
private changeCounter = 0
|
private changeCounter = 0
|
||||||
private dispatch: any = null
|
private dispatch: any = null
|
||||||
private syncInFlight: Promise<void> | null = null
|
|
||||||
private pendingSync = false
|
|
||||||
|
|
||||||
configure(config: Partial<AutoSyncConfig>) {
|
configure(config: Partial<AutoSyncConfig>) {
|
||||||
this.config = { ...this.config, ...config }
|
this.config = { ...this.config, ...config }
|
||||||
@@ -71,32 +69,12 @@ export class AutoSyncManager {
|
|||||||
private async performSync() {
|
private async performSync() {
|
||||||
if (!this.dispatch) return
|
if (!this.dispatch) return
|
||||||
|
|
||||||
if (this.syncInFlight) {
|
|
||||||
this.pendingSync = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncPromise = (async () => {
|
|
||||||
try {
|
|
||||||
await this.dispatch(syncToFlaskBulk())
|
|
||||||
this.lastSyncTime = Date.now()
|
|
||||||
this.changeCounter = 0
|
|
||||||
} catch (error) {
|
|
||||||
console.error('[AutoSync] Sync failed:', error)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
this.syncInFlight = syncPromise
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await syncPromise
|
await this.dispatch(syncToFlaskBulk())
|
||||||
} finally {
|
this.lastSyncTime = Date.now()
|
||||||
this.syncInFlight = null
|
this.changeCounter = 0
|
||||||
}
|
} catch (error) {
|
||||||
|
console.error('[AutoSync] Sync failed:', error)
|
||||||
if (this.pendingSync) {
|
|
||||||
this.pendingSync = false
|
|
||||||
await this.performSync()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user