@antelopejs/interface-database 0.1.4 → 0.1.6
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/dist/antelope.test.js +2 -2
- package/dist/antelope.test.js.map +1 -1
- package/dist/atomic.d.ts +41 -0
- package/dist/atomic.js +135 -0
- package/dist/atomic.js.map +1 -0
- package/dist/common.js.map +1 -1
- package/dist/datum.d.ts +2 -2
- package/dist/datum.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/query.js +1 -1
- package/dist/query.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +4 -1
- package/dist/selection.js +7 -0
- package/dist/selection.js.map +1 -1
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js.map +1 -1
- package/dist/tests/aggregation_operations.test.js +1 -1
- package/dist/tests/aggregation_operations.test.js.map +1 -1
- package/dist/tests/atomic_mutation.test.d.ts +1 -0
- package/dist/tests/atomic_mutation.test.js +185 -0
- package/dist/tests/atomic_mutation.test.js.map +1 -0
- package/dist/tests/atomic_mutation_operations.test.d.ts +1 -0
- package/dist/tests/atomic_mutation_operations.test.js +278 -0
- package/dist/tests/atomic_mutation_operations.test.js.map +1 -0
- package/dist/tests/basic_operations.test.js +1 -1
- package/dist/tests/basic_operations.test.js.map +1 -1
- package/dist/tests/between_operations.test.js +1 -1
- package/dist/tests/between_operations.test.js.map +1 -1
- package/dist/tests/change_feeds.test.js +1 -1
- package/dist/tests/change_feeds.test.js.map +1 -1
- package/dist/tests/datasets/codec_fields.js.map +1 -1
- package/dist/tests/date_operations.test.js +1 -1
- package/dist/tests/date_operations.test.js.map +1 -1
- package/dist/tests/do_operations.test.js +1 -1
- package/dist/tests/do_operations.test.js.map +1 -1
- package/dist/tests/filters.test.js +1 -1
- package/dist/tests/filters.test.js.map +1 -1
- package/dist/tests/group_operations.test.js +1 -1
- package/dist/tests/group_operations.test.js.map +1 -1
- package/dist/tests/instance_modes.test.js +1 -1
- package/dist/tests/instance_modes.test.js.map +1 -1
- package/dist/tests/joins.test.js +2 -2
- package/dist/tests/joins.test.js.map +1 -1
- package/dist/tests/lookup_operations.test.js +2 -2
- package/dist/tests/lookup_operations.test.js.map +1 -1
- package/dist/tests/merge_operations.test.js +2 -2
- package/dist/tests/merge_operations.test.js.map +1 -1
- package/dist/tests/sorting.test.js +1 -1
- package/dist/tests/sorting.test.js.map +1 -1
- package/dist/tests/union_operations.test.js +2 -2
- package/dist/tests/union_operations.test.js.map +1 -1
- package/docs/1.query_types/2.table.md +10 -4
- package/docs/1.query_types/3.selection.md +15 -17
- package/docs/1.query_types/5.stream.md +2 -5
- package/docs/1.query_types/6.feed.md +1 -1
- package/docs/1.query_types/9.valueproxy.md +43 -41
- package/docs/2.operations/2.database_management.md +2 -2
- package/docs/2.operations/3.table_operations.md +1 -1
- package/docs/2.operations/4.crud.md +1 -2
- package/docs/2.operations/5.indexes.md +4 -4
- package/docs/2.operations/6.filtering.md +23 -27
- package/docs/2.operations/7.lookup.md +13 -15
- package/docs/2.operations/8.atomic_mutations.md +71 -0
- package/docs/3.results/3.index_changes.md +9 -5
- package/docs/3.results/4.table_changes.md +3 -3
- package/docs/3.results/6.value_changes.md +5 -5
- package/docs/index.md +1 -0
- package/package.json +55 -40
- package/skills/database-interface/SKILL.md +41 -7
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Atomic single-record mutations
|
|
2
|
+
|
|
3
|
+
`Table.atomicMutation(id, request)` checks one immutable, instance-scoped record identity and one condition inside the database mutation. It returns `Query<AtomicMutationOutcome>`. It does not provide multi-record transactions, a lock service, receipts, or external-effect coordination.
|
|
4
|
+
|
|
5
|
+
Generic `filter(...).update(...)` and `filter(...).delete()` do **not** promise this guarantee. Use a direct `schema.instance(instanceId).table(tableId)` with one literal string identity. The default instance is supported; `CROSS_INSTANCE`, filtered selections, expression keys, cursors, and upserts are not.
|
|
6
|
+
|
|
7
|
+
## Compare a revision
|
|
8
|
+
|
|
9
|
+
Public types are exported from `@antelopejs/interface-database` and `@antelopejs/interface-database/atomic`. An update checks the current revision and installs the patch and next revision together. A delete checks the revision inside the deletion.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const outcome = await table.atomicMutation(recordId, {
|
|
13
|
+
type: "update",
|
|
14
|
+
revisionField: "revision",
|
|
15
|
+
expectedRevision: observed.revision,
|
|
16
|
+
nextRevision: freshRevision,
|
|
17
|
+
patch: { title: "Updated title" },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const deletion = await table.atomicMutation(recordId, {
|
|
21
|
+
type: "delete",
|
|
22
|
+
revisionField: "revision",
|
|
23
|
+
expectedRevision: observed.revision,
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Revision tokens are nonempty strings. Each update must install a changed token that the caller guarantees has never been used for that identity. All writers of protected state must advance the revision; otherwise a successful comparison does not prove that the observed state is current. Initialize new records with fresh revisions and use a new identity for each incarnation, including recreation after deletion.
|
|
28
|
+
|
|
29
|
+
`expectedRevision: { kind: "missing" }` matches an **absent revision field on an existing record**. It does not match a stored `null`, an empty string, or a missing record. An update can use this expectation to bootstrap legacy data atomically; a delete can use it when the domain permits deleting an unversioned record. A backend that cannot distinguish absence from null must reject this expectation rather than weaken it. In PostgreSQL, declared SQL columns collapse this distinction; an overflow JSON field can preserve it. Review mapper serialization before choosing revision storage.
|
|
30
|
+
|
|
31
|
+
## Patch semantics
|
|
32
|
+
|
|
33
|
+
The patch replaces each supplied top-level field. Nested objects and arrays replace their entire previous values; they do not merge recursively. Unspecified fields remain unchanged, and `null` stores null rather than removing the field. There is no unset operation.
|
|
34
|
+
|
|
35
|
+
Patches contain only constant strings, booleans, finite numbers, null, valid Dates, dense arrays, and plain objects. Undefined, functions, query proxies, cycles, and operator/path field names are rejected. The patch cannot contain `id`, `_id`, the revision field, or adapter-owned scope fields. Adapters must preserve their normal Date storage representation without interpreting objects that resemble query stages as executable expressions.
|
|
36
|
+
|
|
37
|
+
## Delete by one observed scalar
|
|
38
|
+
|
|
39
|
+
`deleteIfEqual` provides a narrower condition for domains where one field equality is sufficient, such as deletion of an expired record whose observed timestamp remains unchanged.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const outcome = await table.atomicMutation(recordId, {
|
|
43
|
+
type: "deleteIfEqual",
|
|
44
|
+
field: "lastSeenAt",
|
|
45
|
+
expectedValue: observed.lastSeenAt,
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`expectedValue` accepts a string, finite number, boolean, or valid Date. Equality is exact and type-sensitive, with Dates compared by their stored instant; scalar equality must not match an array containing that scalar. Missing fields never match. Null, objects, arrays, identity fields, and scope fields are not supported.
|
|
50
|
+
|
|
51
|
+
This variant does not check or advance a revision and does not prevent ABA: a value can change and later return to its observed value. The domain must prove that equality alone authorizes deletion. It is not a substitute for revision checks on state transitions.
|
|
52
|
+
|
|
53
|
+
## Handle all outcomes
|
|
54
|
+
|
|
55
|
+
| Outcome | Meaning |
|
|
56
|
+
| ------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `applied` | The adapter received definitive acknowledgement that the one mutation applied. |
|
|
58
|
+
| `not-applied` | The adapter received definitive acknowledgement that the target was missing or its scope or condition did not match. |
|
|
59
|
+
| `unknown` | The adapter cannot determine whether the mutation applied, including acknowledgement loss. |
|
|
60
|
+
|
|
61
|
+
Definitive validation and unsupported-operation errors throw. Neither an exception nor `unknown` means mismatch. The interface rejects invalid adapter results, including `undefined`, zero, and generic modified counts, with `AtomicMutationUnsupportedError`; adapters must reject unsupported stages explicitly. There is no fallback to generic filtered writes and no separate capability probe. Use an adapter whose implementation has been verified before relying on this API.
|
|
62
|
+
|
|
63
|
+
The interface and adapter must not automatically retry an uncertain mutation. Do not repeat external effects or infer failure from a later mismatch. Domain-specific recovery can reconcile durable evidence and receipts, but a fresh read alone cannot prove whether an earlier operation or external effect occurred. Any deliberate replay requires a domain proof and does not turn `not-applied` into evidence about the original attempt.
|
|
64
|
+
|
|
65
|
+
## Adapter contract
|
|
66
|
+
|
|
67
|
+
The canonical terminal stage is `{ stage: "atomicMutation", options: undefined, args: [id, request] }`, immediately after `schema`, `instance`, and `table`. `AtomicMutation<T>` is the union of `AtomicUpdate<T>`, `AtomicDelete<T>`, and `AtomicDeleteIfEqual<T>`.
|
|
68
|
+
|
|
69
|
+
Adapters can reuse `ValidateAtomicMutation(id, request, protectedFields)` and `ValidateAtomicMutationTable(prefixStages)`. They must additionally validate the terminal envelope and schema-specific restrictions. Scope, immutable identity, and the revision or equality condition must participate in the target-local native mutation, not only an earlier selection or identity subquery. Backend tests must prove contention, scope isolation, missing/null distinction, top-level replacement, and acknowledgement-loss behavior.
|
|
70
|
+
|
|
71
|
+
This API remains unpublished during adapter and consumer verification. Package metadata alone does not establish capability; do not infer support from the unchanged development package version.
|
|
@@ -6,8 +6,8 @@ In AQL, indexes are defined as part of the schema definition rather than created
|
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
interface IndexDefinition {
|
|
9
|
-
fields?: string[];
|
|
10
|
-
multi?: boolean;
|
|
9
|
+
fields?: string[]; // Fields for compound indexes
|
|
10
|
+
multi?: boolean; // Multi-valued index
|
|
11
11
|
}
|
|
12
12
|
```
|
|
13
13
|
|
|
@@ -22,8 +22,8 @@ const schema = new Schema("myapp", {
|
|
|
22
22
|
users: {
|
|
23
23
|
fields: { name: "string", email: "string", role: "string" },
|
|
24
24
|
indexes: {
|
|
25
|
-
email: {},
|
|
26
|
-
role: {},
|
|
25
|
+
email: {}, // Simple index (field name = index name)
|
|
26
|
+
role: {}, // Simple index
|
|
27
27
|
name_role: { fields: ["name", "role"] }, // Compound index
|
|
28
28
|
},
|
|
29
29
|
},
|
|
@@ -72,7 +72,11 @@ const users = db.table("users");
|
|
|
72
72
|
const admins = await users.getAll("admin", "role");
|
|
73
73
|
|
|
74
74
|
// Range query on an indexed field
|
|
75
|
-
const recent = await users.between(
|
|
75
|
+
const recent = await users.between(
|
|
76
|
+
"createdAt",
|
|
77
|
+
new Date("2024-01-01"),
|
|
78
|
+
new Date(),
|
|
79
|
+
);
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
For more details, see [Index Management](../2.operations/5.indexes.md).
|
|
@@ -17,10 +17,10 @@ Field types describe the structure of each field:
|
|
|
17
17
|
|
|
18
18
|
```typescript
|
|
19
19
|
type FieldType =
|
|
20
|
-
| string
|
|
21
|
-
| Array<StringFieldType>
|
|
20
|
+
| string // Primitive type name (e.g., "string", "number")
|
|
21
|
+
| Array<StringFieldType> // Array of a given type
|
|
22
22
|
| { [subfield: string]: StringFieldType } // Nested object
|
|
23
|
-
| import("io-ts").Mixed;
|
|
23
|
+
| import("io-ts").Mixed; // io-ts codec at the field root
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
A field can be described either with the string-token vocabulary (primitives, arrays, nested records) or with a single [io-ts](https://github.com/gcanti/io-ts) codec at the root of the field. Codecs are not mixed inside string-tree records — use io-ts combinators (`t.type`, `t.array`, …) to nest within a codec.
|
|
@@ -14,11 +14,11 @@ interface Changes<T> {
|
|
|
14
14
|
|
|
15
15
|
## Properties
|
|
16
16
|
|
|
17
|
-
| Property | Type
|
|
18
|
-
| ------------ |
|
|
19
|
-
| `changeType` | `"added" \| "removed" \| "modified"`
|
|
20
|
-
| `oldValue` | `T \| undefined`
|
|
21
|
-
| `newValue` | `T \| undefined`
|
|
17
|
+
| Property | Type | Description |
|
|
18
|
+
| ------------ | ------------------------------------ | ------------------------------------------------------ |
|
|
19
|
+
| `changeType` | `"added" \| "removed" \| "modified"` | The kind of change that occurred. |
|
|
20
|
+
| `oldValue` | `T \| undefined` | The document before the change. Undefined for inserts. |
|
|
21
|
+
| `newValue` | `T \| undefined` | The document after the change. Undefined for deletes. |
|
|
22
22
|
|
|
23
23
|
## Determine the Change Type
|
|
24
24
|
|
package/docs/index.md
CHANGED
|
@@ -25,6 +25,7 @@ The Database interface provides AQL (Antelope Query Language), a database-agnost
|
|
|
25
25
|
- [Index Management](./2.operations/5.indexes.md) - Define and use secondary indexes
|
|
26
26
|
- [Filtering and Querying](./2.operations/6.filtering.md) - Filter, compare, and transform data
|
|
27
27
|
- [Lookup](./2.operations/7.lookup.md) - Foreign key joins and data population
|
|
28
|
+
- [Atomic Mutations](./2.operations/8.atomic_mutations.md) - Conditional single-record mutations and outcomes
|
|
28
29
|
|
|
29
30
|
- [**3. Results**](./3.results/1.index.md) - Result types and handling:
|
|
30
31
|
- [Insert Results](./3.results/2.write_results.md) - Results from insert operations
|
package/package.json
CHANGED
|
@@ -1,30 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-database",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "AntelopeJS interface for database-agnostic query operations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"database",
|
|
8
|
+
"interface"
|
|
9
9
|
],
|
|
10
|
+
"license": "Apache-2.0",
|
|
10
11
|
"repository": {
|
|
11
12
|
"type": "git",
|
|
12
13
|
"url": "git+https://github.com/AntelopeJS/interface-database.git"
|
|
13
14
|
},
|
|
14
|
-
"license": "Apache-2.0",
|
|
15
|
-
"packageManager": "pnpm@10.6.5",
|
|
16
|
-
"main": "dist/index.js",
|
|
17
|
-
"types": "dist/index.d.ts",
|
|
18
15
|
"files": [
|
|
19
16
|
"dist",
|
|
20
17
|
"docs",
|
|
21
18
|
"skills"
|
|
22
19
|
],
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"typesVersions": {
|
|
23
|
+
"*": {
|
|
24
|
+
"atomic": [
|
|
25
|
+
"dist/atomic.d.ts"
|
|
26
|
+
],
|
|
27
|
+
"common": [
|
|
28
|
+
"dist/common.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"datum": [
|
|
31
|
+
"dist/datum.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"query": [
|
|
34
|
+
"dist/query.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"schema": [
|
|
37
|
+
"dist/schema.d.ts"
|
|
38
|
+
],
|
|
39
|
+
"selection": [
|
|
40
|
+
"dist/selection.d.ts"
|
|
41
|
+
],
|
|
42
|
+
"stream": [
|
|
43
|
+
"dist/stream.d.ts"
|
|
44
|
+
],
|
|
45
|
+
"valueproxy": [
|
|
46
|
+
"dist/valueproxy.d.ts"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
23
50
|
"exports": {
|
|
24
51
|
".": {
|
|
25
52
|
"types": "./dist/index.d.ts",
|
|
26
53
|
"default": "./dist/index.js"
|
|
27
54
|
},
|
|
55
|
+
"./atomic": {
|
|
56
|
+
"types": "./dist/atomic.d.ts",
|
|
57
|
+
"default": "./dist/atomic.js"
|
|
58
|
+
},
|
|
28
59
|
"./common": {
|
|
29
60
|
"types": "./dist/common.d.ts",
|
|
30
61
|
"default": "./dist/common.js"
|
|
@@ -55,26 +86,37 @@
|
|
|
55
86
|
},
|
|
56
87
|
"./package.json": "./package.json"
|
|
57
88
|
},
|
|
89
|
+
"publishConfig": {
|
|
90
|
+
"access": "public",
|
|
91
|
+
"provenance": true
|
|
92
|
+
},
|
|
58
93
|
"scripts": {
|
|
59
94
|
"build": "rimraf dist && tsc",
|
|
60
|
-
"format": "
|
|
61
|
-
"lint:fix": "
|
|
62
|
-
"lint": "
|
|
95
|
+
"format": "oxfmt .",
|
|
96
|
+
"lint:fix": "oxlint --fix",
|
|
97
|
+
"lint": "oxlint",
|
|
63
98
|
"prepack": "pnpm run build",
|
|
64
99
|
"release": "pnpm run lint && pnpm run prepack && release-it",
|
|
65
|
-
"test": "pnpm run build && ajs module test ."
|
|
100
|
+
"test": "pnpm run build && ajs module test .",
|
|
101
|
+
"format:check": "oxfmt --check .",
|
|
102
|
+
"knip": "knip"
|
|
66
103
|
},
|
|
67
104
|
"devDependencies": {
|
|
68
105
|
"@antelopejs/interface-core": ">=0.0.3 <1.0.0",
|
|
69
|
-
"@
|
|
106
|
+
"@antelopejs/tooling-configs": "^0.0.4",
|
|
70
107
|
"@types/chai": "^4.3.20",
|
|
71
108
|
"@types/mocha": "^10.0.10",
|
|
72
109
|
"@types/node": "^22.19.15",
|
|
73
110
|
"@types/semver": "^7.7.1",
|
|
74
111
|
"chai": "^4.5.0",
|
|
112
|
+
"eslint-plugin-perfectionist": "^5.11.0",
|
|
75
113
|
"fp-ts": "^2.16.0",
|
|
76
114
|
"io-ts": "^2.2.0",
|
|
115
|
+
"knip": "^6.34.0",
|
|
77
116
|
"mongodb-memory-server-core": "^11.0.1",
|
|
117
|
+
"oxfmt": "^0.66.0",
|
|
118
|
+
"oxlint": "1.81.0",
|
|
119
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
78
120
|
"release-it": "^19.2.4",
|
|
79
121
|
"release-it-changelogen": "^0.1.0",
|
|
80
122
|
"rimraf": "^6.1.3",
|
|
@@ -83,34 +125,7 @@
|
|
|
83
125
|
"peerDependencies": {
|
|
84
126
|
"@antelopejs/interface-core": ">=0.0.3 <1.0.0"
|
|
85
127
|
},
|
|
86
|
-
"
|
|
87
|
-
"access": "public"
|
|
88
|
-
},
|
|
89
|
-
"typesVersions": {
|
|
90
|
-
"*": {
|
|
91
|
-
"common": [
|
|
92
|
-
"dist/common.d.ts"
|
|
93
|
-
],
|
|
94
|
-
"datum": [
|
|
95
|
-
"dist/datum.d.ts"
|
|
96
|
-
],
|
|
97
|
-
"query": [
|
|
98
|
-
"dist/query.d.ts"
|
|
99
|
-
],
|
|
100
|
-
"schema": [
|
|
101
|
-
"dist/schema.d.ts"
|
|
102
|
-
],
|
|
103
|
-
"selection": [
|
|
104
|
-
"dist/selection.d.ts"
|
|
105
|
-
],
|
|
106
|
-
"stream": [
|
|
107
|
-
"dist/stream.d.ts"
|
|
108
|
-
],
|
|
109
|
-
"valueproxy": [
|
|
110
|
-
"dist/valueproxy.d.ts"
|
|
111
|
-
]
|
|
112
|
-
}
|
|
113
|
-
},
|
|
128
|
+
"packageManager": "pnpm@10.6.5",
|
|
114
129
|
"antelopeJs": {
|
|
115
130
|
"test": "src/antelope.test.ts",
|
|
116
131
|
"skills": [
|
|
@@ -13,10 +13,34 @@ AQL is a lazy, chainable query builder. Every class here (`Schema`, `Table`, `Se
|
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
15
|
// Root re-exports everything most consumers need:
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
Schema,
|
|
18
|
+
SchemaInstance,
|
|
19
|
+
SchemaDefinition,
|
|
20
|
+
Table,
|
|
21
|
+
Selection,
|
|
22
|
+
SingleSelection,
|
|
23
|
+
Stream,
|
|
24
|
+
Datum,
|
|
25
|
+
Query,
|
|
26
|
+
ValueProxy,
|
|
27
|
+
ValueProxyOrValue,
|
|
28
|
+
InstanceId,
|
|
29
|
+
CROSS_INSTANCE,
|
|
30
|
+
} from "@antelopejs/interface-database";
|
|
17
31
|
// Types only available via subpath (not re-exported at root):
|
|
18
|
-
import {
|
|
19
|
-
|
|
32
|
+
import {
|
|
33
|
+
TableDefinition,
|
|
34
|
+
IndexDefinition,
|
|
35
|
+
FieldType,
|
|
36
|
+
} from "@antelopejs/interface-database/schema";
|
|
37
|
+
import {
|
|
38
|
+
Changes,
|
|
39
|
+
DeepPartial,
|
|
40
|
+
InsertOptions,
|
|
41
|
+
Value,
|
|
42
|
+
ExtractType,
|
|
43
|
+
} from "@antelopejs/interface-database/common";
|
|
20
44
|
// Each root symbol is also importable from its own subpath (/schema, /selection, /stream, /datum, /query, /valueproxy) — equivalent alternatives, do not combine with the root import for the same symbol.
|
|
21
45
|
```
|
|
22
46
|
|
|
@@ -27,7 +51,12 @@ Add `@antelopejs/interface-database` to the module's `dependencies`; `@antelopej
|
|
|
27
51
|
```ts
|
|
28
52
|
import { Schema } from "@antelopejs/interface-database";
|
|
29
53
|
|
|
30
|
-
interface Novel {
|
|
54
|
+
interface Novel {
|
|
55
|
+
_id?: string;
|
|
56
|
+
title: string;
|
|
57
|
+
pageCount: number;
|
|
58
|
+
available: boolean;
|
|
59
|
+
}
|
|
31
60
|
|
|
32
61
|
// Registers the schema with the provider. The generic maps table names to row types.
|
|
33
62
|
const schema = new Schema<{ novels: Novel }>("library", {
|
|
@@ -39,12 +68,17 @@ const schema = new Schema<{ novels: Novel }>("library", {
|
|
|
39
68
|
|
|
40
69
|
const novels = schema.instance().table("novels"); // default instance
|
|
41
70
|
|
|
42
|
-
const ids = await novels.insert({
|
|
71
|
+
const ids = await novels.insert({
|
|
72
|
+
title: "The Glass Meridian",
|
|
73
|
+
pageCount: 412,
|
|
74
|
+
available: true,
|
|
75
|
+
}); // string[] of inserted ids
|
|
43
76
|
const longNovels = await novels
|
|
44
77
|
.filter((n) => n.key("pageCount").ge(300).and(n.key("available")))
|
|
45
|
-
.orderBy("pageCount", "desc");
|
|
78
|
+
.orderBy("pageCount", "desc"); // await executes: Novel[]
|
|
46
79
|
await novels.get(ids[0]).update({ available: false }); // number of modified docs
|
|
47
|
-
for await (const change of novels.changes()) {
|
|
80
|
+
for await (const change of novels.changes()) {
|
|
81
|
+
// change feed via async iteration
|
|
48
82
|
console.log(change.changeType, change.newValue); // "added" | "removed" | "modified"
|
|
49
83
|
}
|
|
50
84
|
```
|