diff --git a/.changeset/aggregate-snapshots.md b/.changeset/aggregate-snapshots.md deleted file mode 100644 index 3c3b7fd1..00000000 --- a/.changeset/aggregate-snapshots.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@bounda-dev/core": minor -"@bounda-dev/cli": minor -"@bounda-dev/postgresql": minor ---- - -Snapshots of an aggregate's state, opt-in per aggregate with `runtime.overrides..snapshot: { every }`. A `snapshot:` subscriber writes them in the background, and a command loads the snapshot and folds only the events after it. A snapshot is a cache of the fold and never the source of truth: it is valid only for the `stateFingerprint` the generator now writes to the registry, a digest of the modules that shape the state and of the type inferred for it, plus the configured `snapshot.version`; one that is missing, from other code, unreadable or past the stream's head is ignored, and a share of the loads (`snapshot.verify`, `0.01` by default) is checked against a fold of the whole stream, which drops a snapshot that folds to another state. An app from `createTestApp` checks every load and throws `SnapshotError`. The state is kept with its types (`Date`, `Map`, `Set`, `bigint`). Every adapter gains a `snapshots` table and `Storage.snapshotStore`, which keeps the newest snapshot of a fingerprint whatever order two writers put theirs in; `load` with `fromVersion` in the SQL event stores now reads the stream's head before the events, bounded by it, so an append landing between the two reads can no longer report a head past the events returned. diff --git a/.changeset/idempotent-commands.md b/.changeset/idempotent-commands.md deleted file mode 100644 index c0405a18..00000000 --- a/.changeset/idempotent-commands.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@bounda-dev/core": minor -"@bounda-dev/postgresql": minor -"@bounda-dev/react-router": minor ---- - -A command can be dispatched again without running twice: `commands.x(payload, { idempotencyKey })` -remembers what the first dispatch under a key answered, in the same transaction as the events it -stores, and every later dispatch with the same key, command, payload and `delay` answers the same, -its rejection included, without running the handler; two racing for one key store the events once. -The same key for another command, payload or `delay` rejects with `IdempotencyKeyConflictError` -(`IDEMPOTENCY_KEY_CONFLICT`). A key lives `runtime.commands.idempotencyKeyLifetime`, 24 hours by -default, in a new `bounda_idempotency_keys` table of every adapter, which the scheduled-command -worker purges. `Storage` and `StorageTransaction` gain an `idempotencyStore`, with its contract -test in `@bounda-dev/core/adapter/testing`. The React Router middleware gives every command of a request -a key derived from its `Idempotency-Key` header and the command's name, `idempotencyKeyFor(header, -name)`, so an action that dispatches two commands keys each apart, and `failure()` answers a -conflict with a 422. The in-memory adapter now answers, inside a transaction, the positions its events commit -with, as the SQL adapters do. diff --git a/.changeset/personal-data.md b/.changeset/personal-data.md deleted file mode 100644 index 7caf1f68..00000000 --- a/.changeset/personal-data.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@bounda-dev/core": minor -"@bounda-dev/cli": minor -"@bounda-dev/sqlite": minor -"@bounda-dev/postgresql": minor -"@bounda-dev/cloudflare": minor -"@bounda-dev/react-router": minor ---- - -Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 5e723a79..97b3cd46 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,19 @@ # @bounda-dev/cli +## 0.3.0 + +### Minor Changes + +- c9bff07: Snapshots of an aggregate's state, opt-in per aggregate with `runtime.overrides..snapshot: { every }`. A `snapshot:` subscriber writes them in the background, and a command loads the snapshot and folds only the events after it. A snapshot is a cache of the fold and never the source of truth: it is valid only for the `stateFingerprint` the generator now writes to the registry, a digest of the modules that shape the state and of the type inferred for it, plus the configured `snapshot.version`; one that is missing, from other code, unreadable or past the stream's head is ignored, and a share of the loads (`snapshot.verify`, `0.01` by default) is checked against a fold of the whole stream, which drops a snapshot that folds to another state. An app from `createTestApp` checks every load and throws `SnapshotError`. The state is kept with its types (`Date`, `Map`, `Set`, `bigint`). Every adapter gains a `snapshots` table and `Storage.snapshotStore`, which keeps the newest snapshot of a fingerprint whatever order two writers put theirs in; `load` with `fromVersion` in the SQL event stores now reads the stream's head before the events, bounded by it, so an append landing between the two reads can no longer report a head past the events returned. +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + +### Patch Changes + +- Updated dependencies [c9bff07] +- Updated dependencies [85b68d2] +- Updated dependencies [699bb9f] + - @bounda-dev/core@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index ee5c458f..8290c946 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/cli", - "version": "0.2.4", + "version": "0.3.0", "description": "The bounda CLI: reads the project layout and writes the registry and the types", "license": "Apache-2.0", "repository": { diff --git a/packages/cloudflare/CHANGELOG.md b/packages/cloudflare/CHANGELOG.md index bd4917c4..8d41370d 100644 --- a/packages/cloudflare/CHANGELOG.md +++ b/packages/cloudflare/CHANGELOG.md @@ -1,5 +1,18 @@ # @bounda-dev/cloudflare +## 0.3.0 + +### Minor Changes + +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + +### Patch Changes + +- Updated dependencies [c9bff07] +- Updated dependencies [85b68d2] +- Updated dependencies [699bb9f] + - @bounda-dev/core@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index 010b1856..31d37575 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/cloudflare", - "version": "0.2.4", + "version": "0.3.0", "description": "Cloudflare Durable Objects storage adapter for Bounda", "license": "Apache-2.0", "repository": { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 740f9c34..e8fd8526 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,25 @@ # @bounda-dev/core +## 0.3.0 + +### Minor Changes + +- c9bff07: Snapshots of an aggregate's state, opt-in per aggregate with `runtime.overrides..snapshot: { every }`. A `snapshot:` subscriber writes them in the background, and a command loads the snapshot and folds only the events after it. A snapshot is a cache of the fold and never the source of truth: it is valid only for the `stateFingerprint` the generator now writes to the registry, a digest of the modules that shape the state and of the type inferred for it, plus the configured `snapshot.version`; one that is missing, from other code, unreadable or past the stream's head is ignored, and a share of the loads (`snapshot.verify`, `0.01` by default) is checked against a fold of the whole stream, which drops a snapshot that folds to another state. An app from `createTestApp` checks every load and throws `SnapshotError`. The state is kept with its types (`Date`, `Map`, `Set`, `bigint`). Every adapter gains a `snapshots` table and `Storage.snapshotStore`, which keeps the newest snapshot of a fingerprint whatever order two writers put theirs in; `load` with `fromVersion` in the SQL event stores now reads the stream's head before the events, bounded by it, so an append landing between the two reads can no longer report a head past the events returned. +- 85b68d2: A command can be dispatched again without running twice: `commands.x(payload, { idempotencyKey })` + remembers what the first dispatch under a key answered, in the same transaction as the events it + stores, and every later dispatch with the same key, command, payload and `delay` answers the same, + its rejection included, without running the handler; two racing for one key store the events once. + The same key for another command, payload or `delay` rejects with `IdempotencyKeyConflictError` + (`IDEMPOTENCY_KEY_CONFLICT`). A key lives `runtime.commands.idempotencyKeyLifetime`, 24 hours by + default, in a new `bounda_idempotency_keys` table of every adapter, which the scheduled-command + worker purges. `Storage` and `StorageTransaction` gain an `idempotencyStore`, with its contract + test in `@bounda-dev/core/adapter/testing`. The React Router middleware gives every command of a request + a key derived from its `Idempotency-Key` header and the command's name, `idempotencyKeyFor(header, + name)`, so an action that dispatches two commands keys each apart, and `failure()` answers a + conflict with a 422. The in-memory adapter now answers, inside a transaction, the positions its events commit + with, as the SQL adapters do. +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + ## 0.2.4 No changes in this release. diff --git a/packages/core/package.json b/packages/core/package.json index fcb7675f..6a7b317b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/core", - "version": "0.2.4", + "version": "0.3.0", "description": "Bounda runtime: aggregates, commands, events, policies, processes, read models", "license": "Apache-2.0", "repository": { diff --git a/packages/create-bounda/CHANGELOG.md b/packages/create-bounda/CHANGELOG.md index e351c83f..bb0cdc2a 100644 --- a/packages/create-bounda/CHANGELOG.md +++ b/packages/create-bounda/CHANGELOG.md @@ -1,5 +1,9 @@ # create-bounda +## 0.3.0 + +No changes in this release. + ## 0.2.4 No changes in this release. diff --git a/packages/create-bounda/package.json b/packages/create-bounda/package.json index 5be4a33b..476cdf0e 100644 --- a/packages/create-bounda/package.json +++ b/packages/create-bounda/package.json @@ -1,6 +1,6 @@ { "name": "create-bounda", - "version": "0.2.4", + "version": "0.3.0", "description": "Scaffold a new Bounda project", "license": "Apache-2.0", "repository": { diff --git a/packages/postgresql/CHANGELOG.md b/packages/postgresql/CHANGELOG.md index 5a8c4fb7..cace3035 100644 --- a/packages/postgresql/CHANGELOG.md +++ b/packages/postgresql/CHANGELOG.md @@ -1,5 +1,32 @@ # @bounda-dev/postgresql +## 0.3.0 + +### Minor Changes + +- c9bff07: Snapshots of an aggregate's state, opt-in per aggregate with `runtime.overrides..snapshot: { every }`. A `snapshot:` subscriber writes them in the background, and a command loads the snapshot and folds only the events after it. A snapshot is a cache of the fold and never the source of truth: it is valid only for the `stateFingerprint` the generator now writes to the registry, a digest of the modules that shape the state and of the type inferred for it, plus the configured `snapshot.version`; one that is missing, from other code, unreadable or past the stream's head is ignored, and a share of the loads (`snapshot.verify`, `0.01` by default) is checked against a fold of the whole stream, which drops a snapshot that folds to another state. An app from `createTestApp` checks every load and throws `SnapshotError`. The state is kept with its types (`Date`, `Map`, `Set`, `bigint`). Every adapter gains a `snapshots` table and `Storage.snapshotStore`, which keeps the newest snapshot of a fingerprint whatever order two writers put theirs in; `load` with `fromVersion` in the SQL event stores now reads the stream's head before the events, bounded by it, so an append landing between the two reads can no longer report a head past the events returned. +- 85b68d2: A command can be dispatched again without running twice: `commands.x(payload, { idempotencyKey })` + remembers what the first dispatch under a key answered, in the same transaction as the events it + stores, and every later dispatch with the same key, command, payload and `delay` answers the same, + its rejection included, without running the handler; two racing for one key store the events once. + The same key for another command, payload or `delay` rejects with `IdempotencyKeyConflictError` + (`IDEMPOTENCY_KEY_CONFLICT`). A key lives `runtime.commands.idempotencyKeyLifetime`, 24 hours by + default, in a new `bounda_idempotency_keys` table of every adapter, which the scheduled-command + worker purges. `Storage` and `StorageTransaction` gain an `idempotencyStore`, with its contract + test in `@bounda-dev/core/adapter/testing`. The React Router middleware gives every command of a request + a key derived from its `Idempotency-Key` header and the command's name, `idempotencyKeyFor(header, + name)`, so an action that dispatches two commands keys each apart, and `failure()` answers a + conflict with a 422. The in-memory adapter now answers, inside a transaction, the positions its events commit + with, as the SQL adapters do. +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + +### Patch Changes + +- Updated dependencies [c9bff07] +- Updated dependencies [85b68d2] +- Updated dependencies [699bb9f] + - @bounda-dev/core@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/packages/postgresql/package.json b/packages/postgresql/package.json index c02773b2..239a5247 100644 --- a/packages/postgresql/package.json +++ b/packages/postgresql/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/postgresql", - "version": "0.2.4", + "version": "0.3.0", "description": "PostgreSQL storage adapter for Bounda", "license": "Apache-2.0", "repository": { diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 318679a6..2ea7b777 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,33 @@ # @bounda-dev/react-router +## 0.3.0 + +### Minor Changes + +- 85b68d2: A command can be dispatched again without running twice: `commands.x(payload, { idempotencyKey })` + remembers what the first dispatch under a key answered, in the same transaction as the events it + stores, and every later dispatch with the same key, command, payload and `delay` answers the same, + its rejection included, without running the handler; two racing for one key store the events once. + The same key for another command, payload or `delay` rejects with `IdempotencyKeyConflictError` + (`IDEMPOTENCY_KEY_CONFLICT`). A key lives `runtime.commands.idempotencyKeyLifetime`, 24 hours by + default, in a new `bounda_idempotency_keys` table of every adapter, which the scheduled-command + worker purges. `Storage` and `StorageTransaction` gain an `idempotencyStore`, with its contract + test in `@bounda-dev/core/adapter/testing`. The React Router middleware gives every command of a request + a key derived from its `Idempotency-Key` header and the command's name, `idempotencyKeyFor(header, + name)`, so an action that dispatches two commands keys each apart, and `failure()` answers a + conflict with a 422. The in-memory adapter now answers, inside a transaction, the positions its events commit + with, as the SQL adapters do. +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + +### Patch Changes + +- Updated dependencies [c9bff07] +- Updated dependencies [85b68d2] +- Updated dependencies [699bb9f] + - @bounda-dev/core@0.3.0 + - @bounda-dev/cli@0.3.0 + - @bounda-dev/cloudflare@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 372d1381..330af509 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/react-router", - "version": "0.2.4", + "version": "0.3.0", "description": "React Router framework-mode integration for Bounda", "license": "Apache-2.0", "repository": { diff --git a/packages/sqlite/CHANGELOG.md b/packages/sqlite/CHANGELOG.md index 0cbfaa94..2c07f4f9 100644 --- a/packages/sqlite/CHANGELOG.md +++ b/packages/sqlite/CHANGELOG.md @@ -1,5 +1,18 @@ # @bounda-dev/sqlite +## 0.3.0 + +### Minor Changes + +- 699bb9f: Personal data in events, forgotten by crypto-shredding. An event's `payload` receives `personal` and `person` next to `z`: `personal(schema)` marks a field as personal data, stored encrypted (AES-256-GCM through WebCrypto, on Node and in `workerd`) with a key of the person the event names, `person(schema)` marks the field that names them, and without it the aggregate's id does. `app.forget(personId)`, on every `BoundaClient`, deletes the key and appends `PersonalDataForgotten` to the stream `bounda:`, in one transaction: from then on every personal field of theirs reads as `null`, typed `T | null` in every handler. A read model that projects an event with personal fields must handle it from `projections/bounda/personal-data-forgotten.ts`, which the generator checks in `.bounda/types.ts`; a policy may react to it from `policies/bounda/`. The adapters keep the keys, with their fingerprints, in a new `personal_keys` table, `Storage` and `StorageTransaction` gain `personalKeys`, and the storage contracts cover it. The global stream shows the fields as stored, encrypted. A command that loaded a person's data before `forget` and appends after it fails with `ConcurrencyError` and is decided again, instead of writing their data back under the new key another command may have given them meanwhile: the state remembers the key each person's data was read under, and `AppendArgs` gains `persons` (`{ person, fingerprint }`) for this, which a store ignores. Snapshots and remembered rejections name the same and are written only while that key is still the person's; in PostgreSQL a GIN index on their `persons` lets `forget` find them without reading every row. + +### Patch Changes + +- Updated dependencies [c9bff07] +- Updated dependencies [85b68d2] +- Updated dependencies [699bb9f] + - @bounda-dev/core@0.3.0 + ## 0.2.4 ### Patch Changes diff --git a/packages/sqlite/package.json b/packages/sqlite/package.json index 5d6d2167..1eeac30e 100644 --- a/packages/sqlite/package.json +++ b/packages/sqlite/package.json @@ -1,6 +1,6 @@ { "name": "@bounda-dev/sqlite", - "version": "0.2.4", + "version": "0.3.0", "description": "SQLite and libSQL storage adapter for Bounda", "license": "Apache-2.0", "repository": {