@glance-apps/sync 1.1.0 → 1.1.2
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/CHANGELOG.md +26 -1
- package/package.json +1 -1
- package/src/engine.js +18 -16
- package/src/providers.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.2] - 2026-06-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **412 retry — local DB mutated before upload**: `applyPayload` was called before
|
|
15
|
+
`upload()` in `doCycle()`. If the upload returned a 412 Precondition Failed (concurrent
|
|
16
|
+
write from another device), the retry cycle re-merged already-dirtied local state with
|
|
17
|
+
the freshly downloaded remote, producing inconsistent data (e.g. completion events
|
|
18
|
+
referencing chore sync IDs that no longer exist after the second merge). `applyPayload`
|
|
19
|
+
is now deferred until after a successful upload (204), leaving the local DB untouched
|
|
20
|
+
if the upload fails so the retry starts from a clean state.
|
|
21
|
+
|
|
22
|
+
## [1.1.1] - 2026-06-03
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **WebDAV — 405 on MKCOL treated as directory-already-exists**: `mkcolWithParents`
|
|
27
|
+
now handles HTTP 405 (Method Not Allowed) the same as 409/404, triggering the
|
|
28
|
+
parent-directory creation fallback. Some WebDAV servers (e.g. certain nginx
|
|
29
|
+
configurations) return 405 rather than 409 when MKCOL is called on a path that
|
|
30
|
+
already exists as a collection.
|
|
31
|
+
|
|
10
32
|
## [1.1.0] - 2026-05-25
|
|
11
33
|
|
|
12
34
|
### Added
|
|
@@ -97,7 +119,10 @@ entire sync infrastructure can be shared across the GLANCE app family.
|
|
|
97
119
|
- TypeScript declarations in `types/index.d.ts`.
|
|
98
120
|
- WebDAV CORS proxy handler in `api/webdav-proxy.js`.
|
|
99
121
|
|
|
100
|
-
[Unreleased]: https://github.com/glance-apps/glance-sync/compare/v1.
|
|
122
|
+
[Unreleased]: https://github.com/glance-apps/glance-sync/compare/v1.1.2...HEAD
|
|
123
|
+
[1.1.2]: https://github.com/glance-apps/glance-sync/compare/v1.1.1...v1.1.2
|
|
124
|
+
[1.1.1]: https://github.com/glance-apps/glance-sync/compare/v1.1.0...v1.1.1
|
|
125
|
+
[1.1.0]: https://github.com/glance-apps/glance-sync/compare/v1.0.3...v1.1.0
|
|
101
126
|
[1.0.3]: https://github.com/glance-apps/glance-sync/compare/v1.0.2...v1.0.3
|
|
102
127
|
[1.0.2]: https://github.com/glance-apps/glance-sync/compare/v1.0.1...v1.0.2
|
|
103
128
|
[1.0.1]: https://github.com/glance-apps/glance-sync/compare/v1.0.0...v1.0.1
|
package/package.json
CHANGED
package/src/engine.js
CHANGED
|
@@ -346,34 +346,36 @@ export const createSyncEngine = (config) => {
|
|
|
346
346
|
const mergeResult = mergePayloads(localData, remote.data);
|
|
347
347
|
const { data: mergedData, localChanged, remoteChanged } = mergeResult;
|
|
348
348
|
|
|
349
|
-
if (localChanged) {
|
|
350
|
-
let mayApply = true;
|
|
351
|
-
if (validateApplyPayload) {
|
|
352
|
-
const check = await validateApplyPayload({ ...remote, data: mergedData });
|
|
353
|
-
if (!check?.valid) {
|
|
354
|
-
// eslint-disable-next-line no-console
|
|
355
|
-
console.error(`[${appId}] applyRemoteData aborted:`, check?.reason || 'validation failed');
|
|
356
|
-
mayApply = false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (mayApply) {
|
|
360
|
-
await applyPayload(mergedData, { allowEmpty: !!remote.lastModified });
|
|
361
|
-
localStorage.setItem(KEY_LOCAL_MOD, new Date().toISOString());
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
349
|
if (remoteChanged || localChanged) {
|
|
366
350
|
const mergedPayload = {
|
|
367
351
|
version: ENVELOPE_DATA_VERSION,
|
|
368
352
|
lastModified: new Date().toISOString(),
|
|
369
353
|
data: mergedData,
|
|
370
354
|
};
|
|
355
|
+
// Upload first — if this throws (e.g. 412 PRECONDITION_FAILED), the
|
|
356
|
+
// local DB must remain untouched so the retry starts from clean state.
|
|
371
357
|
await upload({
|
|
372
358
|
prebuiltPayload: mergedPayload,
|
|
373
359
|
skipLockCheck: true,
|
|
374
360
|
etag: etag || overrideEtag || null,
|
|
375
361
|
});
|
|
376
362
|
|
|
363
|
+
if (localChanged) {
|
|
364
|
+
let mayApply = true;
|
|
365
|
+
if (validateApplyPayload) {
|
|
366
|
+
const check = await validateApplyPayload({ ...remote, data: mergedData });
|
|
367
|
+
if (!check?.valid) {
|
|
368
|
+
// eslint-disable-next-line no-console
|
|
369
|
+
console.error(`[${appId}] applyRemoteData aborted:`, check?.reason || 'validation failed');
|
|
370
|
+
mayApply = false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (mayApply) {
|
|
374
|
+
await applyPayload(mergedData, { allowEmpty: !!remote.lastModified });
|
|
375
|
+
localStorage.setItem(KEY_LOCAL_MOD, new Date().toISOString());
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
377
379
|
if (hasNeverSynced && localChanged) {
|
|
378
380
|
onFirstSyncReload?.();
|
|
379
381
|
return true;
|
package/src/providers.js
CHANGED
|
@@ -102,7 +102,7 @@ async function parseDownloadResponse(res, engineConfig) {
|
|
|
102
102
|
|
|
103
103
|
async function mkcolWithParents(dirUrl, authHeaders, wf) {
|
|
104
104
|
const res = await wf('MKCOL', dirUrl, authHeaders);
|
|
105
|
-
if (res.status === 409 || res.status === 404) {
|
|
105
|
+
if (res.status === 409 || res.status === 404 || res.status === 405) {
|
|
106
106
|
// Derive parent by stripping the last path segment via URL() to avoid
|
|
107
107
|
// producing malformed strings on root-level inputs.
|
|
108
108
|
let parent;
|