anchordb-angular 1.3.3 → 1.4.1
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 +479 -0
- package/README.md +6 -0
- package/package.json +4 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
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
|
+
## 1.4.1
|
|
9
|
+
|
|
10
|
+
**Security: `anchordb-sync-server` now keeps users apart.** Up to 1.4.0 the server checked the
|
|
11
|
+
identity `authenticate` returned and then dropped it: every user pulled every user's documents and
|
|
12
|
+
could change or delete any of them by id. Every version before 1.4.1 is affected. Found by the
|
|
13
|
+
expense-tracker team.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Each document belongs to a scope**, `user:<id>` for the user who created it, and a user reads and
|
|
18
|
+
changes only their own. `authenticate` may return a user id, as before, or
|
|
19
|
+
`{ userId, scopes }` to give a user further scopes (shared books, from 1.5.0).
|
|
20
|
+
- **Refusals reveal nothing.** Someone else's document is answered exactly as one that does not
|
|
21
|
+
exist: `rejected` with `code: 404`.
|
|
22
|
+
- **Retry answers and unique checks are per user.** Remembered answers are keyed by user as well as
|
|
23
|
+
`clientId`, so one user can't read back another's. Unique indexes are checked within the user's
|
|
24
|
+
documents; `{ global: true }` on a unique index checks across everyone (for usernames, say).
|
|
25
|
+
- **Fails closed.** Calling `handlePush`/`handlePull` yourself on a server with `authenticate` and no
|
|
26
|
+
caller is refused with 401. The Express, Next.js and NestJS adapters pass the caller for you.
|
|
27
|
+
- **A server with no `authenticate` warns at startup**, since every caller then shares every
|
|
28
|
+
document. Pass `singleUser: true` if that is what you want; it becomes required in 1.5.0.
|
|
29
|
+
|
|
30
|
+
### New
|
|
31
|
+
|
|
32
|
+
- **`adoptUnscoped(options, { assignScope })`.** Documents written before 1.4.1 have no scope, and a
|
|
33
|
+
server with `authenticate` hides them from everyone until this one-time command assigns them, for
|
|
34
|
+
example `assignScope: (collection, doc) => doc.userId ? "user:" + doc.userId : null`. Each one it
|
|
35
|
+
assigns is sent to its owner's phones again. It is safe to run twice, and two processes running it
|
|
36
|
+
at once don't assign anything twice. A server that only one person used can assign everything to
|
|
37
|
+
that person: `() => "user:<their id>"`.
|
|
38
|
+
- **Epoch.** Every response carries an `epoch` (default `"2"` with `authenticate`; set `epoch` to
|
|
39
|
+
change it). When a phone sees a new one, it deletes its synced copy and pulls again, keeping its
|
|
40
|
+
unsent changes (`sync:reset` event). That clears out other users' documents that phones pulled from
|
|
41
|
+
1.4.0 servers.
|
|
42
|
+
- **Protocol version 2.** A server with `authenticate` answers phones built with an older anchordb
|
|
43
|
+
`426 Update the app`, since they ignore the epoch. `legacyClients: "allow"` serves them for a
|
|
44
|
+
transition period. They are still kept apart, but they keep whatever they already pulled.
|
|
45
|
+
- **Pull limit** capped at 1000 changes (`maxPullLimit`).
|
|
46
|
+
- **Each pulled change carries its `scope`**, and the phone records it with the document.
|
|
47
|
+
|
|
48
|
+
### Changed on the phone (`anchordb`)
|
|
49
|
+
|
|
50
|
+
- A change refused with 404 is parked, as before, and the phone now also deletes its copy of that
|
|
51
|
+
document — so another user's document that was edited offline doesn't stay on the phone. A
|
|
52
|
+
refused insert keeps the user's new document.
|
|
53
|
+
- **A document deleted on the user's other phone** now comes back as a conflict marked
|
|
54
|
+
`deleted: true` with `document: null`, and goes through the conflict strategy: `server-wins`
|
|
55
|
+
accepts the delete; `last-write-wins` keeps a newer local edit and brings the document back;
|
|
56
|
+
`client-wins` always brings it back; `field-level` falls back to last-write-wins. A custom
|
|
57
|
+
`resolver` receives `remote: null`, and returns null to accept the delete. The `conflict` event
|
|
58
|
+
gains `remoteDeleted` and `resolvedDeleted`.
|
|
59
|
+
|
|
60
|
+
### Upgrading — in this order
|
|
61
|
+
|
|
62
|
+
1. **Upgrade the server** to `anchordb-sync-server@1.4.1`.
|
|
63
|
+
2. **Run `adoptUnscoped`** once with an `assignScope` that knows whose each document is. Until you
|
|
64
|
+
do, those documents are hidden from everyone. Nothing is ever deleted on the server.
|
|
65
|
+
3. **Ship a new app build** with `anchordb@1.4.1`. On its first sync it starts over once, because of
|
|
66
|
+
the new epoch, and its unsent changes are kept.
|
|
67
|
+
|
|
68
|
+
App builds that use an anchordb version before 1.4.1 **stop syncing** (426) until users install the
|
|
69
|
+
update, unless the server sets `legacyClients: "allow"`. A server without `authenticate` is unchanged:
|
|
70
|
+
it still serves everyone everything, and still accepts old phones.
|
|
71
|
+
|
|
72
|
+
## 1.4.0
|
|
73
|
+
|
|
74
|
+
**Transactions, migrations, recovery, TTL indexes, encryption, a test kit — and guides that ship in the
|
|
75
|
+
package.** Asked for by the expense-tracker team after building an offline-first app on 1.3; every
|
|
76
|
+
item has tests, and nothing existing changes behaviour except as noted.
|
|
77
|
+
|
|
78
|
+
### New
|
|
79
|
+
|
|
80
|
+
- **`db.transaction(async (session) => …)`** — writes across collections, all or nothing, with
|
|
81
|
+
Mongoose's API: `{ session }` on `create`, `insertOne/Many`, `update*`, `replaceOne`, `delete*`,
|
|
82
|
+
`findOneAnd*`, `bulkWrite`, `aggregate`, the index methods and `doc.save()`, and `.session()` on
|
|
83
|
+
queries. A throw rolls every collection back; `change` events and queued sync mutations wait for
|
|
84
|
+
the commit. A call inside made without the session times out the transaction with
|
|
85
|
+
`TransactionTimeoutError` (`transactionTimeoutMs`, default 30 s) instead of hanging. Expo, Capacitor,
|
|
86
|
+
`node:sqlite` and memory; IndexedDB refuses with an explanation. `AnchorSession` / `ClientSession`.
|
|
87
|
+
- **Versioned migrations**: `createAnchorDB({ version, migrations: { 2: async (ctx) => … } })`, run by
|
|
88
|
+
`open()`, each step in its own transaction that records the version reached. A failed step is
|
|
89
|
+
rolled back and reported as `MigrationError`; `open()` can be retried. Older apps refuse newer data.
|
|
90
|
+
- **Recovery**: open failures are `DatabaseOpenError`, or `CorruptDatabaseError` /
|
|
91
|
+
`DatabaseLockedError`, with the driver's error as `cause`. `db.reset()` deletes the storage even
|
|
92
|
+
when it no longer opens. `integrityCheck: true` runs `PRAGMA quick_check` on open.
|
|
93
|
+
`db.rebuildIndexes()` rebuilds index entries from the documents.
|
|
94
|
+
- **TTL indexes are enforced.** `expireAfterSeconds` was stored and never applied; a sweep now deletes
|
|
95
|
+
expired documents with MongoDB's rules, every 60 s (`ttlIntervalMs`, `ttl: false`), and `db.expire()`
|
|
96
|
+
sweeps on demand.
|
|
97
|
+
- **Encryption**: `ExpoSqliteAdapter({ encryptionKey })` opens the file with SQLCipher, and refuses
|
|
98
|
+
rather than store plain text when expo-sqlite is built without it.
|
|
99
|
+
- **Observability**: `db:open`, `db:transaction` and `db:slowQuery` (with `slowQueryMs`) events, and
|
|
100
|
+
`db.diagnostics()` for the storage queue depth.
|
|
101
|
+
- **`anchordb/testing`**: `fakeExpoSqlite` and `fakeCapacitorSqlite` run the real adapters in Node,
|
|
102
|
+
and `runAdapterConformance` holds any adapter to the storage contract. The Expo stand-in is the
|
|
103
|
+
expense-tracker team's, which found the 1.3.2 bugs.
|
|
104
|
+
- **Default file name**: an Expo or Capacitor adapter built without a name takes the database's —
|
|
105
|
+
`new ExpoSqliteAdapter({ sqlite })` with `name: "expense-tracker"` opens `expense-tracker.db`.
|
|
106
|
+
- `anchordb-react`: **`useAnchorStatus(db)`** documented as the way to render opening, failure and retry.
|
|
107
|
+
- **Guides in the package**, under `docs/`: sync, concurrency and transactions, migrations / recovery
|
|
108
|
+
/ TTL / encryption, aggregation, testing, the inspector. Sync existed since 1.0 but was never
|
|
109
|
+
explained, which is why the team built their own.
|
|
110
|
+
- **Every package ships this changelog**, and each release is tagged on GitHub.
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
|
|
114
|
+
- Every storage adapter queues all of its calls, reads included, per database. Concurrency was already
|
|
115
|
+
safe for writes in 1.3.3; reads now also never see another caller's uncommitted transaction.
|
|
116
|
+
- An error from `open()` is now a `DatabaseOpenError` (message prefixed "The database did not open:")
|
|
117
|
+
instead of the driver's bare error, which is kept as `cause`. Code that matched on the message still
|
|
118
|
+
finds it inside.
|
|
119
|
+
- `node:sqlite` adapter: prepared statements are dropped when the schema changes. One planned against
|
|
120
|
+
a partial index inside a transaction otherwise kept that plan and matched nothing when reused.
|
|
121
|
+
|
|
122
|
+
### Not yet
|
|
123
|
+
|
|
124
|
+
- Sync on a timer: `autoSync` and `intervalMs` are still reserved; call `db.sync.now()`.
|
|
125
|
+
- Suspense for `useFind`; server-side expiry of sync delete markers; transactions on IndexedDB.
|
|
126
|
+
|
|
127
|
+
## 1.3.3
|
|
128
|
+
|
|
129
|
+
**Writes through Expo and Capacitor SQLite are safe under concurrency, and work on a device.** Found
|
|
130
|
+
in an expense tracker on 1.3.2; every case below has a regression test.
|
|
131
|
+
|
|
132
|
+
- **Concurrent writes no longer share a transaction** (Expo, Capacitor). Every driver call awaits each
|
|
133
|
+
statement, so two writes started together interleaved, and the second ran inside the first's
|
|
134
|
+
transaction: a write reported as failed could be partly saved, and one caller's rollback could undo
|
|
135
|
+
another's committed write. `SqlAdapterBase` now queues every driver call, reads included.
|
|
136
|
+
- **Expo SQLite writes no longer fail with `database is locked`.** Inside
|
|
137
|
+
`withExclusiveTransactionAsync` statements now go to the `txn` connection expo-sqlite hands over,
|
|
138
|
+
instead of the outer one the exclusive lock blocks. Apps that mapped the exclusive transaction onto
|
|
139
|
+
`withTransactionAsync` as a workaround keep working.
|
|
140
|
+
- **Concurrent upserts of the same new key** now insert once and update the rest, as MongoDB 4.2+ does,
|
|
141
|
+
instead of rejecting with E11000. A duplicate on some other unique field is still reported.
|
|
142
|
+
- **A failed `db.open()` can be retried.** The rejection was cached, so a transient failure needed a
|
|
143
|
+
new `AnchorDB`.
|
|
144
|
+
- **Duplicate-key errors name the configured database** — `expense-tracker.metas`, not
|
|
145
|
+
`anchor.metas`. `ExpoSqliteAdapter` defaults its name to the file name without `.db`, and
|
|
146
|
+
`createAnchorDB` passes `config.name`.
|
|
147
|
+
- `anchordb-react`: **`useAnchorReady` no longer waits forever when the database fails to open.** It
|
|
148
|
+
throws the error during render, for an error boundary. The new `useAnchorStatus(db)` returns
|
|
149
|
+
`{ status, ready, error, retry }` to show the failure and retry in place, and `anchorOpenStore(db)`
|
|
150
|
+
is the store both read. Neither starts work while rendering any more.
|
|
151
|
+
|
|
152
|
+
## 1.3.2
|
|
153
|
+
|
|
154
|
+
Documentation only — no code changes.
|
|
155
|
+
|
|
156
|
+
- **Bugs, support and feedback go to the AnchorDB project page**, at
|
|
157
|
+
[nisalanadeera.com/projects/anchordb](https://www.nisalanadeera.com/projects/anchordb#feedback): a form
|
|
158
|
+
that asks for the package, version and platform, and a reply by email.
|
|
159
|
+
- Every package's npm **Homepage** is that page, and **Report a bug** (`npm bugs`) opens the form.
|
|
160
|
+
- Every README links the overview and the form, and says what a bug report that can be acted on
|
|
161
|
+
includes.
|
|
162
|
+
- The package READMEs' family tables list all six packages, with `anchordb-lens-link`.
|
|
163
|
+
- All six packages move to 1.3.2 together.
|
|
164
|
+
|
|
165
|
+
## 1.3.1
|
|
166
|
+
|
|
167
|
+
**Lens Link keeps both apps running in the background.** Found on a phone: switching from the app to
|
|
168
|
+
Anchor Lens let the phone stop the app within seconds, and the link went with it.
|
|
169
|
+
|
|
170
|
+
- `anchordb-lens-link`: while the link is on, the app runs an Android foreground service with an
|
|
171
|
+
ongoing notification — *Anchor Lens Link on*, then *Anchor Lens connected* — which is what Android
|
|
172
|
+
lets an app use to keep running. `keepAlive: false` turns it off; `link.keepAliveError` says why it
|
|
173
|
+
could not start.
|
|
174
|
+
- `startKeepAlive`, `updateKeepAlive` and `stopKeepAlive` keep a client running the same way. Anchor
|
|
175
|
+
Lens uses them while it is connected to an app, so you can switch between the two.
|
|
176
|
+
- `backgroundStatus()`, `askNotificationPermission()`, `askBatteryUnrestricted()`,
|
|
177
|
+
`openNotificationSettings()` and `openBackgroundSettings()`: the permissions and settings that stop a
|
|
178
|
+
phone overruling it, including Xiaomi's Autostart screen.
|
|
179
|
+
- Only `anchordb-lens-link` changes. The other packages stay at 1.3.0.
|
|
180
|
+
|
|
181
|
+
## 1.3.0
|
|
182
|
+
|
|
183
|
+
**Anchor Lens Link: open a QA build's database in Anchor Lens on the same phone, from a file.** Asked
|
|
184
|
+
for after testing on a phone, where reaching another app took a laptop, the relay, an address, a room, a
|
|
185
|
+
QR scan and a pairing code.
|
|
186
|
+
|
|
187
|
+
- **New package, `anchordb-lens-link`.** `enableLensLink(db, { allowInProduction: true })` starts a
|
|
188
|
+
WebSocket server on 127.0.0.1, puts an `InspectorAgent` behind it and writes a sealed
|
|
189
|
+
`Android/media/<package>/anchor-lens/<database>.anchorlens`. In Anchor Lens, *Open app from file*
|
|
190
|
+
reads it, connects and authenticates with nothing typed. The port and the secret change every
|
|
191
|
+
launch. Android only; the native part is a small Expo module on Java-WebSocket. It is a separate
|
|
192
|
+
package, and does nothing unless the app calls it, so production apps need never ship it.
|
|
193
|
+
- **`auth.sharedSecret` for the inspector.** An agent can authenticate a secret both sides already
|
|
194
|
+
hold instead of a code shown on screen. It is proved the same way, as an HMAC over a single-use
|
|
195
|
+
challenge, but it is not burned, so a client can reconnect; a spent or expired challenge is replaced
|
|
196
|
+
at once. Secrets shorter than 32 characters are refused. Pairing codes work as before.
|
|
197
|
+
- **`assertInspectorAllowed` is exported**, so a package that starts an agent applies the same
|
|
198
|
+
production guard as the relay connection.
|
|
199
|
+
- Tested in Node end to end: the sealed file, an agent with a shared secret behind a WebSocket server
|
|
200
|
+
that behaves like the native one, and a Lens client that reads the file, proves the secret, reads and
|
|
201
|
+
writes through the Model API, reconnects with the same file, and is shut out after `close()`.
|
|
202
|
+
|
|
203
|
+
## 1.2.0
|
|
204
|
+
|
|
205
|
+
Anchor Lens can create and drop indexes, and a malformed `_id` is refused instead of stored. Both
|
|
206
|
+
found while testing Lens on a phone.
|
|
207
|
+
|
|
208
|
+
- **Create Index and Drop Index on the inspector protocol.** `InspectorClient.createIndex(collection,
|
|
209
|
+
key, options)` and `dropIndex(collection, name)`, routed through `Model.createIndex()` and
|
|
210
|
+
`dropIndex()`. Agents had advertised `indexes:write` with no method behind it. Keys are checked
|
|
211
|
+
(`1` or `-1`, no `$` fields), read-only sessions refuse both, a unique index over values that already
|
|
212
|
+
collide fails with `duplicate_key`, and `_id_` cannot be dropped.
|
|
213
|
+
- **`getSchema(collection)` on the inspector protocol** — a declared model's fields with their type,
|
|
214
|
+
required flag, enum and plain default, nested objects as dotted paths. What Lens needs to offer
|
|
215
|
+
"Add all fields" on Insert document. Validator functions and function defaults are not sent; a
|
|
216
|
+
collection with no declared model returns no fields.
|
|
217
|
+
- **An insert stored an object `_id` as "[object Object]".** Insert skipped the `_id` handling updates
|
|
218
|
+
apply, and storage keys rows by the id's string form — so Extended JSON pasted into Lens, with
|
|
219
|
+
`{ "$oid": … }` read as a plain object, produced such a row, and every such document would have
|
|
220
|
+
collided on it. Objects that are not ObjectIds are now refused with a `CastError`. Strings and
|
|
221
|
+
numbers are kept, as the driver keeps them.
|
|
222
|
+
|
|
223
|
+
## 1.1.0
|
|
224
|
+
|
|
225
|
+
**Anchor Lens can read other apps.** Pointing Lens at a second app on a real phone found every step of
|
|
226
|
+
that path broken or missing.
|
|
227
|
+
|
|
228
|
+
- **`inspector: { enabled, relayUrl }` did nothing.** It ran a production check and never connected.
|
|
229
|
+
With `relayUrl` set the database now publishes itself to `anchor-relay` on construction, stays
|
|
230
|
+
connected, reconnects after a drop, and reports pairing codes through `inspector.onPairing` and the
|
|
231
|
+
`inspector:pairing` / `inspector:state` events. `db.inspector` exposes the connection, and
|
|
232
|
+
`connectInspectorToRelay(db, options)` does the same at runtime.
|
|
233
|
+
- **Pairing asked for a 32-character hex challenge, typed on a phone.** The agent now sends it with
|
|
234
|
+
`connect` as `ConnectResult.challenge` — a nonce, not a secret; the code never leaves the app — and
|
|
235
|
+
opens a fresh pairing window, announcing its code, when the last one was spent or expired. Lens types
|
|
236
|
+
only the code.
|
|
237
|
+
- **The production guard never fired on React Native.** It read `process.env.NODE_ENV` through
|
|
238
|
+
`globalThis`, which Metro does not inline, so a release build looked like development. `__DEV__ ===
|
|
239
|
+
false` now counts as production too.
|
|
240
|
+
- **`anchordb-relay`:**
|
|
241
|
+
- With `--host 0.0.0.0` it printed `ws://0.0.0.0:9440` — the listen address, which no device can
|
|
242
|
+
dial. It now prints the machine's network addresses.
|
|
243
|
+
- It prints the Lens link as a QR code in the terminal.
|
|
244
|
+
- `--room <id>` fixes the room, so an app built with the agent URL survives relay restarts.
|
|
245
|
+
- The room it printed was reclaimed after 30 idle minutes, so an app connecting later got "unknown
|
|
246
|
+
room". It is now kept for the life of the relay.
|
|
247
|
+
- Stopping it hung while an app or Lens was connected: neither `wss.close` nor `http.close` ends
|
|
248
|
+
established connections. They are now terminated.
|
|
249
|
+
- Tested over real sockets: a relay server, an app publishing with `connectInspectorToRelay`, and a Lens
|
|
250
|
+
client that pairs with only the code, reads and writes, then shuts down cleanly.
|
|
251
|
+
|
|
252
|
+
## 1.0.6
|
|
253
|
+
|
|
254
|
+
Crash on launch in React Native apps that load `anchordb` before `anchordb-react`.
|
|
255
|
+
|
|
256
|
+
- **Importing `anchordb` needed a crypto source.** `ObjectId` drew its per-process random block and
|
|
257
|
+
its counter seed when the module loaded. On React Native, `globalThis.crypto` comes from the
|
|
258
|
+
`react-native-get-random-values` polyfill that `anchordb-react` loads — so an app that evaluated
|
|
259
|
+
`anchordb` first died with `No cryptographically secure random source is available` before it
|
|
260
|
+
generated a single id. Lens React did exactly that through a re-export and closed the instant it
|
|
261
|
+
launched on a phone; the Contacts app survived only because its first import happened to be
|
|
262
|
+
`createAnchorDB`. Both values are now drawn on the first `ObjectId`, and a failed draw caches
|
|
263
|
+
nothing, so a polyfill that loads later still works.
|
|
264
|
+
- The error message now points React Native users at `anchordb-react`'s `createAnchorDB`.
|
|
265
|
+
- A new test imports the whole package with no crypto source. Without this fix it fails with the
|
|
266
|
+
original error.
|
|
267
|
+
|
|
268
|
+
## 1.0.5
|
|
269
|
+
|
|
270
|
+
Inspector fix, found by building Anchor Lens on a database on the device.
|
|
271
|
+
|
|
272
|
+
- **Schemaless collections were reported to Lens as schema-backed.** `db.collection(name)` — which
|
|
273
|
+
the inspector agent uses for any collection without a model, including every collection Lens
|
|
274
|
+
creates by inserting into it — registers an internal `__collection__<name>` model.
|
|
275
|
+
`getCollections` counted it as a schema, so Lens showed `hasSchema: true` and a model pill reading
|
|
276
|
+
`__collection__notes` instead of "schemaless". The agent now reports only models the app declared.
|
|
277
|
+
- **`modelForCollection` returned whichever model registered first.** A collection reached through
|
|
278
|
+
`db.collection()` before its model was declared resolved to the schemaless model, so an export
|
|
279
|
+
emitted ObjectIds and Dates as bare strings. A declared model now always wins.
|
|
280
|
+
|
|
281
|
+
## 1.0.4
|
|
282
|
+
|
|
283
|
+
`anchordb-angular` in native Capacitor builds. No change to database behaviour.
|
|
284
|
+
|
|
285
|
+
- **Capacitor links native plugins only from the package.json of the app.** `@capacitor-community/sqlite`
|
|
286
|
+
arrives through `anchordb-angular`, so it was installed but `npx cap sync` left it out of the
|
|
287
|
+
Android project — found by building the Ionic test app, whose `capacitor.settings.gradle` had no
|
|
288
|
+
SQLite plugin. On a device the first query would have failed with
|
|
289
|
+
`"CapacitorSQLite" plugin is not implemented on android`. `ionicAdapter` now checks
|
|
290
|
+
`Capacitor.isPluginAvailable("CapacitorSQLite")` and throws naming
|
|
291
|
+
`npm install @capacitor-community/sqlite` and `npx cap sync`. It does not fall back to IndexedDB,
|
|
292
|
+
which would strand the data in a store the app abandons once the plugin is linked.
|
|
293
|
+
- **`@capacitor/core` was declared `^8.5.2` and `@capacitor-community/sqlite` `^8.1.1`** — the same
|
|
294
|
+
`npm install -w` caret rewrite as `anchordb-react` 1.0.1. A Capacitor 6 or 7 app would have got a
|
|
295
|
+
second `@capacitor/core`, and plugins registered on one instance are invisible to the other. Both
|
|
296
|
+
are now `>=6`, held by a regression test.
|
|
297
|
+
- The README and package reference showed `inject(AnchorService)` and `AnchorModule.forRoot(...)`
|
|
298
|
+
inside `imports`. Neither works: the providers use string tokens, and `forRoot` returns plain
|
|
299
|
+
providers. Both now show `@Inject("AnchorService")` and spread `.providers`.
|
|
300
|
+
- Verified in the Ionic test app on Capacitor 8.5.2: with the plugin listed, `cap sync` reports
|
|
301
|
+
`@capacitor-community/sqlite@8.1.1` for Android and the Gradle settings include it.
|
|
302
|
+
|
|
303
|
+
## 1.0.3
|
|
304
|
+
|
|
305
|
+
Guidance fix for Expo apps. No change to database behaviour.
|
|
306
|
+
|
|
307
|
+
- **`createAnchorDB` and the `anchordb-react` README recommended `npx expo install --fix`**, which
|
|
308
|
+
cannot help: it only realigns packages listed in the package.json of the app, and `expo-sqlite`
|
|
309
|
+
and `react-native-get-random-values` arrive through `anchordb-react`. Both now name
|
|
310
|
+
`npx expo install expo-sqlite react-native-get-random-values`, which pins the drivers at the top
|
|
311
|
+
level where the wide ranges from 1.0.2 dedupe onto them.
|
|
312
|
+
- Verified in an Expo SDK 56 app: with the pins, both drivers resolve to the versions the SDK pins
|
|
313
|
+
(`expo-sqlite` 56.0.6, `react-native-get-random-values` 1.11.0), deduped, and
|
|
314
|
+
`npx expo install --check` reports dependencies up to date. Without the pin the polyfill resolves
|
|
315
|
+
to 2.0.0 against the 1.11 native module Expo Go ships.
|
|
316
|
+
|
|
317
|
+
## 1.0.2
|
|
318
|
+
|
|
319
|
+
`anchordb-react` pinned its native drivers to a single Expo SDK.
|
|
320
|
+
|
|
321
|
+
- **`expo-sqlite` was declared `^57.0.3`**, so an app on Expo SDK 56 (`expo-sqlite ~56.0.4`) could
|
|
322
|
+
not dedupe it. npm nested a second, SDK 57 `expo-sqlite` whose JavaScript no longer matched the
|
|
323
|
+
native build of the app. The intended range was `>=14`; `npm install -w` rewrote it with a caret
|
|
324
|
+
when the dependency was added. Both drivers now take wide ranges (`expo-sqlite >=14`,
|
|
325
|
+
`react-native-get-random-values >=1.11`), so npm reuses whatever version the Expo SDK of the app
|
|
326
|
+
already installed.
|
|
327
|
+
- A test now asserts both ranges accept the drivers Expo SDK 56 and 57 ship and are not caret- or
|
|
328
|
+
tilde-pinned, so a future `npm install -w` cannot silently narrow them again.
|
|
329
|
+
|
|
330
|
+
Found by comparing against a working Expo SDK 56 app on the same machine.
|
|
331
|
+
|
|
332
|
+
## 1.0.1
|
|
333
|
+
|
|
334
|
+
Two defects in `anchordb-angular`, both in its *documented* usage, found by building a real Ionic
|
|
335
|
+
app against the published package for the first time.
|
|
336
|
+
|
|
337
|
+
- **The provider arrays did not typecheck against Angular.** `Provider.useFactory` was declared
|
|
338
|
+
optional, so the array was not assignable to Angular’s `Provider[]` and the documented
|
|
339
|
+
`bootstrapApplication` example failed to compile. It is now required, matching what every
|
|
340
|
+
provider in the package actually returns, and `useValue` — declared but never used — is gone.
|
|
341
|
+
- **`.toObservable()` was documented but implemented nowhere.** The real API is
|
|
342
|
+
`AnchorService.find$` / `findOne$` / `count$`, which return an RxJS-interop `AnchorObservable`
|
|
343
|
+
the `async` pipe accepts directly. The documentation now shows that.
|
|
344
|
+
|
|
345
|
+
## 1.0.0
|
|
346
|
+
|
|
347
|
+
**Versioning changes with this release.** The major version now tracks the React / Angular major the
|
|
348
|
+
packages are built against: React 18 stays on the `0.x` line, React 19 begins at `1.x`. Projects on
|
|
349
|
+
React 18 remain on 0.2.1, preserved on the
|
|
350
|
+
[`release/0.x-react18`](https://github.com/knnadeera/anchordb/tree/release/0.x-react18) branch.
|
|
351
|
+
|
|
352
|
+
### One install, ready to use
|
|
353
|
+
|
|
354
|
+
`npm install anchordb-react` now brings the database, the SQLite driver and the crypto polyfill —
|
|
355
|
+
and `createAnchorDB({ name })` needs no adapter wiring. `npm install anchordb-angular` does the
|
|
356
|
+
same, with `provideAnchorIonic` choosing Capacitor SQLite on a device and IndexedDB under
|
|
357
|
+
`ionic serve`.
|
|
358
|
+
|
|
359
|
+
- `anchordb-react` gains `createAnchorDB`, and depends on `expo-sqlite` and
|
|
360
|
+
`react-native-get-random-values`
|
|
361
|
+
- `anchordb-angular` gains `ionicAdapter` and `provideAnchorIonic`, and depends on
|
|
362
|
+
`@capacitor/core` and `@capacitor-community/sqlite`
|
|
363
|
+
|
|
364
|
+
### Breaking
|
|
365
|
+
|
|
366
|
+
- **`anchordb-react` is now React Native only.** It imports `react-native-get-random-values`, which
|
|
367
|
+
imports React Native internals, so a React web build fails. Browser apps use `anchordb` directly
|
|
368
|
+
with `anchordb/storage/indexeddb`, which is unchanged.
|
|
369
|
+
- **React 19 is required** by `anchordb-react`, since `expo-sqlite` pulls React Native.
|
|
370
|
+
|
|
371
|
+
### Fixed
|
|
372
|
+
|
|
373
|
+
- **Subpath imports were unusable under `moduleResolution: Node10`.** Neither `anchordb` nor
|
|
374
|
+
`anchordb-sync-server` declared `typesVersions`, so `anchordb/storage/expo-sqlite` and friends
|
|
375
|
+
resolved to nothing for any consumer on classic resolution — and broke this repo's own CommonJS
|
|
376
|
+
build. Every `node10` result in are-the-types-wrong is now green where most were previously
|
|
377
|
+
failing.
|
|
378
|
+
- **The `ExpoDatabase` interface did not match the real `expo-sqlite`.** `params` was declared
|
|
379
|
+
optional where the driver requires it, and typed `unknown[]` where SQLite accepts only five bind
|
|
380
|
+
types. The actual module was therefore not assignable to the interface, so every TypeScript user
|
|
381
|
+
following the documented usage got an error. Caught by typechecking against the real package for
|
|
382
|
+
the first time.
|
|
383
|
+
- **`sideEffects: false` on `anchordb-react`** would have let a bundler tree-shake away the crypto
|
|
384
|
+
polyfill, making the first `ObjectId` throw. It now names the built entries.
|
|
385
|
+
|
|
386
|
+
## 0.2.1
|
|
387
|
+
|
|
388
|
+
Documentation and discoverability only — no code changes.
|
|
389
|
+
|
|
390
|
+
- Every package README now carries a **The AnchorDB family** table, so someone landing on
|
|
391
|
+
`anchordb-react` from an npm search can see the other four and what each is for.
|
|
392
|
+
- npm keywords added to `anchordb-react`, `anchordb-angular`, `anchordb-sync-server` and
|
|
393
|
+
`anchordb-relay`, which had none and so did not surface in npm search at all. `anchordb` gained
|
|
394
|
+
aggregation, schema and document-database terms.
|
|
395
|
+
- Corrected stale `0.1.0` version claims in three READMEs.
|
|
396
|
+
|
|
397
|
+
## 0.2.0 — first published release
|
|
398
|
+
|
|
399
|
+
Published as the unscoped `anchordb` family. The `@anchor` scope was never available: it needs an
|
|
400
|
+
npm organization, so the packages took unscoped names instead.
|
|
401
|
+
|
|
402
|
+
### Added
|
|
403
|
+
|
|
404
|
+
- **Bulk writes on the inspector protocol** — `updateMany` and `deleteMany`, behind a new
|
|
405
|
+
`documents:bulk` capability, routed through the ordinary Model API so a bulk write from Anchor
|
|
406
|
+
Lens validates, stamps and queues exactly like an app write. An empty filter is refused unless
|
|
407
|
+
explicitly acknowledged.
|
|
408
|
+
- **CSV export and import** (`toCsv`, `fromCsv`, `detectImportFormat`), matching
|
|
409
|
+
`mongoexport --type=csv`. Values are cast through the collection schema on import.
|
|
410
|
+
- **A `filter` option on export**, for exporting query results rather than a whole collection.
|
|
411
|
+
- **A `default` Extended JSON mode**, alongside `relaxed` and `canonical`.
|
|
412
|
+
- **A MongoDB bridge** (`MongoBridgeClient` here, `MongoBridge` in `anchordb-relay`), so Lens can
|
|
413
|
+
pull collections from a real MongoDB or push a device into one.
|
|
414
|
+
|
|
415
|
+
### Fixed
|
|
416
|
+
|
|
417
|
+
- **`modifiedCount` could never be 0.** `updatedAt` and the version key were stamped before the
|
|
418
|
+
change comparison, so every matched document counted as modified and a no-op still bumped the
|
|
419
|
+
timestamp. A no-op update is now matched but not modified, and writes nothing — which also stops a
|
|
420
|
+
bulk update over 10,000 documents queueing 10,000 mutations when only a few hundred changed.
|
|
421
|
+
- **`MongooseStore` was never exported** from `anchordb-sync-server`, so it was absent from the
|
|
422
|
+
published types and unreachable by anyone following the README.
|
|
423
|
+
|
|
424
|
+
## 0.1.0 — never published
|
|
425
|
+
|
|
426
|
+
The initial implementation. Superseded by 0.2.0 before anything reached npm.
|
|
427
|
+
|
|
428
|
+
First release. Five packages, published together.
|
|
429
|
+
|
|
430
|
+
### `anchordb`
|
|
431
|
+
|
|
432
|
+
- **Local-first database** with a Mongoose-shaped API. No `sync` block means a purely local
|
|
433
|
+
database: no mutation queue, no network code, no sync metadata, hard deletes.
|
|
434
|
+
- **Mongoose parity**: `Schema`, `model()`, documents with `.save()`/`.isModified()`, virtuals,
|
|
435
|
+
methods, statics, `pre`/`post` hooks, `ValidationError`/`CastError` shapes, and `code: 11000`.
|
|
436
|
+
- **Decorators** (`anchordb/decorators`) matching `@nestjs/mongoose` — `@Schema`, `@Prop`,
|
|
437
|
+
`SchemaFactory`, `raw()` — so one decorated class can be shared with a NestJS backend.
|
|
438
|
+
Metadata is optional: explicit `@Prop(String)` works with `emitDecoratorMetadata` off.
|
|
439
|
+
- **Real MongoDB ObjectId**, generated on-device, cross-validated byte-for-byte against `bson`.
|
|
440
|
+
- **Indexes** — single, compound, multikey, `unique`, `sparse`, `partialFilterExpression`, TTL —
|
|
441
|
+
with unique violations enforced at the storage layer and reported as `E11000`.
|
|
442
|
+
- **Lazy chainable query builder**: `.populate().sort().skip().limit().select().exec()`, thenable,
|
|
443
|
+
async-iterable, with `explain()`.
|
|
444
|
+
- **Aggregation pipeline** with streaming execution, index pushdown, and a `mongoCompatible` strict
|
|
445
|
+
mode that rejects non-portable constructs at development time.
|
|
446
|
+
- **UTC-canonical timestamps** with IANA timezone support, validated at construction.
|
|
447
|
+
- **Extended JSON v2 interop** — `export()` feeds `mongoimport`; `mongoexport` output imports back.
|
|
448
|
+
- **Offline mutation queue** and sync engine with five conflict strategies
|
|
449
|
+
(`last-write-wins`, `server-wins`, `client-wins`, `field-level`, `custom`).
|
|
450
|
+
- **Inspector protocol v1** with pairing, sessions, and live change events.
|
|
451
|
+
- **Five storage adapters**: Memory, Node SQLite, IndexedDB, Expo SQLite, Capacitor SQLite.
|
|
452
|
+
|
|
453
|
+
### `anchordb-sync-server`
|
|
454
|
+
|
|
455
|
+
- Framework-agnostic protocol engine over a pluggable `ServerStore`.
|
|
456
|
+
- Express, NestJS and Next.js adapters, all held to one conformance suite.
|
|
457
|
+
- `MemoryStore` (default) and `MongooseStore`.
|
|
458
|
+
|
|
459
|
+
### `anchordb-react`
|
|
460
|
+
|
|
461
|
+
- `useFind`, `useFindOne`, `useCount`, `useAggregate`, `useSyncStatus`, `useAnchorEvent`.
|
|
462
|
+
|
|
463
|
+
### `anchordb-angular`
|
|
464
|
+
|
|
465
|
+
- `AnchorModule.forRoot`/`forFeature`, `getModelToken`, `AnchorService` with RxJS-compatible
|
|
466
|
+
observables. Angular and RxJS are optional peers and are never imported.
|
|
467
|
+
|
|
468
|
+
### `anchordb-relay`
|
|
469
|
+
|
|
470
|
+
- Development-only WebSocket relay plus the `anchor-relay` CLI.
|
|
471
|
+
|
|
472
|
+
### Known limitations
|
|
473
|
+
|
|
474
|
+
- **Expo SQLite and Capacitor SQLite are unverified** — written and typechecked, never executed on
|
|
475
|
+
a device. Test before production use.
|
|
476
|
+
- The REST sync transport and `MongooseStore` have no live-server tests.
|
|
477
|
+
- Not supported in v1: discriminators, `refPath`, transactions, server-side change streams,
|
|
478
|
+
text/geo/hashed indexes, collation, and the `$graphLookup`/`$geoNear`/`$search`/`$merge`/`$out`/
|
|
479
|
+
`$unionWith` aggregation stages.
|
package/README.md
CHANGED
|
@@ -133,6 +133,12 @@ Each one needs a different third-party framework as a peer dependency, and npm r
|
|
|
133
133
|
package rather than per import — which is why they are not one package. Full reasoning and API
|
|
134
134
|
reference: [github.com/knnadeera/anchordb](https://github.com/knnadeera/anchordb).
|
|
135
135
|
|
|
136
|
+
## Changes
|
|
137
|
+
|
|
138
|
+
What changed in each version is in [CHANGELOG.md](./CHANGELOG.md), shipped in this package — one
|
|
139
|
+
changelog for the whole family, since all six packages are released together. Releases are also
|
|
140
|
+
tagged on [GitHub](https://github.com/knnadeera/anchordb/releases).
|
|
141
|
+
|
|
136
142
|
## Bugs, support and feedback
|
|
137
143
|
|
|
138
144
|
Report a bug, ask for help or suggest a feature on the [AnchorDB project page](https://www.nisalanadeera.com/projects/anchordb#feedback) —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anchordb-angular",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
25
|
"README.md",
|
|
26
|
-
"LICENSE"
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"CHANGELOG.md"
|
|
27
28
|
],
|
|
28
29
|
"scripts": {
|
|
29
30
|
"build": "rimraf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.build.cjs.json && node ../core/scripts/finalize-dist.mjs",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@capacitor-community/sqlite": ">=6",
|
|
36
37
|
"@capacitor/core": ">=6",
|
|
37
|
-
"anchordb": "^1.
|
|
38
|
+
"anchordb": "^1.4.1"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"rimraf": "^6.0.1"
|