anchordb 0.2.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/CHANGELOG.md +61 -0
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/cjs/conflict/strategies.d.ts +110 -0
- package/dist/cjs/conflict/strategies.d.ts.map +1 -0
- package/dist/cjs/conflict/strategies.js +298 -0
- package/dist/cjs/conflict/strategies.js.map +1 -0
- package/dist/cjs/conflict/types.d.ts +38 -0
- package/dist/cjs/conflict/types.d.ts.map +1 -0
- package/dist/cjs/conflict/types.js +3 -0
- package/dist/cjs/conflict/types.js.map +1 -0
- package/dist/cjs/core/anchor-db.d.ts +154 -0
- package/dist/cjs/core/anchor-db.d.ts.map +1 -0
- package/dist/cjs/core/anchor-db.js +273 -0
- package/dist/cjs/core/anchor-db.js.map +1 -0
- package/dist/cjs/core/errors.d.ts +125 -0
- package/dist/cjs/core/errors.d.ts.map +1 -0
- package/dist/cjs/core/errors.js +174 -0
- package/dist/cjs/core/errors.js.map +1 -0
- package/dist/cjs/core/hex.d.ts +4 -0
- package/dist/cjs/core/hex.d.ts.map +1 -0
- package/dist/cjs/core/hex.js +41 -0
- package/dist/cjs/core/hex.js.map +1 -0
- package/dist/cjs/core/hlc.d.ts +96 -0
- package/dist/cjs/core/hlc.d.ts.map +1 -0
- package/dist/cjs/core/hlc.js +161 -0
- package/dist/cjs/core/hlc.js.map +1 -0
- package/dist/cjs/core/object-id.d.ts +38 -0
- package/dist/cjs/core/object-id.d.ts.map +1 -0
- package/dist/cjs/core/object-id.js +167 -0
- package/dist/cjs/core/object-id.js.map +1 -0
- package/dist/cjs/core/random.d.ts +6 -0
- package/dist/cjs/core/random.d.ts.map +1 -0
- package/dist/cjs/core/random.js +39 -0
- package/dist/cjs/core/random.js.map +1 -0
- package/dist/cjs/core/runtime.d.ts +96 -0
- package/dist/cjs/core/runtime.d.ts.map +1 -0
- package/dist/cjs/core/runtime.js +3 -0
- package/dist/cjs/core/runtime.js.map +1 -0
- package/dist/cjs/core/uuid.d.ts +16 -0
- package/dist/cjs/core/uuid.d.ts.map +1 -0
- package/dist/cjs/core/uuid.js +37 -0
- package/dist/cjs/core/uuid.js.map +1 -0
- package/dist/cjs/decorators/index.d.ts +48 -0
- package/dist/cjs/decorators/index.d.ts.map +1 -0
- package/dist/cjs/decorators/index.js +295 -0
- package/dist/cjs/decorators/index.js.map +1 -0
- package/dist/cjs/events/emitter.d.ts +16 -0
- package/dist/cjs/events/emitter.d.ts.map +1 -0
- package/dist/cjs/events/emitter.js +56 -0
- package/dist/cjs/events/emitter.js.map +1 -0
- package/dist/cjs/index/keys.d.ts +79 -0
- package/dist/cjs/index/keys.d.ts.map +1 -0
- package/dist/cjs/index/keys.js +208 -0
- package/dist/cjs/index/keys.js.map +1 -0
- package/dist/cjs/index.d.ts +81 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +193 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/inspector/agent.d.ts +75 -0
- package/dist/cjs/inspector/agent.d.ts.map +1 -0
- package/dist/cjs/inspector/agent.js +520 -0
- package/dist/cjs/inspector/agent.js.map +1 -0
- package/dist/cjs/inspector/auth.d.ts +49 -0
- package/dist/cjs/inspector/auth.d.ts.map +1 -0
- package/dist/cjs/inspector/auth.js +234 -0
- package/dist/cjs/inspector/auth.js.map +1 -0
- package/dist/cjs/inspector/bridge.d.ts +141 -0
- package/dist/cjs/inspector/bridge.d.ts.map +1 -0
- package/dist/cjs/inspector/bridge.js +190 -0
- package/dist/cjs/inspector/bridge.js.map +1 -0
- package/dist/cjs/inspector/client.d.ts +111 -0
- package/dist/cjs/inspector/client.d.ts.map +1 -0
- package/dist/cjs/inspector/client.js +218 -0
- package/dist/cjs/inspector/client.js.map +1 -0
- package/dist/cjs/inspector/protocol.d.ts +160 -0
- package/dist/cjs/inspector/protocol.d.ts.map +1 -0
- package/dist/cjs/inspector/protocol.js +53 -0
- package/dist/cjs/inspector/protocol.js.map +1 -0
- package/dist/cjs/inspector/transports/in-process.d.ts +7 -0
- package/dist/cjs/inspector/transports/in-process.d.ts.map +1 -0
- package/dist/cjs/inspector/transports/in-process.js +58 -0
- package/dist/cjs/inspector/transports/in-process.js.map +1 -0
- package/dist/cjs/inspector/transports/websocket.d.ts +39 -0
- package/dist/cjs/inspector/transports/websocket.d.ts.map +1 -0
- package/dist/cjs/inspector/transports/websocket.js +60 -0
- package/dist/cjs/inspector/transports/websocket.js.map +1 -0
- package/dist/cjs/interop/csv.d.ts +53 -0
- package/dist/cjs/interop/csv.d.ts.map +1 -0
- package/dist/cjs/interop/csv.js +230 -0
- package/dist/cjs/interop/csv.js.map +1 -0
- package/dist/cjs/interop/ejson.d.ts +41 -0
- package/dist/cjs/interop/ejson.d.ts.map +1 -0
- package/dist/cjs/interop/ejson.js +182 -0
- package/dist/cjs/interop/ejson.js.map +1 -0
- package/dist/cjs/interop/export-import.d.ts +98 -0
- package/dist/cjs/interop/export-import.d.ts.map +1 -0
- package/dist/cjs/interop/export-import.js +222 -0
- package/dist/cjs/interop/export-import.js.map +1 -0
- package/dist/cjs/model/document.d.ts +66 -0
- package/dist/cjs/model/document.d.ts.map +1 -0
- package/dist/cjs/model/document.js +302 -0
- package/dist/cjs/model/document.js.map +1 -0
- package/dist/cjs/model/model.d.ts +178 -0
- package/dist/cjs/model/model.d.ts.map +1 -0
- package/dist/cjs/model/model.js +823 -0
- package/dist/cjs/model/model.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/query/aggregation/expressions.d.ts +34 -0
- package/dist/cjs/query/aggregation/expressions.d.ts.map +1 -0
- package/dist/cjs/query/aggregation/expressions.js +552 -0
- package/dist/cjs/query/aggregation/expressions.js.map +1 -0
- package/dist/cjs/query/aggregation/pipeline.d.ts +79 -0
- package/dist/cjs/query/aggregation/pipeline.d.ts.map +1 -0
- package/dist/cjs/query/aggregation/pipeline.js +597 -0
- package/dist/cjs/query/aggregation/pipeline.js.map +1 -0
- package/dist/cjs/query/aggregation/source.d.ts +35 -0
- package/dist/cjs/query/aggregation/source.d.ts.map +1 -0
- package/dist/cjs/query/aggregation/source.js +81 -0
- package/dist/cjs/query/aggregation/source.js.map +1 -0
- package/dist/cjs/query/builder/query.d.ts +113 -0
- package/dist/cjs/query/builder/query.d.ts.map +1 -0
- package/dist/cjs/query/builder/query.js +215 -0
- package/dist/cjs/query/builder/query.js.map +1 -0
- package/dist/cjs/query/filter.d.ts +50 -0
- package/dist/cjs/query/filter.d.ts.map +1 -0
- package/dist/cjs/query/filter.js +275 -0
- package/dist/cjs/query/filter.js.map +1 -0
- package/dist/cjs/query/path.d.ts +41 -0
- package/dist/cjs/query/path.d.ts.map +1 -0
- package/dist/cjs/query/path.js +294 -0
- package/dist/cjs/query/path.js.map +1 -0
- package/dist/cjs/query/update.d.ts +30 -0
- package/dist/cjs/query/update.d.ts.map +1 -0
- package/dist/cjs/query/update.js +205 -0
- package/dist/cjs/query/update.js.map +1 -0
- package/dist/cjs/reactive/live-query.d.ts +41 -0
- package/dist/cjs/reactive/live-query.d.ts.map +1 -0
- package/dist/cjs/reactive/live-query.js +88 -0
- package/dist/cjs/reactive/live-query.js.map +1 -0
- package/dist/cjs/schema/cast.d.ts +38 -0
- package/dist/cjs/schema/cast.d.ts.map +1 -0
- package/dist/cjs/schema/cast.js +280 -0
- package/dist/cjs/schema/cast.js.map +1 -0
- package/dist/cjs/schema/index.d.ts +5 -0
- package/dist/cjs/schema/index.d.ts.map +1 -0
- package/dist/cjs/schema/index.js +27 -0
- package/dist/cjs/schema/index.js.map +1 -0
- package/dist/cjs/schema/schema.d.ts +58 -0
- package/dist/cjs/schema/schema.d.ts.map +1 -0
- package/dist/cjs/schema/schema.js +363 -0
- package/dist/cjs/schema/schema.js.map +1 -0
- package/dist/cjs/schema/types.d.ts +144 -0
- package/dist/cjs/schema/types.d.ts.map +1 -0
- package/dist/cjs/schema/types.js +20 -0
- package/dist/cjs/schema/types.js.map +1 -0
- package/dist/cjs/schema/validate.d.ts +15 -0
- package/dist/cjs/schema/validate.d.ts.map +1 -0
- package/dist/cjs/schema/validate.js +101 -0
- package/dist/cjs/schema/validate.js.map +1 -0
- package/dist/cjs/storage/adapters/capacitor-sqlite.d.ts +67 -0
- package/dist/cjs/storage/adapters/capacitor-sqlite.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/capacitor-sqlite.js +69 -0
- package/dist/cjs/storage/adapters/capacitor-sqlite.js.map +1 -0
- package/dist/cjs/storage/adapters/expo-sqlite.d.ts +55 -0
- package/dist/cjs/storage/adapters/expo-sqlite.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/expo-sqlite.js +53 -0
- package/dist/cjs/storage/adapters/expo-sqlite.js.map +1 -0
- package/dist/cjs/storage/adapters/indexeddb.d.ts +55 -0
- package/dist/cjs/storage/adapters/indexeddb.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/indexeddb.js +393 -0
- package/dist/cjs/storage/adapters/indexeddb.js.map +1 -0
- package/dist/cjs/storage/adapters/memory.d.ts +44 -0
- package/dist/cjs/storage/adapters/memory.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/memory.js +286 -0
- package/dist/cjs/storage/adapters/memory.js.map +1 -0
- package/dist/cjs/storage/adapters/node-sqlite.d.ts +59 -0
- package/dist/cjs/storage/adapters/node-sqlite.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/node-sqlite.js +457 -0
- package/dist/cjs/storage/adapters/node-sqlite.js.map +1 -0
- package/dist/cjs/storage/adapters/sql-base.d.ts +84 -0
- package/dist/cjs/storage/adapters/sql-base.d.ts.map +1 -0
- package/dist/cjs/storage/adapters/sql-base.js +393 -0
- package/dist/cjs/storage/adapters/sql-base.js.map +1 -0
- package/dist/cjs/storage/types.d.ts +131 -0
- package/dist/cjs/storage/types.d.ts.map +1 -0
- package/dist/cjs/storage/types.js +3 -0
- package/dist/cjs/storage/types.js.map +1 -0
- package/dist/cjs/sync/adapters/rest.d.ts +31 -0
- package/dist/cjs/sync/adapters/rest.d.ts.map +1 -0
- package/dist/cjs/sync/adapters/rest.js +75 -0
- package/dist/cjs/sync/adapters/rest.js.map +1 -0
- package/dist/cjs/sync/attach.d.ts +30 -0
- package/dist/cjs/sync/attach.d.ts.map +1 -0
- package/dist/cjs/sync/attach.js +104 -0
- package/dist/cjs/sync/attach.js.map +1 -0
- package/dist/cjs/sync/engine.d.ts +96 -0
- package/dist/cjs/sync/engine.d.ts.map +1 -0
- package/dist/cjs/sync/engine.js +491 -0
- package/dist/cjs/sync/engine.js.map +1 -0
- package/dist/cjs/sync/protocol.d.ts +128 -0
- package/dist/cjs/sync/protocol.d.ts.map +1 -0
- package/dist/cjs/sync/protocol.js +38 -0
- package/dist/cjs/sync/protocol.js.map +1 -0
- package/dist/cjs/sync/queue.d.ts +77 -0
- package/dist/cjs/sync/queue.d.ts.map +1 -0
- package/dist/cjs/sync/queue.js +245 -0
- package/dist/cjs/sync/queue.js.map +1 -0
- package/dist/cjs/sync/server/memory-server.d.ts +65 -0
- package/dist/cjs/sync/server/memory-server.d.ts.map +1 -0
- package/dist/cjs/sync/server/memory-server.js +193 -0
- package/dist/cjs/sync/server/memory-server.js.map +1 -0
- package/dist/cjs/timestamps/index.d.ts +47 -0
- package/dist/cjs/timestamps/index.d.ts.map +1 -0
- package/dist/cjs/timestamps/index.js +88 -0
- package/dist/cjs/timestamps/index.js.map +1 -0
- package/dist/cjs/timestamps/timezone.d.ts +65 -0
- package/dist/cjs/timestamps/timezone.d.ts.map +1 -0
- package/dist/cjs/timestamps/timezone.js +208 -0
- package/dist/cjs/timestamps/timezone.js.map +1 -0
- package/dist/esm/conflict/strategies.d.ts +110 -0
- package/dist/esm/conflict/strategies.d.ts.map +1 -0
- package/dist/esm/conflict/strategies.js +289 -0
- package/dist/esm/conflict/strategies.js.map +1 -0
- package/dist/esm/conflict/types.d.ts +38 -0
- package/dist/esm/conflict/types.d.ts.map +1 -0
- package/dist/esm/conflict/types.js +2 -0
- package/dist/esm/conflict/types.js.map +1 -0
- package/dist/esm/core/anchor-db.d.ts +154 -0
- package/dist/esm/core/anchor-db.d.ts.map +1 -0
- package/dist/esm/core/anchor-db.js +269 -0
- package/dist/esm/core/anchor-db.js.map +1 -0
- package/dist/esm/core/errors.d.ts +125 -0
- package/dist/esm/core/errors.d.ts.map +1 -0
- package/dist/esm/core/errors.js +161 -0
- package/dist/esm/core/errors.js.map +1 -0
- package/dist/esm/core/hex.d.ts +4 -0
- package/dist/esm/core/hex.d.ts.map +1 -0
- package/dist/esm/core/hex.js +37 -0
- package/dist/esm/core/hex.js.map +1 -0
- package/dist/esm/core/hlc.d.ts +96 -0
- package/dist/esm/core/hlc.d.ts.map +1 -0
- package/dist/esm/core/hlc.js +153 -0
- package/dist/esm/core/hlc.js.map +1 -0
- package/dist/esm/core/object-id.d.ts +38 -0
- package/dist/esm/core/object-id.d.ts.map +1 -0
- package/dist/esm/core/object-id.js +163 -0
- package/dist/esm/core/object-id.js.map +1 -0
- package/dist/esm/core/random.d.ts +6 -0
- package/dist/esm/core/random.d.ts.map +1 -0
- package/dist/esm/core/random.js +34 -0
- package/dist/esm/core/random.js.map +1 -0
- package/dist/esm/core/runtime.d.ts +96 -0
- package/dist/esm/core/runtime.d.ts.map +1 -0
- package/dist/esm/core/runtime.js +2 -0
- package/dist/esm/core/runtime.js.map +1 -0
- package/dist/esm/core/uuid.d.ts +16 -0
- package/dist/esm/core/uuid.d.ts.map +1 -0
- package/dist/esm/core/uuid.js +33 -0
- package/dist/esm/core/uuid.js.map +1 -0
- package/dist/esm/decorators/index.d.ts +48 -0
- package/dist/esm/decorators/index.d.ts.map +1 -0
- package/dist/esm/decorators/index.js +286 -0
- package/dist/esm/decorators/index.js.map +1 -0
- package/dist/esm/events/emitter.d.ts +16 -0
- package/dist/esm/events/emitter.d.ts.map +1 -0
- package/dist/esm/events/emitter.js +52 -0
- package/dist/esm/events/emitter.js.map +1 -0
- package/dist/esm/index/keys.d.ts +79 -0
- package/dist/esm/index/keys.d.ts.map +1 -0
- package/dist/esm/index/keys.js +198 -0
- package/dist/esm/index/keys.js.map +1 -0
- package/dist/esm/index.d.ts +81 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +59 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/inspector/agent.d.ts +75 -0
- package/dist/esm/inspector/agent.d.ts.map +1 -0
- package/dist/esm/inspector/agent.js +482 -0
- package/dist/esm/inspector/agent.js.map +1 -0
- package/dist/esm/inspector/auth.d.ts +49 -0
- package/dist/esm/inspector/auth.d.ts.map +1 -0
- package/dist/esm/inspector/auth.js +228 -0
- package/dist/esm/inspector/auth.js.map +1 -0
- package/dist/esm/inspector/bridge.d.ts +141 -0
- package/dist/esm/inspector/bridge.d.ts.map +1 -0
- package/dist/esm/inspector/bridge.js +182 -0
- package/dist/esm/inspector/bridge.js.map +1 -0
- package/dist/esm/inspector/client.d.ts +111 -0
- package/dist/esm/inspector/client.d.ts.map +1 -0
- package/dist/esm/inspector/client.js +213 -0
- package/dist/esm/inspector/client.js.map +1 -0
- package/dist/esm/inspector/protocol.d.ts +160 -0
- package/dist/esm/inspector/protocol.d.ts.map +1 -0
- package/dist/esm/inspector/protocol.js +43 -0
- package/dist/esm/inspector/protocol.js.map +1 -0
- package/dist/esm/inspector/transports/in-process.d.ts +7 -0
- package/dist/esm/inspector/transports/in-process.d.ts.map +1 -0
- package/dist/esm/inspector/transports/in-process.js +55 -0
- package/dist/esm/inspector/transports/in-process.js.map +1 -0
- package/dist/esm/inspector/transports/websocket.d.ts +39 -0
- package/dist/esm/inspector/transports/websocket.d.ts.map +1 -0
- package/dist/esm/inspector/transports/websocket.js +56 -0
- package/dist/esm/inspector/transports/websocket.js.map +1 -0
- package/dist/esm/interop/csv.d.ts +53 -0
- package/dist/esm/interop/csv.d.ts.map +1 -0
- package/dist/esm/interop/csv.js +222 -0
- package/dist/esm/interop/csv.js.map +1 -0
- package/dist/esm/interop/ejson.d.ts +41 -0
- package/dist/esm/interop/ejson.d.ts.map +1 -0
- package/dist/esm/interop/ejson.js +176 -0
- package/dist/esm/interop/ejson.js.map +1 -0
- package/dist/esm/interop/export-import.d.ts +98 -0
- package/dist/esm/interop/export-import.d.ts.map +1 -0
- package/dist/esm/interop/export-import.js +210 -0
- package/dist/esm/interop/export-import.js.map +1 -0
- package/dist/esm/model/document.d.ts +66 -0
- package/dist/esm/model/document.d.ts.map +1 -0
- package/dist/esm/model/document.js +297 -0
- package/dist/esm/model/document.js.map +1 -0
- package/dist/esm/model/model.d.ts +178 -0
- package/dist/esm/model/model.d.ts.map +1 -0
- package/dist/esm/model/model.js +815 -0
- package/dist/esm/model/model.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/query/aggregation/expressions.d.ts +34 -0
- package/dist/esm/query/aggregation/expressions.d.ts.map +1 -0
- package/dist/esm/query/aggregation/expressions.js +545 -0
- package/dist/esm/query/aggregation/expressions.js.map +1 -0
- package/dist/esm/query/aggregation/pipeline.d.ts +79 -0
- package/dist/esm/query/aggregation/pipeline.d.ts.map +1 -0
- package/dist/esm/query/aggregation/pipeline.js +591 -0
- package/dist/esm/query/aggregation/pipeline.js.map +1 -0
- package/dist/esm/query/aggregation/source.d.ts +35 -0
- package/dist/esm/query/aggregation/source.d.ts.map +1 -0
- package/dist/esm/query/aggregation/source.js +77 -0
- package/dist/esm/query/aggregation/source.js.map +1 -0
- package/dist/esm/query/builder/query.d.ts +113 -0
- package/dist/esm/query/builder/query.d.ts.map +1 -0
- package/dist/esm/query/builder/query.js +209 -0
- package/dist/esm/query/builder/query.js.map +1 -0
- package/dist/esm/query/filter.d.ts +50 -0
- package/dist/esm/query/filter.d.ts.map +1 -0
- package/dist/esm/query/filter.js +267 -0
- package/dist/esm/query/filter.js.map +1 -0
- package/dist/esm/query/path.d.ts +41 -0
- package/dist/esm/query/path.d.ts.map +1 -0
- package/dist/esm/query/path.js +282 -0
- package/dist/esm/query/path.js.map +1 -0
- package/dist/esm/query/update.d.ts +30 -0
- package/dist/esm/query/update.d.ts.map +1 -0
- package/dist/esm/query/update.js +198 -0
- package/dist/esm/query/update.js.map +1 -0
- package/dist/esm/reactive/live-query.d.ts +41 -0
- package/dist/esm/reactive/live-query.d.ts.map +1 -0
- package/dist/esm/reactive/live-query.js +83 -0
- package/dist/esm/reactive/live-query.js.map +1 -0
- package/dist/esm/schema/cast.d.ts +38 -0
- package/dist/esm/schema/cast.d.ts.map +1 -0
- package/dist/esm/schema/cast.js +275 -0
- package/dist/esm/schema/cast.js.map +1 -0
- package/dist/esm/schema/index.d.ts +5 -0
- package/dist/esm/schema/index.d.ts.map +1 -0
- package/dist/esm/schema/index.js +5 -0
- package/dist/esm/schema/index.js.map +1 -0
- package/dist/esm/schema/schema.d.ts +58 -0
- package/dist/esm/schema/schema.d.ts.map +1 -0
- package/dist/esm/schema/schema.js +358 -0
- package/dist/esm/schema/schema.js.map +1 -0
- package/dist/esm/schema/types.d.ts +144 -0
- package/dist/esm/schema/types.d.ts.map +1 -0
- package/dist/esm/schema/types.js +17 -0
- package/dist/esm/schema/types.js.map +1 -0
- package/dist/esm/schema/validate.d.ts +15 -0
- package/dist/esm/schema/validate.d.ts.map +1 -0
- package/dist/esm/schema/validate.js +98 -0
- package/dist/esm/schema/validate.js.map +1 -0
- package/dist/esm/storage/adapters/capacitor-sqlite.d.ts +67 -0
- package/dist/esm/storage/adapters/capacitor-sqlite.d.ts.map +1 -0
- package/dist/esm/storage/adapters/capacitor-sqlite.js +65 -0
- package/dist/esm/storage/adapters/capacitor-sqlite.js.map +1 -0
- package/dist/esm/storage/adapters/expo-sqlite.d.ts +55 -0
- package/dist/esm/storage/adapters/expo-sqlite.d.ts.map +1 -0
- package/dist/esm/storage/adapters/expo-sqlite.js +49 -0
- package/dist/esm/storage/adapters/expo-sqlite.js.map +1 -0
- package/dist/esm/storage/adapters/indexeddb.d.ts +55 -0
- package/dist/esm/storage/adapters/indexeddb.d.ts.map +1 -0
- package/dist/esm/storage/adapters/indexeddb.js +389 -0
- package/dist/esm/storage/adapters/indexeddb.js.map +1 -0
- package/dist/esm/storage/adapters/memory.d.ts +44 -0
- package/dist/esm/storage/adapters/memory.d.ts.map +1 -0
- package/dist/esm/storage/adapters/memory.js +282 -0
- package/dist/esm/storage/adapters/memory.js.map +1 -0
- package/dist/esm/storage/adapters/node-sqlite.d.ts +59 -0
- package/dist/esm/storage/adapters/node-sqlite.d.ts.map +1 -0
- package/dist/esm/storage/adapters/node-sqlite.js +450 -0
- package/dist/esm/storage/adapters/node-sqlite.js.map +1 -0
- package/dist/esm/storage/adapters/sql-base.d.ts +84 -0
- package/dist/esm/storage/adapters/sql-base.d.ts.map +1 -0
- package/dist/esm/storage/adapters/sql-base.js +383 -0
- package/dist/esm/storage/adapters/sql-base.js.map +1 -0
- package/dist/esm/storage/types.d.ts +131 -0
- package/dist/esm/storage/types.d.ts.map +1 -0
- package/dist/esm/storage/types.js +2 -0
- package/dist/esm/storage/types.js.map +1 -0
- package/dist/esm/sync/adapters/rest.d.ts +31 -0
- package/dist/esm/sync/adapters/rest.d.ts.map +1 -0
- package/dist/esm/sync/adapters/rest.js +71 -0
- package/dist/esm/sync/adapters/rest.js.map +1 -0
- package/dist/esm/sync/attach.d.ts +30 -0
- package/dist/esm/sync/attach.d.ts.map +1 -0
- package/dist/esm/sync/attach.js +98 -0
- package/dist/esm/sync/attach.js.map +1 -0
- package/dist/esm/sync/engine.d.ts +96 -0
- package/dist/esm/sync/engine.d.ts.map +1 -0
- package/dist/esm/sync/engine.js +487 -0
- package/dist/esm/sync/engine.js.map +1 -0
- package/dist/esm/sync/protocol.d.ts +128 -0
- package/dist/esm/sync/protocol.d.ts.map +1 -0
- package/dist/esm/sync/protocol.js +33 -0
- package/dist/esm/sync/protocol.js.map +1 -0
- package/dist/esm/sync/queue.d.ts +77 -0
- package/dist/esm/sync/queue.d.ts.map +1 -0
- package/dist/esm/sync/queue.js +241 -0
- package/dist/esm/sync/queue.js.map +1 -0
- package/dist/esm/sync/server/memory-server.d.ts +65 -0
- package/dist/esm/sync/server/memory-server.d.ts.map +1 -0
- package/dist/esm/sync/server/memory-server.js +189 -0
- package/dist/esm/sync/server/memory-server.js.map +1 -0
- package/dist/esm/timestamps/index.d.ts +47 -0
- package/dist/esm/timestamps/index.d.ts.map +1 -0
- package/dist/esm/timestamps/index.js +70 -0
- package/dist/esm/timestamps/index.js.map +1 -0
- package/dist/esm/timestamps/timezone.d.ts +65 -0
- package/dist/esm/timestamps/timezone.d.ts.map +1 -0
- package/dist/esm/timestamps/timezone.js +196 -0
- package/dist/esm/timestamps/timezone.js.map +1 -0
- package/package.json +163 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `anchordb` packages.
|
|
4
|
+
|
|
5
|
+
This project follows [semantic versioning](https://semver.org/). While the version is `0.x`, minor
|
|
6
|
+
releases may contain breaking changes.
|
|
7
|
+
|
|
8
|
+
## 0.1.0 — unreleased
|
|
9
|
+
|
|
10
|
+
First release. Five packages, published together.
|
|
11
|
+
|
|
12
|
+
### `anchordb`
|
|
13
|
+
|
|
14
|
+
- **Local-first database** with a Mongoose-shaped API. No `sync` block means a purely local
|
|
15
|
+
database: no mutation queue, no network code, no sync metadata, hard deletes.
|
|
16
|
+
- **Mongoose parity**: `Schema`, `model()`, documents with `.save()`/`.isModified()`, virtuals,
|
|
17
|
+
methods, statics, `pre`/`post` hooks, `ValidationError`/`CastError` shapes, and `code: 11000`.
|
|
18
|
+
- **Decorators** (`anchordb/decorators`) matching `@nestjs/mongoose` — `@Schema`, `@Prop`,
|
|
19
|
+
`SchemaFactory`, `raw()` — so one decorated class can be shared with a NestJS backend.
|
|
20
|
+
Metadata is optional: explicit `@Prop(String)` works with `emitDecoratorMetadata` off.
|
|
21
|
+
- **Real MongoDB ObjectId**, generated on-device, cross-validated byte-for-byte against `bson`.
|
|
22
|
+
- **Indexes** — single, compound, multikey, `unique`, `sparse`, `partialFilterExpression`, TTL —
|
|
23
|
+
with unique violations enforced at the storage layer and reported as `E11000`.
|
|
24
|
+
- **Lazy chainable query builder**: `.populate().sort().skip().limit().select().exec()`, thenable,
|
|
25
|
+
async-iterable, with `explain()`.
|
|
26
|
+
- **Aggregation pipeline** with streaming execution, index pushdown, and a `mongoCompatible` strict
|
|
27
|
+
mode that rejects non-portable constructs at development time.
|
|
28
|
+
- **UTC-canonical timestamps** with IANA timezone support, validated at construction.
|
|
29
|
+
- **Extended JSON v2 interop** — `export()` feeds `mongoimport`; `mongoexport` output imports back.
|
|
30
|
+
- **Offline mutation queue** and sync engine with five conflict strategies
|
|
31
|
+
(`last-write-wins`, `server-wins`, `client-wins`, `field-level`, `custom`).
|
|
32
|
+
- **Inspector protocol v1** with pairing, sessions, and live change events.
|
|
33
|
+
- **Five storage adapters**: Memory, Node SQLite, IndexedDB, Expo SQLite, Capacitor SQLite.
|
|
34
|
+
|
|
35
|
+
### `anchordb-sync-server`
|
|
36
|
+
|
|
37
|
+
- Framework-agnostic protocol engine over a pluggable `ServerStore`.
|
|
38
|
+
- Express, NestJS and Next.js adapters, all held to one conformance suite.
|
|
39
|
+
- `MemoryStore` (default) and `MongooseStore`.
|
|
40
|
+
|
|
41
|
+
### `anchordb-react`
|
|
42
|
+
|
|
43
|
+
- `useFind`, `useFindOne`, `useCount`, `useAggregate`, `useSyncStatus`, `useAnchorEvent`.
|
|
44
|
+
|
|
45
|
+
### `anchordb-angular`
|
|
46
|
+
|
|
47
|
+
- `AnchorModule.forRoot`/`forFeature`, `getModelToken`, `AnchorService` with RxJS-compatible
|
|
48
|
+
observables. Angular and RxJS are optional peers and are never imported.
|
|
49
|
+
|
|
50
|
+
### `anchordb-relay`
|
|
51
|
+
|
|
52
|
+
- Development-only WebSocket relay plus the `anchor-relay` CLI.
|
|
53
|
+
|
|
54
|
+
### Known limitations
|
|
55
|
+
|
|
56
|
+
- **Expo SQLite and Capacitor SQLite are unverified** — written and typechecked, never executed on
|
|
57
|
+
a device. Test before production use.
|
|
58
|
+
- The REST sync transport and `MongooseStore` have no live-server tests.
|
|
59
|
+
- Not supported in v1: discriminators, `refPath`, transactions, server-side change streams,
|
|
60
|
+
text/geo/hashed indexes, collation, and the `$graphLookup`/`$geoNear`/`$search`/`$merge`/`$out`/
|
|
61
|
+
`$unionWith` aggregation stages.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AnchorDB contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# anchordb
|
|
2
|
+
|
|
3
|
+
**MongoDB + Mongoose, for mobile apps.** A local, persistent document database with a
|
|
4
|
+
Mongoose-shaped API that works completely offline — and *optionally* synchronises with a backend.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install anchordb
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Quick start — no backend required
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { AnchorDB, Schema } from "anchordb";
|
|
14
|
+
|
|
15
|
+
const db = new AnchorDB({ name: "my-app" }); // no sync block → purely local
|
|
16
|
+
|
|
17
|
+
const userSchema = new Schema({
|
|
18
|
+
name: { type: String, required: true, trim: true },
|
|
19
|
+
email: { type: String, required: true, unique: true, lowercase: true },
|
|
20
|
+
age: { type: Number, min: 0, max: 150 },
|
|
21
|
+
role: { type: String, enum: ["admin", "user"], default: "user" },
|
|
22
|
+
}, { timestamps: true });
|
|
23
|
+
|
|
24
|
+
const User = db.model("User", userSchema);
|
|
25
|
+
|
|
26
|
+
await User.create({ name: "Nisala", age: 28, email: "n@example.com" });
|
|
27
|
+
|
|
28
|
+
const adults = await User.find({ age: { $gte: 18 } })
|
|
29
|
+
.sort("-createdAt")
|
|
30
|
+
.limit(10);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That is Mongoose code. It runs on a phone, offline, with no server anywhere.
|
|
34
|
+
|
|
35
|
+
## Two principles
|
|
36
|
+
|
|
37
|
+
**1. Zero re-learning.** If you know MongoDB and Mongoose on the backend, you already know this:
|
|
38
|
+
the same `Schema`, `model()`, query builder, `populate`, `ObjectId`, index options and error codes
|
|
39
|
+
(`err.code === 11000`, `err.errors[path].kind`). Anything Mongoose does that AnchorDB does not is
|
|
40
|
+
written down, never silently reinterpreted.
|
|
41
|
+
|
|
42
|
+
**2. Sync is optional.** An app with no backend is the default case, not a degraded one. With no
|
|
43
|
+
`sync` block there is no mutation queue, no network code, no sync metadata and no tombstones.
|
|
44
|
+
Adding sync later requires no changes to your CRUD code.
|
|
45
|
+
|
|
46
|
+
## Decorators (optional)
|
|
47
|
+
|
|
48
|
+
Identical in shape to `@nestjs/mongoose`, so the same decorated class can be shared between an
|
|
49
|
+
Ionic/Angular app and a NestJS backend:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { Schema, Prop, SchemaFactory } from "anchordb/decorators";
|
|
53
|
+
|
|
54
|
+
@Schema({ timestamps: true })
|
|
55
|
+
export class User {
|
|
56
|
+
@Prop({ required: true }) name!: string;
|
|
57
|
+
@Prop(String) nickname!: string;
|
|
58
|
+
@Prop([String]) tags!: string[];
|
|
59
|
+
}
|
|
60
|
+
export const UserSchema = SchemaFactory.createForClass(User);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Requires `experimentalDecorators` + `emitDecoratorMetadata` for type inference — or pass the type
|
|
64
|
+
explicitly (`@Prop(String)`) and no metadata is needed at all.
|
|
65
|
+
|
|
66
|
+
## Storage adapters
|
|
67
|
+
|
|
68
|
+
| Adapter | Import | Platform | Verified here |
|
|
69
|
+
| --- | --- | --- | --- |
|
|
70
|
+
| Memory | `MemoryAdapter` from `anchordb` | any | yes — conformance suite |
|
|
71
|
+
| Node SQLite | `anchordb/storage/node-sqlite` | Node 22.5+ (built in, no native deps) | yes — conformance suite |
|
|
72
|
+
| IndexedDB | `anchordb/storage/indexeddb` | browser, PWA, `ionic serve` | yes — conformance suite |
|
|
73
|
+
| Expo SQLite | `anchordb/storage/expo-sqlite` | React Native, iOS/Android | **no — see below** |
|
|
74
|
+
| Capacitor SQLite | `anchordb/storage/capacitor-sqlite` | Ionic native | **no — see below** |
|
|
75
|
+
|
|
76
|
+
All adapters sit behind one `DatabaseAdapter` interface, and the three verified ones are held to a
|
|
77
|
+
single 70-test conformance suite, so unique-index semantics, null handling and write atomicity
|
|
78
|
+
cannot drift between them.
|
|
79
|
+
|
|
80
|
+
## Interchange with MongoDB
|
|
81
|
+
|
|
82
|
+
Export and import speak what `mongoexport` writes and `mongoimport` reads, in both directions and
|
|
83
|
+
in both formats:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
// Extended JSON — lossless. NDJSON by default, which mongoimport reads with no extra flag.
|
|
87
|
+
const file = await Model.export({ mode: "canonical" });
|
|
88
|
+
|
|
89
|
+
// Only what a filter matches, run as a real query so it is cast and indexed like any other.
|
|
90
|
+
const page = await Model.export({ filter: { status: "active" } });
|
|
91
|
+
|
|
92
|
+
// CSV — for spreadsheets, and for mongoimport --type=csv --headerline.
|
|
93
|
+
const csv = await Model.export({ format: "csv" });
|
|
94
|
+
|
|
95
|
+
// Import auto-detects a JSON array, NDJSON or CSV.
|
|
96
|
+
await db.import(text, { mode: "upsert" });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`mongoimportCommand()` prints the exact command that ingests the result.
|
|
100
|
+
|
|
101
|
+
CSV carries no types, so on import values are inferred conservatively and then **cast through the
|
|
102
|
+
collection's schema** — a declared `Number` comes back a number rather than the text the file held.
|
|
103
|
+
Two things CSV genuinely cannot round-trip, stated rather than hidden: `null` and a missing field
|
|
104
|
+
are both an empty cell (read back as missing, matching `mongoimport --ignoreBlanks`), and nested
|
|
105
|
+
fields travel as dotted paths.
|
|
106
|
+
|
|
107
|
+
Extended JSON has three modes, matching the ones MongoDB Compass offers: `canonical` (lossless),
|
|
108
|
+
`relaxed` (readable), and `default` (relaxed, except integers past 2^53 which are marked
|
|
109
|
+
`$numberLong` so they do not silently become doubles).
|
|
110
|
+
|
|
111
|
+
## Status
|
|
112
|
+
|
|
113
|
+
**0.2.0 — early, and 0.x means breaking changes are expected.**
|
|
114
|
+
|
|
115
|
+
**631 tests across 17 files.** Implemented and covered: the local database, Mongoose-shaped schema
|
|
116
|
+
and validation, decorators, indexes with real `E11000` enforcement, the lazy query builder,
|
|
117
|
+
`populate`, documents with `.save()`, aggregation, UTC timestamps and timezones, MongoDB Extended
|
|
118
|
+
JSON and CSV interop, the offline mutation queue, the sync engine with all five conflict strategies,
|
|
119
|
+
and the inspector protocol.
|
|
120
|
+
|
|
121
|
+
### New in 0.2.0
|
|
122
|
+
|
|
123
|
+
- **`updateMany` and `deleteMany` on the inspector protocol**, behind a new `documents:bulk`
|
|
124
|
+
capability. Both route through the ordinary Model API, so a bulk write from Anchor Lens validates,
|
|
125
|
+
stamps and queues exactly like an app write. An empty filter is refused unless explicitly
|
|
126
|
+
acknowledged — an inspector that can empty a collection on a mis-tap is worse than one that asks
|
|
127
|
+
twice. The capability is negotiated, so an older agent and a newer Lens still connect.
|
|
128
|
+
- **CSV export and import**, and a `filter` on export.
|
|
129
|
+
- **A MongoDB bridge protocol** (`MongoBridgeClient`), so Anchor Lens can pull collections from a
|
|
130
|
+
real MongoDB or push a device into one. The driver runs in `anchordb-relay` — React
|
|
131
|
+
Native has no TCP sockets — and both directions carry the same Extended JSON as the file export.
|
|
132
|
+
- **`default` Extended JSON mode.**
|
|
133
|
+
- **A no-op update is now matched but not modified**, as in MongoDB: `modifiedCount` excludes
|
|
134
|
+
documents the update did not change, and their `updatedAt` and version key are left alone. It
|
|
135
|
+
previously counted every match, because the timestamp was stamped before the comparison. Beyond
|
|
136
|
+
correctness this matters for bulk writes — setting a field over 10,000 documents no longer queues
|
|
137
|
+
10,000 mutations when only a few hundred needed changing.
|
|
138
|
+
|
|
139
|
+
### What is NOT verified
|
|
140
|
+
|
|
141
|
+
Stated plainly rather than left to be discovered:
|
|
142
|
+
|
|
143
|
+
- **Expo SQLite and Capacitor SQLite have never been executed.** They are written against the
|
|
144
|
+
documented APIs and typechecked, but this package is developed on a machine with no simulator or
|
|
145
|
+
device. All of their query, index and unique-enforcement logic is inherited from a shared base
|
|
146
|
+
that *is* covered by the conformance suite; the unverified surface is roughly forty lines of
|
|
147
|
+
driver plumbing each. **Test on a device before relying on them in production.**
|
|
148
|
+
- **The REST sync transport** is typechecked but has no live-HTTP test. The protocol it speaks is
|
|
149
|
+
covered end to end through an in-process transport.
|
|
150
|
+
- **`MongooseStore`** in `anchordb-sync-server` has not run against a live MongoDB here.
|
|
151
|
+
|
|
152
|
+
### Not supported in v1
|
|
153
|
+
|
|
154
|
+
Documented rather than silently absent: discriminators, `refPath`, transactions/sessions,
|
|
155
|
+
server-side change streams, text/`2dsphere`/hashed indexes, collation, and the
|
|
156
|
+
`$graphLookup`/`$geoNear`/`$search`/`$merge`/`$out`/`$unionWith` aggregation stages.
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ConflictContext, ConflictResolutionConfig, ConflictStrategyName, Resolution } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* The five conflict strategies, behind one interface.
|
|
4
|
+
*
|
|
5
|
+
* The sync engine knows only `resolve(context) -> Resolution`. No strategy-specific branching leaks
|
|
6
|
+
* into the engine, which is what makes switching strategy a pure configuration change (§19) and
|
|
7
|
+
* makes a sixth strategy a matter of registering one more object.
|
|
8
|
+
*/
|
|
9
|
+
export interface ConflictStrategyHandler<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
10
|
+
readonly name: ConflictStrategyName | string;
|
|
11
|
+
resolve(context: ConflictContext<T>): Promise<Resolution<T>> | Resolution<T>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Last write wins — the default.
|
|
15
|
+
*
|
|
16
|
+
* "Latest" is decided by the hybrid logical clock, never by raw device wall-clock strings. The
|
|
17
|
+
* comparison order is documented in SYNC_PROTOCOL.md:
|
|
18
|
+
*
|
|
19
|
+
* 1. HLC physical time (already skew-corrected against the server)
|
|
20
|
+
* 2. HLC logical counter, for writes inside the same millisecond
|
|
21
|
+
* 3. clientId, lexicographically
|
|
22
|
+
*
|
|
23
|
+
* Step 3 is what makes this a *total* order. Without it two devices could produce genuinely equal
|
|
24
|
+
* stamps and the winner would depend on evaluation order — different devices would converge to
|
|
25
|
+
* different documents, which is precisely the failure LWW is supposed to prevent.
|
|
26
|
+
*
|
|
27
|
+
* On an exact tie the server wins, because the server's copy is the one everyone else has already
|
|
28
|
+
* observed; preferring the client would make convergence depend on who pushed last.
|
|
29
|
+
*/
|
|
30
|
+
export declare class LastWriteWinsStrategy<T extends Record<string, unknown>> implements ConflictStrategyHandler<T> {
|
|
31
|
+
readonly name = "last-write-wins";
|
|
32
|
+
resolve(ctx: ConflictContext<T>): Resolution<T>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Server wins — the local change is discarded and the server's document adopted.
|
|
36
|
+
*
|
|
37
|
+
* `pushBack: false` matters: re-pushing a document the server just gave us would create an endless
|
|
38
|
+
* echo between the two.
|
|
39
|
+
*/
|
|
40
|
+
export declare class ServerWinsStrategy<T extends Record<string, unknown>> implements ConflictStrategyHandler<T> {
|
|
41
|
+
readonly name = "server-wins";
|
|
42
|
+
resolve(ctx: ConflictContext<T>): Resolution<T>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Client wins — the local change overwrites the server's.
|
|
46
|
+
*
|
|
47
|
+
* The loop hazard is real and is handled by the engine rather than here: the re-push carries
|
|
48
|
+
* `baseVersion = remoteVersion` (the version we just observed), so the server's optimistic check
|
|
49
|
+
* now passes instead of conflicting again forever. The engine also caps `conflictAttempts`.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ClientWinsStrategy<T extends Record<string, unknown>> implements ConflictStrategyHandler<T> {
|
|
52
|
+
readonly name = "client-wins";
|
|
53
|
+
resolve(ctx: ConflictContext<T>): Resolution<T>;
|
|
54
|
+
}
|
|
55
|
+
declare const DELETED: unique symbol;
|
|
56
|
+
/**
|
|
57
|
+
* Field-level three-way merge.
|
|
58
|
+
*
|
|
59
|
+
* Compares local and remote against their common ancestor (`base`, the last server-confirmed
|
|
60
|
+
* document kept in the shadow table). Paths only one side touched merge silently; paths both sides
|
|
61
|
+
* touched are a genuine conflict and fall to `fieldPolicy`.
|
|
62
|
+
*
|
|
63
|
+
* Documented edge cases, each with a test:
|
|
64
|
+
*
|
|
65
|
+
* - **No base.** Without an ancestor we cannot tell an edit from an insert, so a merge would be a
|
|
66
|
+
* guess. Falls back to last-write-wins rather than inventing a result.
|
|
67
|
+
* - **Arrays are atomic by default.** Element-wise merging of two concurrently reordered arrays
|
|
68
|
+
* has no correct answer in general. `arrayStrategy: "union-by-id"` opts into merging arrays of
|
|
69
|
+
* objects that carry `_id`/`id`.
|
|
70
|
+
* - **Deleted fields.** "Deleted on one side" is distinguished from "absent on both" by diffing
|
|
71
|
+
* against base — otherwise a deletion would be silently undone by the other side's copy.
|
|
72
|
+
* - **Type changes** (string -> object) are treated as an ordinary same-path conflict.
|
|
73
|
+
* - **Object replacement.** An edit at `a` conflicts with an edit at `a.b`: parent/child overlap is
|
|
74
|
+
* detected explicitly, because treating them as unrelated paths would resurrect a replaced object.
|
|
75
|
+
*/
|
|
76
|
+
export declare class FieldLevelStrategy<T extends Record<string, unknown>> implements ConflictStrategyHandler<T> {
|
|
77
|
+
readonly name = "field-level";
|
|
78
|
+
private readonly policy;
|
|
79
|
+
private readonly arrayStrategy;
|
|
80
|
+
private readonly fallback;
|
|
81
|
+
constructor(config?: ConflictResolutionConfig<T>);
|
|
82
|
+
resolve(ctx: ConflictContext<T>): Resolution<T>;
|
|
83
|
+
private decide;
|
|
84
|
+
/** Opt-in array merging for arrays of objects with a stable identity. */
|
|
85
|
+
private mergeArraysById;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Custom resolver.
|
|
89
|
+
*
|
|
90
|
+
* The developer's function may return either a bare document (treated as the resolved value) or a
|
|
91
|
+
* full `Resolution` when it needs to control `pushBack`. A throwing resolver is *not* caught here —
|
|
92
|
+
* the engine parks the mutation and keeps draining the queue, so one bad resolver cannot wedge sync
|
|
93
|
+
* for every other document.
|
|
94
|
+
*/
|
|
95
|
+
export declare class CustomStrategy<T extends Record<string, unknown>> implements ConflictStrategyHandler<T> {
|
|
96
|
+
readonly name = "custom";
|
|
97
|
+
private readonly resolver;
|
|
98
|
+
constructor(config: ConflictResolutionConfig<T>);
|
|
99
|
+
resolve(ctx: ConflictContext<T>): Promise<Resolution<T>>;
|
|
100
|
+
}
|
|
101
|
+
/** Registry — open for user-defined strategies (§11). */
|
|
102
|
+
export declare class ConflictStrategyRegistry<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
103
|
+
private readonly strategies;
|
|
104
|
+
constructor(config?: ConflictResolutionConfig<T>);
|
|
105
|
+
register(strategy: ConflictStrategyHandler<T>): void;
|
|
106
|
+
get(name: string): ConflictStrategyHandler<T>;
|
|
107
|
+
names(): string[];
|
|
108
|
+
}
|
|
109
|
+
export { DELETED as FIELD_DELETED };
|
|
110
|
+
//# sourceMappingURL=strategies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.d.ts","sourceRoot":"","sources":["../../../src/conflict/strategies.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,wBAAwB,EACxB,oBAAoB,EAEpB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClG,QAAQ,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAAC;IAC7C,OAAO,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;CAC9E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,uBAAuB,CAAC,CAAC,CAAC;IACzG,QAAQ,CAAC,IAAI,qBAAqB;IAElC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;CAKhD;AAED;;;;;GAKG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,uBAAuB,CAAC,CAAC,CAAC;IACtG,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;CAGhD;AAED;;;;;;GAMG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,uBAAuB,CAAC,CAAC,CAAC;IACtG,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;CAGhD;AAED,QAAA,MAAM,OAAO,eAAiC,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,uBAAuB,CAAC,CAAC,CAAC;IACtG,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6C;IACpE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;gBAE/C,MAAM,GAAE,wBAAwB,CAAC,CAAC,CAAM;IAKpD,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAsD/C,OAAO,CAAC,MAAM;IAMd,yEAAyE;IACzE,OAAO,CAAC,eAAe;CA+BxB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,YAAW,uBAAuB,CAAC,CAAC,CAAC;IAClG,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuD;gBAEpE,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAUzC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;CAc/D;AAED,yDAAyD;AACzD,qBAAa,wBAAwB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiD;gBAEhE,MAAM,GAAE,wBAAwB,CAAC,CAAC,CAAM;IAUpD,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,IAAI;IAIpD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,uBAAuB,CAAC,CAAC,CAAC;IAU7C,KAAK,IAAI,MAAM,EAAE;CAGlB;AAmED,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIELD_DELETED = exports.ConflictStrategyRegistry = exports.CustomStrategy = exports.FieldLevelStrategy = exports.ClientWinsStrategy = exports.ServerWinsStrategy = exports.LastWriteWinsStrategy = void 0;
|
|
4
|
+
const hlc_ts_1 = require("../core/hlc.js");
|
|
5
|
+
const path_ts_1 = require("../query/path.js");
|
|
6
|
+
/**
|
|
7
|
+
* Last write wins — the default.
|
|
8
|
+
*
|
|
9
|
+
* "Latest" is decided by the hybrid logical clock, never by raw device wall-clock strings. The
|
|
10
|
+
* comparison order is documented in SYNC_PROTOCOL.md:
|
|
11
|
+
*
|
|
12
|
+
* 1. HLC physical time (already skew-corrected against the server)
|
|
13
|
+
* 2. HLC logical counter, for writes inside the same millisecond
|
|
14
|
+
* 3. clientId, lexicographically
|
|
15
|
+
*
|
|
16
|
+
* Step 3 is what makes this a *total* order. Without it two devices could produce genuinely equal
|
|
17
|
+
* stamps and the winner would depend on evaluation order — different devices would converge to
|
|
18
|
+
* different documents, which is precisely the failure LWW is supposed to prevent.
|
|
19
|
+
*
|
|
20
|
+
* On an exact tie the server wins, because the server's copy is the one everyone else has already
|
|
21
|
+
* observed; preferring the client would make convergence depend on who pushed last.
|
|
22
|
+
*/
|
|
23
|
+
class LastWriteWinsStrategy {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.name = "last-write-wins";
|
|
26
|
+
}
|
|
27
|
+
resolve(ctx) {
|
|
28
|
+
const cmp = (0, hlc_ts_1.compareHlcStamps)(ctx.localHlc, ctx.remoteHlc);
|
|
29
|
+
if (cmp > 0)
|
|
30
|
+
return { winner: "local", document: ctx.local, pushBack: true };
|
|
31
|
+
return { winner: "remote", document: ctx.remote, pushBack: false };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.LastWriteWinsStrategy = LastWriteWinsStrategy;
|
|
35
|
+
/**
|
|
36
|
+
* Server wins — the local change is discarded and the server's document adopted.
|
|
37
|
+
*
|
|
38
|
+
* `pushBack: false` matters: re-pushing a document the server just gave us would create an endless
|
|
39
|
+
* echo between the two.
|
|
40
|
+
*/
|
|
41
|
+
class ServerWinsStrategy {
|
|
42
|
+
constructor() {
|
|
43
|
+
this.name = "server-wins";
|
|
44
|
+
}
|
|
45
|
+
resolve(ctx) {
|
|
46
|
+
return { winner: "remote", document: ctx.remote, pushBack: false };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.ServerWinsStrategy = ServerWinsStrategy;
|
|
50
|
+
/**
|
|
51
|
+
* Client wins — the local change overwrites the server's.
|
|
52
|
+
*
|
|
53
|
+
* The loop hazard is real and is handled by the engine rather than here: the re-push carries
|
|
54
|
+
* `baseVersion = remoteVersion` (the version we just observed), so the server's optimistic check
|
|
55
|
+
* now passes instead of conflicting again forever. The engine also caps `conflictAttempts`.
|
|
56
|
+
*/
|
|
57
|
+
class ClientWinsStrategy {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.name = "client-wins";
|
|
60
|
+
}
|
|
61
|
+
resolve(ctx) {
|
|
62
|
+
return { winner: "local", document: ctx.local, pushBack: true };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.ClientWinsStrategy = ClientWinsStrategy;
|
|
66
|
+
const DELETED = Symbol("anchor.field.deleted");
|
|
67
|
+
exports.FIELD_DELETED = DELETED;
|
|
68
|
+
/**
|
|
69
|
+
* Field-level three-way merge.
|
|
70
|
+
*
|
|
71
|
+
* Compares local and remote against their common ancestor (`base`, the last server-confirmed
|
|
72
|
+
* document kept in the shadow table). Paths only one side touched merge silently; paths both sides
|
|
73
|
+
* touched are a genuine conflict and fall to `fieldPolicy`.
|
|
74
|
+
*
|
|
75
|
+
* Documented edge cases, each with a test:
|
|
76
|
+
*
|
|
77
|
+
* - **No base.** Without an ancestor we cannot tell an edit from an insert, so a merge would be a
|
|
78
|
+
* guess. Falls back to last-write-wins rather than inventing a result.
|
|
79
|
+
* - **Arrays are atomic by default.** Element-wise merging of two concurrently reordered arrays
|
|
80
|
+
* has no correct answer in general. `arrayStrategy: "union-by-id"` opts into merging arrays of
|
|
81
|
+
* objects that carry `_id`/`id`.
|
|
82
|
+
* - **Deleted fields.** "Deleted on one side" is distinguished from "absent on both" by diffing
|
|
83
|
+
* against base — otherwise a deletion would be silently undone by the other side's copy.
|
|
84
|
+
* - **Type changes** (string -> object) are treated as an ordinary same-path conflict.
|
|
85
|
+
* - **Object replacement.** An edit at `a` conflicts with an edit at `a.b`: parent/child overlap is
|
|
86
|
+
* detected explicitly, because treating them as unrelated paths would resurrect a replaced object.
|
|
87
|
+
*/
|
|
88
|
+
class FieldLevelStrategy {
|
|
89
|
+
constructor(config = {}) {
|
|
90
|
+
this.name = "field-level";
|
|
91
|
+
this.fallback = new LastWriteWinsStrategy();
|
|
92
|
+
this.policy = config.fieldPolicy ?? "lww";
|
|
93
|
+
this.arrayStrategy = config.arrayStrategy ?? "atomic";
|
|
94
|
+
}
|
|
95
|
+
resolve(ctx) {
|
|
96
|
+
if (!ctx.base) {
|
|
97
|
+
// Honest degradation: a "merge" without an ancestor is a guess dressed up as a decision.
|
|
98
|
+
return this.fallback.resolve(ctx);
|
|
99
|
+
}
|
|
100
|
+
const base = ctx.base;
|
|
101
|
+
const local = ctx.local;
|
|
102
|
+
const remote = ctx.remote;
|
|
103
|
+
const localChanges = diff(base, local);
|
|
104
|
+
const remoteChanges = diff(base, remote);
|
|
105
|
+
const merged = (0, path_ts_1.deepClone)(remote);
|
|
106
|
+
const conflicts = [];
|
|
107
|
+
let tookLocal = false;
|
|
108
|
+
for (const [path, localValue] of localChanges) {
|
|
109
|
+
const remoteValue = remoteChanges.get(path);
|
|
110
|
+
const overlaps = remoteChanges.has(path) || hasAncestorOrDescendant(path, remoteChanges);
|
|
111
|
+
if (!overlaps) {
|
|
112
|
+
// Only the client touched this path — apply it on top of the server document.
|
|
113
|
+
applyChange(merged, path, localValue);
|
|
114
|
+
tookLocal = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (remoteChanges.has(path) && (0, path_ts_1.valuesEqual)(localValue, remoteValue)) {
|
|
118
|
+
continue; // both sides made the same edit; nothing to decide
|
|
119
|
+
}
|
|
120
|
+
conflicts.push(path);
|
|
121
|
+
const decision = this.decide(path, ctx);
|
|
122
|
+
if (decision === "client") {
|
|
123
|
+
applyChange(merged, path, localValue);
|
|
124
|
+
tookLocal = true;
|
|
125
|
+
}
|
|
126
|
+
// "server" needs no action: `merged` started as the remote document.
|
|
127
|
+
}
|
|
128
|
+
if (this.arrayStrategy === "union-by-id") {
|
|
129
|
+
this.mergeArraysById(base, local, remote, merged);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
winner: tookLocal ? "merged" : "remote",
|
|
133
|
+
document: merged,
|
|
134
|
+
// Only push back when the merge actually contains something the server has not seen.
|
|
135
|
+
pushBack: tookLocal,
|
|
136
|
+
...(conflicts.length > 0 ? { conflictedPaths: conflicts } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
decide(path, ctx) {
|
|
140
|
+
const policy = typeof this.policy === "function" ? this.policy(path, ctx) : (this.policy ?? "lww");
|
|
141
|
+
if (policy !== "lww")
|
|
142
|
+
return policy;
|
|
143
|
+
return (0, hlc_ts_1.compareHlcStamps)(ctx.localHlc, ctx.remoteHlc) > 0 ? "client" : "server";
|
|
144
|
+
}
|
|
145
|
+
/** Opt-in array merging for arrays of objects with a stable identity. */
|
|
146
|
+
mergeArraysById(base, local, remote, merged) {
|
|
147
|
+
for (const key of new Set([...Object.keys(local), ...Object.keys(remote)])) {
|
|
148
|
+
const l = local[key];
|
|
149
|
+
const r = remote[key];
|
|
150
|
+
if (!Array.isArray(l) || !Array.isArray(r))
|
|
151
|
+
continue;
|
|
152
|
+
if (!l.every(hasIdentity) || !r.every(hasIdentity))
|
|
153
|
+
continue;
|
|
154
|
+
const baseArr = Array.isArray(base[key]) ? base[key] : [];
|
|
155
|
+
const baseIds = new Set(baseArr.map(identityOf));
|
|
156
|
+
const byId = new Map();
|
|
157
|
+
for (const item of r)
|
|
158
|
+
byId.set(identityOf(item), item);
|
|
159
|
+
for (const item of l) {
|
|
160
|
+
const id = identityOf(item);
|
|
161
|
+
// Added locally, or edited locally — local wins for that element.
|
|
162
|
+
if (!baseIds.has(id) || !(0, path_ts_1.valuesEqual)(item, baseArr.find((b) => identityOf(b) === id))) {
|
|
163
|
+
byId.set(id, item);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Removed on either side stays removed.
|
|
167
|
+
const removedLocally = baseArr.filter((b) => !l.some((x) => identityOf(x) === identityOf(b)));
|
|
168
|
+
for (const gone of removedLocally)
|
|
169
|
+
byId.delete(identityOf(gone));
|
|
170
|
+
merged[key] = [...byId.values()];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.FieldLevelStrategy = FieldLevelStrategy;
|
|
175
|
+
/**
|
|
176
|
+
* Custom resolver.
|
|
177
|
+
*
|
|
178
|
+
* The developer's function may return either a bare document (treated as the resolved value) or a
|
|
179
|
+
* full `Resolution` when it needs to control `pushBack`. A throwing resolver is *not* caught here —
|
|
180
|
+
* the engine parks the mutation and keeps draining the queue, so one bad resolver cannot wedge sync
|
|
181
|
+
* for every other document.
|
|
182
|
+
*/
|
|
183
|
+
class CustomStrategy {
|
|
184
|
+
constructor(config) {
|
|
185
|
+
this.name = "custom";
|
|
186
|
+
if (!config.resolver) {
|
|
187
|
+
throw new Error('conflictResolution.strategy is "custom" but no `resolver` was provided. ' +
|
|
188
|
+
"Pass resolver: async ({ local, remote, base }) => {...}");
|
|
189
|
+
}
|
|
190
|
+
this.resolver = config.resolver;
|
|
191
|
+
}
|
|
192
|
+
async resolve(ctx) {
|
|
193
|
+
const result = await this.resolver(ctx);
|
|
194
|
+
if (result && typeof result === "object" && "winner" in result && "document" in result) {
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
const document = result;
|
|
198
|
+
// Infer the winner so events and Lens can report something meaningful.
|
|
199
|
+
const winner = (0, path_ts_1.valuesEqual)(document, ctx.remote)
|
|
200
|
+
? "remote"
|
|
201
|
+
: (0, path_ts_1.valuesEqual)(document, ctx.local)
|
|
202
|
+
? "local"
|
|
203
|
+
: "merged";
|
|
204
|
+
return { winner, document, pushBack: winner !== "remote" };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.CustomStrategy = CustomStrategy;
|
|
208
|
+
/** Registry — open for user-defined strategies (§11). */
|
|
209
|
+
class ConflictStrategyRegistry {
|
|
210
|
+
constructor(config = {}) {
|
|
211
|
+
this.strategies = new Map();
|
|
212
|
+
this.register(new LastWriteWinsStrategy());
|
|
213
|
+
this.register(new ServerWinsStrategy());
|
|
214
|
+
this.register(new ClientWinsStrategy());
|
|
215
|
+
this.register(new FieldLevelStrategy(config));
|
|
216
|
+
if (config.strategy === "custom" || config.resolver) {
|
|
217
|
+
this.register(new CustomStrategy(config));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
register(strategy) {
|
|
221
|
+
this.strategies.set(strategy.name, strategy);
|
|
222
|
+
}
|
|
223
|
+
get(name) {
|
|
224
|
+
const found = this.strategies.get(name);
|
|
225
|
+
if (!found) {
|
|
226
|
+
throw new Error(`Unknown conflict strategy "${name}". Available: ${[...this.strategies.keys()].join(", ")}.`);
|
|
227
|
+
}
|
|
228
|
+
return found;
|
|
229
|
+
}
|
|
230
|
+
names() {
|
|
231
|
+
return [...this.strategies.keys()];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.ConflictStrategyRegistry = ConflictStrategyRegistry;
|
|
235
|
+
// ---- diffing helpers -------------------------------------------------------
|
|
236
|
+
/**
|
|
237
|
+
* Leaf-path diff of `next` against `prev`.
|
|
238
|
+
*
|
|
239
|
+
* Deletions are recorded explicitly with a sentinel rather than omitted, because "absent from the
|
|
240
|
+
* diff" and "deleted by this side" must not be confused — conflating them silently undoes deletions.
|
|
241
|
+
*/
|
|
242
|
+
function diff(prev, next) {
|
|
243
|
+
const out = new Map();
|
|
244
|
+
const prevPaths = (0, path_ts_1.flattenPaths)(prev);
|
|
245
|
+
const nextPaths = (0, path_ts_1.flattenPaths)(next);
|
|
246
|
+
for (const [path, value] of nextPaths) {
|
|
247
|
+
if (isInternal(path))
|
|
248
|
+
continue;
|
|
249
|
+
if (!prevPaths.has(path) || !(0, path_ts_1.valuesEqual)(prevPaths.get(path), value))
|
|
250
|
+
out.set(path, value);
|
|
251
|
+
}
|
|
252
|
+
for (const [path] of prevPaths) {
|
|
253
|
+
if (isInternal(path))
|
|
254
|
+
continue;
|
|
255
|
+
if (!nextPaths.has(path))
|
|
256
|
+
out.set(path, DELETED);
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
/** `updatedAt` and the version key always differ; treating them as edits would conflict every time. */
|
|
261
|
+
function isInternal(path) {
|
|
262
|
+
const head = path.split(".")[0];
|
|
263
|
+
return head === "_id" || head === "updatedAt" || head === "__v" || head === "_sync";
|
|
264
|
+
}
|
|
265
|
+
function applyChange(target, path, value) {
|
|
266
|
+
if (value === DELETED) {
|
|
267
|
+
const parts = path.split(".");
|
|
268
|
+
const last = parts.pop();
|
|
269
|
+
const parent = parts.length ? (0, path_ts_1.getPath)(target, parts.join(".")) : target;
|
|
270
|
+
if (parent && typeof parent === "object")
|
|
271
|
+
delete parent[last];
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
(0, path_ts_1.setPath)(target, path, value);
|
|
275
|
+
}
|
|
276
|
+
/** `a` conflicts with `a.b`: replacing an object and editing inside it are not independent. */
|
|
277
|
+
function hasAncestorOrDescendant(path, others) {
|
|
278
|
+
for (const other of others.keys()) {
|
|
279
|
+
if (other === path)
|
|
280
|
+
continue;
|
|
281
|
+
if (other.startsWith(`${path}.`) || path.startsWith(`${other}.`))
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function hasIdentity(item) {
|
|
287
|
+
return (item !== null &&
|
|
288
|
+
typeof item === "object" &&
|
|
289
|
+
!Array.isArray(item) &&
|
|
290
|
+
(("_id" in item) || ("id" in item)));
|
|
291
|
+
}
|
|
292
|
+
function identityOf(item) {
|
|
293
|
+
if (item === null || typeof item !== "object")
|
|
294
|
+
return item;
|
|
295
|
+
const rec = item;
|
|
296
|
+
return String(rec._id ?? rec.id);
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=strategies.js.map
|