@antelopejs/interface-database 0.1.8 → 0.1.10
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/README.md +89 -14
- package/dist/datum.d.ts +1 -1
- package/dist/datum.js +2 -1
- package/dist/datum.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/query.d.ts +1 -0
- package/dist/query.js +3 -1
- package/dist/query.js.map +1 -1
- package/dist/staged-query/datum.d.ts +10 -0
- package/dist/staged-query/datum.js +16 -0
- package/dist/staged-query/datum.js.map +1 -1
- package/dist/staged-query/index.d.ts +4 -4
- package/dist/staged-query/index.js +5 -1
- package/dist/staged-query/index.js.map +1 -1
- package/dist/staged-query/kind.d.ts +2 -0
- package/dist/staged-query/kind.js +13 -0
- package/dist/staged-query/kind.js.map +1 -0
- package/dist/staged-query/query.d.ts +10 -0
- package/dist/staged-query/query.js +16 -0
- package/dist/staged-query/query.js.map +1 -1
- package/dist/staged-query/schema.js +1 -1
- package/dist/staged-query/schema.js.map +1 -1
- package/dist/staged-query/stream.d.ts +10 -0
- package/dist/staged-query/stream.js +16 -0
- package/dist/staged-query/stream.js.map +1 -1
- package/dist/staged-query/valueproxy.d.ts +10 -0
- package/dist/staged-query/valueproxy.js +16 -0
- package/dist/staged-query/valueproxy.js.map +1 -1
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js +2 -1
- package/dist/stream.js.map +1 -1
- package/dist/tests/cross_instance.test.d.ts +1 -0
- package/dist/tests/cross_instance.test.js +80 -0
- package/dist/tests/cross_instance.test.js.map +1 -0
- package/dist/tests/helpers/other-instance.d.ts +13 -0
- package/dist/tests/helpers/other-instance.js +46 -0
- package/dist/tests/helpers/other-instance.js.map +1 -0
- package/docs/1.query_types/1.index.md +14 -0
- package/docs/1.query_types/2.table.md +8 -0
- package/docs/1.query_types/4.single_selection.md +3 -3
- package/docs/1.query_types/5.stream.md +5 -1
- package/docs/1.query_types/6.feed.md +29 -22
- package/docs/1.query_types/7.query.md +2 -0
- package/docs/1.query_types/8.datum.md +8 -5
- package/docs/1.query_types/9.valueproxy.md +2 -0
- package/docs/2.operations/1.index.md +1 -0
- package/docs/2.operations/2.database_management.md +7 -4
- package/docs/2.operations/3.table_operations.md +28 -5
- package/docs/2.operations/7.lookup.md +16 -9
- package/docs/3.results/4.table_changes.md +3 -2
- package/docs/3.results/6.value_changes.md +2 -6
- package/docs/index.md +8 -0
- package/docs/tsconfig.json +14 -0
- package/package.json +13 -7
- package/skills/database-interface/SKILL.md +5 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
40
|
+
const chai_1 = require("chai");
|
|
41
|
+
const Database = __importStar(require("../staged-query"));
|
|
42
|
+
const other_instance_1 = require("./helpers/other-instance");
|
|
43
|
+
const buildFolder = node_path_1.default.resolve(__dirname, "..");
|
|
44
|
+
const otherInstance = (0, other_instance_1.CreateOtherInstance)([buildFolder]);
|
|
45
|
+
const Other = otherInstance(node_path_1.default.join(buildFolder, "staged-query", "index.js"));
|
|
46
|
+
function OtherTable() {
|
|
47
|
+
return new Other.Schema("cross-instance-other", {
|
|
48
|
+
items: { fields: {}, indexes: {} },
|
|
49
|
+
})
|
|
50
|
+
.instance()
|
|
51
|
+
.table("items");
|
|
52
|
+
}
|
|
53
|
+
describe("Objects built by another interface instance", () => {
|
|
54
|
+
it("IsStream, IsDatum, IsQuery and IsValueProxy recognise them (data-api picks its query path with them)", PredicatesRecogniseOtherInstance);
|
|
55
|
+
it("are not instanceof this instance's classes (providers refuse sub-queries from another binding with it)", InstanceofStaysPerInstance);
|
|
56
|
+
it("share CROSS_INSTANCE with this instance (providers compare it with ===)", CrossInstanceIsShared);
|
|
57
|
+
});
|
|
58
|
+
function PredicatesRecogniseOtherInstance() {
|
|
59
|
+
const otherTable = OtherTable();
|
|
60
|
+
const otherDatum = otherTable.get("id");
|
|
61
|
+
const otherValue = Other.ValueProxy.constant(1);
|
|
62
|
+
(0, chai_1.expect)(Other.Stream).to.not.equal(Database.Stream);
|
|
63
|
+
(0, chai_1.expect)(Database.IsStream(otherTable)).to.equal(true);
|
|
64
|
+
(0, chai_1.expect)(Database.IsQuery(otherTable)).to.equal(true);
|
|
65
|
+
(0, chai_1.expect)(Database.IsDatum(otherTable)).to.equal(false);
|
|
66
|
+
(0, chai_1.expect)(Database.IsDatum(otherDatum)).to.equal(true);
|
|
67
|
+
(0, chai_1.expect)(Database.IsStream(otherDatum)).to.equal(false);
|
|
68
|
+
(0, chai_1.expect)(Database.IsValueProxy(otherValue)).to.equal(true);
|
|
69
|
+
(0, chai_1.expect)(Database.IsQuery(otherValue)).to.equal(false);
|
|
70
|
+
(0, chai_1.expect)(Database.IsStream({})).to.equal(false);
|
|
71
|
+
(0, chai_1.expect)(Database.IsStream(null)).to.equal(false);
|
|
72
|
+
}
|
|
73
|
+
function InstanceofStaysPerInstance() {
|
|
74
|
+
(0, chai_1.expect)(OtherTable() instanceof Database.Query).to.equal(false);
|
|
75
|
+
(0, chai_1.expect)(Other.ValueProxy.constant(1) instanceof Database.ValueProxy).to.equal(false);
|
|
76
|
+
}
|
|
77
|
+
function CrossInstanceIsShared() {
|
|
78
|
+
(0, chai_1.expect)(Other.CROSS_INSTANCE).to.equal(Database.CROSS_INSTANCE);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=cross_instance.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross_instance.test.js","sourceRoot":"","sources":["../../src/tests/cross_instance.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type ModuleRequire = (request: string) => unknown;
|
|
2
|
+
/**
|
|
3
|
+
* Evaluates built packages a second time, the way the core gives each
|
|
4
|
+
* provider binding its own copy of an interface: every file under `folders`
|
|
5
|
+
* is loaded again, with its own classes, symbols and registries, while
|
|
6
|
+
* anything else (interface-core, third-party dependencies) stays the copy
|
|
7
|
+
* already loaded.
|
|
8
|
+
*
|
|
9
|
+
* @param folders Build output folders to evaluate again
|
|
10
|
+
* @returns A `require` resolving into the second copy
|
|
11
|
+
*/
|
|
12
|
+
export declare function CreateOtherInstance(folders: string[]): ModuleRequire;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CreateOtherInstance = CreateOtherInstance;
|
|
7
|
+
const node_module_1 = __importDefault(require("node:module"));
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const internals = node_module_1.default;
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates built packages a second time, the way the core gives each
|
|
13
|
+
* provider binding its own copy of an interface: every file under `folders`
|
|
14
|
+
* is loaded again, with its own classes, symbols and registries, while
|
|
15
|
+
* anything else (interface-core, third-party dependencies) stays the copy
|
|
16
|
+
* already loaded.
|
|
17
|
+
*
|
|
18
|
+
* @param folders Build output folders to evaluate again
|
|
19
|
+
* @returns A `require` resolving into the second copy
|
|
20
|
+
*/
|
|
21
|
+
function CreateOtherInstance(folders) {
|
|
22
|
+
const owned = folders.map((folder) => (0, node_fs_1.realpathSync)(folder) + node_path_1.default.sep);
|
|
23
|
+
const modules = new Map();
|
|
24
|
+
const load = (filename, parent) => {
|
|
25
|
+
const cached = modules.get(filename);
|
|
26
|
+
if (cached) {
|
|
27
|
+
return cached.exports;
|
|
28
|
+
}
|
|
29
|
+
const loaded = new node_module_1.default(filename, parent);
|
|
30
|
+
loaded.filename = filename;
|
|
31
|
+
loaded.paths = internals._nodeModulePaths(node_path_1.default.dirname(filename));
|
|
32
|
+
loaded.require = ((request) => requireFrom(loaded, request));
|
|
33
|
+
modules.set(filename, loaded);
|
|
34
|
+
loaded.load(filename);
|
|
35
|
+
return loaded.exports;
|
|
36
|
+
};
|
|
37
|
+
const requireFrom = (parent, request) => {
|
|
38
|
+
const filename = internals._resolveFilename(request, parent);
|
|
39
|
+
const isOwned = owned.some((root) => filename.startsWith(root));
|
|
40
|
+
return isOwned
|
|
41
|
+
? load(filename, parent)
|
|
42
|
+
: node_module_1.default.prototype.require.call(parent, request);
|
|
43
|
+
};
|
|
44
|
+
return (request) => requireFrom(module, request);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=other-instance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"other-instance.js","sourceRoot":"","sources":["../../../src/tests/helpers/other-instance.ts"],"names":[],"mappings":";;;;;AA2BA,kDA4BC;AAvDD,8DAAiC;AACjC,qCAAuC;AACvC,0DAA6B;AAa7B,MAAM,SAAS,GAAG,qBAAoC,CAAC;AAEvD;;;;;;;;;GASG;AACH,SAAgB,mBAAmB,CAAC,OAAiB;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAA,sBAAY,EAAC,MAAM,CAAC,GAAG,mBAAI,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,MAAc,EAAW,EAAE;QACzD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,qBAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC5C,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC3B,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC,gBAAgB,CAAC,mBAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,OAAe,EAAE,EAAE,CACpC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAsB,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAoC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,OAAe,EAAW,EAAE;QAC/D,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAChE,OAAO,OAAO;YACZ,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC;YACxB,CAAC,CAAC,qBAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,OAAO,CAAC,OAAe,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -41,6 +41,20 @@ for await (const user of schema.instance().table("users")) {
|
|
|
41
41
|
}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
## Recognize a Query Type
|
|
45
|
+
|
|
46
|
+
`IsQuery`, `IsDatum`, `IsStream` and `IsValueProxy` tell what kind of staged object a value is:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { IsStream } from "@antelopejs/interface-database";
|
|
50
|
+
|
|
51
|
+
function describe(query: unknown) {
|
|
52
|
+
return IsStream(query) ? "a sequence of documents" : "a single value";
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Each provider binding of the interface has its own copy of these classes, so a query built by a module bound to another database is not an `instanceof` this module's `Stream`. The predicates recognize it whichever copy built it. Keep `instanceof` for checking that a query was built for this module's own binding.
|
|
57
|
+
|
|
44
58
|
## Available Query Types
|
|
45
59
|
|
|
46
60
|
- [**Table**](./2.table.md) - Represents a database table with insert, get, getAll, and between operations
|
|
@@ -6,6 +6,10 @@ The `Table<T>` class represents a database table and provides methods for insert
|
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
class Table<T> extends Selection<T> {
|
|
9
|
+
atomicMutation(
|
|
10
|
+
id: string,
|
|
11
|
+
request: AtomicMutation<T>,
|
|
12
|
+
): Query<AtomicMutationOutcome>;
|
|
9
13
|
insert(
|
|
10
14
|
obj: DeepPartial<T> | DeepPartial<T>[],
|
|
11
15
|
options?: InsertOptions,
|
|
@@ -74,6 +78,8 @@ const upsertIds = await users.insert(
|
|
|
74
78
|
|
|
75
79
|
Retrieve a single document by its primary key. Returns a `SingleSelection<T>`.
|
|
76
80
|
|
|
81
|
+
Awaiting this selection is declared as `T`, not `T | null`. The declaration does not prove a row exists; providers can return `null` for missing keys, so check before reading fields.
|
|
82
|
+
|
|
77
83
|
```typescript
|
|
78
84
|
const user = await users.get("user-123");
|
|
79
85
|
|
|
@@ -108,6 +114,8 @@ const recent = await users.between(
|
|
|
108
114
|
|
|
109
115
|
## Inherited Methods
|
|
110
116
|
|
|
117
|
+
For conditional single-record writes, see [Atomic Mutations](../2.operations/8.atomic_mutations.md). `atomicMutation()` is a table method, not a selection method; it returns an explicit outcome rather than a modified count.
|
|
118
|
+
|
|
111
119
|
Since `Table` extends `Selection`, which extends `Stream`, it inherits:
|
|
112
120
|
|
|
113
121
|
- **Selection methods**: `update()`, `replace()`, `delete()` -- see [Selection](./3.selection.md)
|
|
@@ -32,6 +32,8 @@ const user = users.get("user-123");
|
|
|
32
32
|
|
|
33
33
|
Since `SingleSelection` extends `Datum`, you can execute it directly to retrieve the document value:
|
|
34
34
|
|
|
35
|
+
The declared result is `T`, even though a provider can return `null` for a missing key. Check for absence before accessing the document; the generic type is not an existence guarantee.
|
|
36
|
+
|
|
35
37
|
```typescript
|
|
36
38
|
const user = await users.get("user-123");
|
|
37
39
|
|
|
@@ -98,9 +100,7 @@ if (count === 1) {
|
|
|
98
100
|
The `changes` method returns change events for the document. Each event contains a `changeType` and the `oldValue`/`newValue` of the document.
|
|
99
101
|
|
|
100
102
|
```typescript
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
for (const change of changes) {
|
|
103
|
+
for await (const change of users.get("user-123").changes()) {
|
|
104
104
|
console.log("Type:", change.changeType); // "added" | "removed" | "modified"
|
|
105
105
|
console.log("Old:", change.oldValue);
|
|
106
106
|
console.log("New:", change.newValue);
|
|
@@ -10,7 +10,9 @@ class Stream<T> extends Query<T[]> {
|
|
|
10
10
|
cast<U>(): Stream<U>;
|
|
11
11
|
|
|
12
12
|
// Field access
|
|
13
|
-
key<K extends keyof T>(key: K, def?:
|
|
13
|
+
key<K extends keyof T, U = undefined>(key: K, def?: U): Stream<
|
|
14
|
+
U extends undefined ? T[K] : Exclude<T[K], undefined | null> | U
|
|
15
|
+
>;
|
|
14
16
|
default<U>(val: Value<U>): Stream<Exclude<T, undefined | null> | U>;
|
|
15
17
|
|
|
16
18
|
// Transformation
|
|
@@ -53,6 +55,8 @@ class Stream<T> extends Query<T[]> {
|
|
|
53
55
|
|
|
54
56
|
Access a specific field from all documents in the stream with `key()`, or provide a default value for null fields with `default()`.
|
|
55
57
|
|
|
58
|
+
`key(field, fallback)` also accepts a fallback. When it is not `undefined`, the result type excludes `null` and `undefined` from the field type and includes the fallback's type; without it, the declared field type is unchanged.
|
|
59
|
+
|
|
56
60
|
```typescript
|
|
57
61
|
const users = schema.instance().table("users");
|
|
58
62
|
|
|
@@ -20,7 +20,7 @@ interface Changes<T> {
|
|
|
20
20
|
|
|
21
21
|
## Create a Change Feed
|
|
22
22
|
|
|
23
|
-
Call `changes()` on
|
|
23
|
+
Call `changes()` on a stream or single selection to build a feed query. Consume it with async iteration to receive events as they arrive:
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
26
|
import { Schema } from "@antelopejs/interface-database";
|
|
@@ -29,10 +29,10 @@ const schema = Schema.get("myapp")!;
|
|
|
29
29
|
const users = schema.instance().table("users");
|
|
30
30
|
|
|
31
31
|
// Monitor all changes to a table
|
|
32
|
-
const allChanges =
|
|
32
|
+
const allChanges = users.changes();
|
|
33
33
|
|
|
34
34
|
// Monitor changes to a specific document
|
|
35
|
-
const userChanges =
|
|
35
|
+
const userChanges = users.get("user-123").changes();
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Process Change Events
|
|
@@ -40,38 +40,45 @@ const userChanges = await users.get("user-123").changes();
|
|
|
40
40
|
Each change event includes a `changeType` field that indicates the nature of the change:
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log("Updated from:", change.oldValue);
|
|
52
|
-
console.log("Updated to:", change.newValue);
|
|
53
|
-
break;
|
|
54
|
-
case "removed":
|
|
55
|
-
console.log("Deleted document:", change.oldValue);
|
|
56
|
-
break;
|
|
43
|
+
for await (const change of users.changes()) {
|
|
44
|
+
if (change.changeType === "added") {
|
|
45
|
+
console.log("New document:", change.newValue);
|
|
46
|
+
} else if (change.changeType === "modified") {
|
|
47
|
+
console.log("Updated from:", change.oldValue);
|
|
48
|
+
console.log("Updated to:", change.newValue);
|
|
49
|
+
} else {
|
|
50
|
+
console.log("Deleted document:", change.oldValue);
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
```
|
|
60
54
|
|
|
61
55
|
## Use with Async Iteration
|
|
62
56
|
|
|
63
|
-
|
|
57
|
+
`changes()` returns `Query<Changes<T>[]>`, but `Query`'s cursor and async iterator yield each array element: one `Changes<T>` event, not a batch. A direct `await` uses `run()` and asks the provider for a completed result; an open-ended feed may never complete, so use iteration for continuous monitoring.
|
|
64
58
|
|
|
65
|
-
```
|
|
66
|
-
|
|
59
|
+
```ts docs-check
|
|
60
|
+
import { Schema } from "@antelopejs/interface-database";
|
|
61
|
+
import type { Changes } from "@antelopejs/interface-database/common";
|
|
62
|
+
|
|
63
|
+
interface User {
|
|
64
|
+
_id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function watchUsers(onChange: (change: Changes<User>) => boolean) {
|
|
69
|
+
const schema = Schema.get<{ users: User }>("myapp");
|
|
70
|
+
if (!schema) throw new Error("Register the myapp schema first");
|
|
71
|
+
const feed = schema.instance().table("users").changes();
|
|
67
72
|
|
|
68
|
-
for await (const
|
|
69
|
-
for (const change of batch) {
|
|
73
|
+
for await (const change of feed) {
|
|
70
74
|
console.log(`${change.changeType}:`, change.newValue ?? change.oldValue);
|
|
75
|
+
if (!onChange(change)) break;
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
```
|
|
74
79
|
|
|
80
|
+
Breaking out of the loop closes its cursor. When using `cursor()` manually, call `return()` when finished.
|
|
81
|
+
|
|
75
82
|
## Use Cases
|
|
76
83
|
|
|
77
84
|
Change feeds are well suited for:
|
|
@@ -41,6 +41,8 @@ Both approaches are equivalent. Use whichever reads better in context.
|
|
|
41
41
|
|
|
42
42
|
## Async Iteration
|
|
43
43
|
|
|
44
|
+
Array-valued queries yield one element at a time, including one `Changes<T>` event per iteration of a change feed. [Atomic mutations](../2.operations/8.atomic_mutations.md) are an exception: they reject cursor execution and must be awaited directly or executed with `run()`.
|
|
45
|
+
|
|
44
46
|
### `cursor()`
|
|
45
47
|
|
|
46
48
|
Return an `AsyncGenerator` that yields results one at a time. This is useful for processing large result sets without loading everything into memory.
|
|
@@ -7,7 +7,9 @@ The `Datum<T>` class represents a single value in the query system. It extends `
|
|
|
7
7
|
```typescript
|
|
8
8
|
class Datum<T> extends Query<T> {
|
|
9
9
|
cast<U>(): Datum<U>;
|
|
10
|
-
key<K extends keyof NonNullable<T
|
|
10
|
+
key<K extends keyof NonNullable<T>, U = undefined>(key: K, def?: U): Datum<
|
|
11
|
+
U extends undefined ? NonNullable<T>[K] : NonNullable<NonNullable<T>[K]> | U
|
|
12
|
+
>;
|
|
11
13
|
default<U>(val: Value<U>): Datum<Exclude<T, undefined | null> | U>;
|
|
12
14
|
do<U>(mapper: (val: ValueProxy<T>) => U): Datum<ExtractType<U>>;
|
|
13
15
|
lookup<U>(other: Selection<U>, localKey: keyof T, otherKey: keyof U): Datum<...>;
|
|
@@ -17,7 +19,7 @@ class Datum<T> extends Query<T> {
|
|
|
17
19
|
|
|
18
20
|
## Field Access
|
|
19
21
|
|
|
20
|
-
The `key` method retrieves a field from a document datum. You can chain `key` calls for nested field access.
|
|
22
|
+
The `key` method retrieves a field from a document datum. You can chain `key` calls for nested field access. A non-undefined fallback removes `null` and `undefined` from the declared field result and adds the fallback's type. Omitting the fallback preserves the declared field type.
|
|
21
23
|
|
|
22
24
|
```typescript
|
|
23
25
|
import { Schema } from "@antelopejs/interface-database";
|
|
@@ -75,11 +77,12 @@ interface User {
|
|
|
75
77
|
name: string;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
const
|
|
80
|
+
const blog = Schema.get<{ posts: Post; users: User }>("blog")!;
|
|
81
|
+
const post = await blog
|
|
79
82
|
.instance()
|
|
80
|
-
.table
|
|
83
|
+
.table("posts")
|
|
81
84
|
.get("post-1")
|
|
82
|
-
.lookup(
|
|
85
|
+
.lookup(blog.instance().table("users"), "authorId", "_id");
|
|
83
86
|
|
|
84
87
|
// post.authorId is now the full User object
|
|
85
88
|
console.log(post.authorId.name);
|
|
@@ -200,6 +200,8 @@ const highest = scores.max();
|
|
|
200
200
|
|
|
201
201
|
Available when the proxy wraps a record/object value.
|
|
202
202
|
|
|
203
|
+
`key(field, fallback)` preserves the declared field type when the fallback is omitted or `undefined`. Otherwise, the proxy's value type is `Exclude<FieldType, undefined | null> | FallbackType`. The fallback may itself be a `ValueProxy`, so its value type participates in the result.
|
|
204
|
+
|
|
203
205
|
```typescript
|
|
204
206
|
const email = user.key("email");
|
|
205
207
|
const emailOrDefault = user.key("email", "none@example.com");
|
|
@@ -19,6 +19,7 @@ Tables live inside schema instances:
|
|
|
19
19
|
Documents are the fundamental unit of data in AQL:
|
|
20
20
|
|
|
21
21
|
- [4. CRUD Operations](./4.crud.md) - Insert, read, update, replace, and delete documents
|
|
22
|
+
- [8. Atomic Mutations](./8.atomic_mutations.md) - Conditional single-record updates and deletes, with explicit outcomes
|
|
22
23
|
|
|
23
24
|
## Index Operations
|
|
24
25
|
|
|
@@ -107,7 +107,7 @@ if (schema) {
|
|
|
107
107
|
|
|
108
108
|
## Work with Tables
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
Put the table-to-document mapping on `Schema`, then access tables by name. The generic parameter of `table()` is a key of that mapping, not a document type:
|
|
111
111
|
|
|
112
112
|
```typescript
|
|
113
113
|
interface User {
|
|
@@ -124,11 +124,14 @@ interface Post {
|
|
|
124
124
|
authorId: string;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
127
|
+
const typedSchema = Schema.get<{ users: User; posts: Post }>("myapp")!;
|
|
128
|
+
const db = typedSchema.instance();
|
|
129
|
+
const users = db.table("users"); // Table<User>
|
|
130
|
+
const posts = db.table("posts"); // Table<Post>
|
|
130
131
|
```
|
|
131
132
|
|
|
133
|
+
Use the same mapping when declaring a schema: `new Schema<{ users: User; posts: Post }>("myapp", definition)`. This type argument describes the schema; it does not validate stored data.
|
|
134
|
+
|
|
132
135
|
## Multi-Tenant Pattern
|
|
133
136
|
|
|
134
137
|
Schemas with multiple instances support multi-tenant architectures. Each tenant gets an isolated instance of the same schema:
|
|
@@ -16,23 +16,46 @@ const users = db.table("users");
|
|
|
16
16
|
|
|
17
17
|
## Type-Safe Tables
|
|
18
18
|
|
|
19
|
-
Provide
|
|
19
|
+
Provide the table-to-document mapping to `Schema`, not to `table()`:
|
|
20
|
+
|
|
21
|
+
```ts docs-check
|
|
22
|
+
import { Schema } from "@antelopejs/interface-database";
|
|
20
23
|
|
|
21
|
-
```typescript
|
|
22
24
|
interface User {
|
|
23
25
|
_id: string;
|
|
24
26
|
name: string;
|
|
25
27
|
email: string;
|
|
26
28
|
status: string;
|
|
29
|
+
role: string;
|
|
30
|
+
createdAt: Date;
|
|
31
|
+
age: number;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
const
|
|
34
|
+
const schema = new Schema<{ users: User }>("myapp", {
|
|
35
|
+
users: {
|
|
36
|
+
fields: {
|
|
37
|
+
name: "string",
|
|
38
|
+
email: "string",
|
|
39
|
+
status: "string",
|
|
40
|
+
role: "string",
|
|
41
|
+
createdAt: "date",
|
|
42
|
+
age: "number",
|
|
43
|
+
},
|
|
44
|
+
indexes: { role: {}, createdAt: {}, name: {} },
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const users = schema.instance().table("users");
|
|
30
48
|
|
|
31
49
|
// TypeScript now validates field names and types
|
|
32
|
-
|
|
33
|
-
const
|
|
50
|
+
export async function readUser() {
|
|
51
|
+
const user = await users.get("user-123"); // Declared as User
|
|
52
|
+
const emails = await users.key("email"); // string[]
|
|
53
|
+
return { user, emails };
|
|
54
|
+
}
|
|
34
55
|
```
|
|
35
56
|
|
|
57
|
+
`get()` declares `SingleSelection<User>`, so awaiting it is typed as `User`, not `User | null`. This declaration does not guarantee that a row exists: a provider may return `null` for a missing key. Check the result before accessing fields. `Schema`'s generic mapping describes documents but does not validate database contents.
|
|
58
|
+
|
|
36
59
|
## Basic Operations
|
|
37
60
|
|
|
38
61
|
### Retrieve All Documents
|
|
@@ -37,12 +37,12 @@ interface User {
|
|
|
37
37
|
email: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const schema = Schema.get("blog")!;
|
|
40
|
+
const schema = Schema.get<{ posts: Post; users: User }>("blog")!;
|
|
41
41
|
const db = schema.instance();
|
|
42
42
|
|
|
43
43
|
const postsWithAuthors = await db
|
|
44
|
-
.table
|
|
45
|
-
.lookup(db.table
|
|
44
|
+
.table("posts")
|
|
45
|
+
.lookup(db.table("users"), "authorId", "_id");
|
|
46
46
|
|
|
47
47
|
// Result type: { _id: string, title: string, authorId: User }
|
|
48
48
|
for (const post of postsWithAuthors) {
|
|
@@ -68,9 +68,12 @@ interface Product {
|
|
|
68
68
|
price: number;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const shop = Schema.get<{ orders: Order; products: Product }>(
|
|
72
|
+
"shop",
|
|
73
|
+
)!.instance();
|
|
74
|
+
const ordersWithProducts = await shop
|
|
75
|
+
.table("orders")
|
|
76
|
+
.lookup(shop.table("products"), "productIds", "_id");
|
|
74
77
|
|
|
75
78
|
// Result type: { ..., productIds: Product[] }
|
|
76
79
|
for (const order of ordersWithProducts) {
|
|
@@ -85,6 +88,9 @@ for (const order of ordersWithProducts) {
|
|
|
85
88
|
Lookup works seamlessly with filters, sorting, and other stream operations:
|
|
86
89
|
|
|
87
90
|
```typescript
|
|
91
|
+
// The shop schema also declares customers and orders.createdAt.
|
|
92
|
+
const db = Schema.get("shop")!.instance();
|
|
93
|
+
|
|
88
94
|
// Filter before lookup
|
|
89
95
|
const recentWithCustomers = await db
|
|
90
96
|
.table("orders")
|
|
@@ -92,10 +98,11 @@ const recentWithCustomers = await db
|
|
|
92
98
|
.lookup(db.table("customers"), "customerId", "_id");
|
|
93
99
|
|
|
94
100
|
// Lookup on a single document
|
|
95
|
-
const
|
|
96
|
-
|
|
101
|
+
const blog = Schema.get<{ posts: Post; users: User }>("blog")!.instance();
|
|
102
|
+
const post = await blog
|
|
103
|
+
.table("posts")
|
|
97
104
|
.get("post-1")
|
|
98
|
-
.lookup(
|
|
105
|
+
.lookup(blog.table("users"), "authorId", "_id");
|
|
99
106
|
```
|
|
100
107
|
|
|
101
108
|
## Lookup vs Join
|
|
@@ -89,7 +89,7 @@ const users = schema.instance().table("users");
|
|
|
89
89
|
const posts = schema.instance().table("posts");
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
For type-safe access, provide
|
|
92
|
+
For type-safe access, provide the table mapping when declaring or retrieving the schema. `table()` infers the document type from the table name:
|
|
93
93
|
|
|
94
94
|
```typescript
|
|
95
95
|
interface User {
|
|
@@ -99,7 +99,8 @@ interface User {
|
|
|
99
99
|
age: number;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
const
|
|
102
|
+
const typedSchema = Schema.get<{ users: User }>("myapp")!;
|
|
103
|
+
const users = typedSchema.instance().table("users"); // Table<User>
|
|
103
104
|
```
|
|
104
105
|
|
|
105
106
|
For more details on table operations, see [Table Operations](../2.operations/3.table_operations.md).
|
|
@@ -36,9 +36,7 @@ import { Schema } from "@antelopejs/interface-database";
|
|
|
36
36
|
const schema = Schema.get("myapp")!;
|
|
37
37
|
const users = schema.instance().table("users");
|
|
38
38
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
for (const change of changes) {
|
|
39
|
+
for await (const change of users.changes()) {
|
|
42
40
|
switch (change.changeType) {
|
|
43
41
|
case "added":
|
|
44
42
|
console.log("New document:", change.newValue);
|
|
@@ -56,9 +54,7 @@ for (const change of changes) {
|
|
|
56
54
|
## Monitor a Single Document
|
|
57
55
|
|
|
58
56
|
```typescript
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
for (const change of userChanges) {
|
|
57
|
+
for await (const change of users.get("user-123").changes()) {
|
|
62
58
|
if (change.changeType === "modified") {
|
|
63
59
|
console.log("User updated:", change.newValue);
|
|
64
60
|
} else if (change.changeType === "removed") {
|
package/docs/index.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
The Database interface provides AQL (Antelope Query Language), a database-agnostic query API. Each module implements this generic interface with the database engine of its choice. AQL offers a chainable, type-safe query builder that works identically across all supported backends, so your application code remains portable regardless of the underlying database.
|
|
6
6
|
|
|
7
|
+
## Import Paths
|
|
8
|
+
|
|
9
|
+
Import `Schema`, `Query`, `Table`, `Datum`, `Stream`, and `ValueProxy` from `@antelopejs/interface-database` in an AntelopeJS module. This entry point registers schemas with the bound provider and installs query execution (`run()`, `then()`, `cursor()`, and async iteration).
|
|
10
|
+
|
|
11
|
+
Supporting types have dedicated paths: import `Changes`, `DeepPartial`, `ExtractType`, and `Value` from `@antelopejs/interface-database/common`, and `FieldType`, `TableDefinition`, and `IndexDefinition` from `@antelopejs/interface-database/schema`. Atomic mutation types are available from the package root and `/atomic`.
|
|
12
|
+
|
|
13
|
+
`@antelopejs/interface-database/staged-query` is the build-only entry point for tools that construct and inspect query stages without the AntelopeJS runtime. Its schema does not register with a provider, and its queries expose `build()` but no execution methods when loaded on their own. Do not `await` a build-only query expecting database results. The runtime entry point augments the same staged classes; keep build-only consumers separate from runtime imports when that isolation matters.
|
|
14
|
+
|
|
7
15
|
## Documentation Structure
|
|
8
16
|
|
|
9
17
|
- [**1. Query Types**](./1.query_types/1.index.md) - Detailed reference for each query type:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "..",
|
|
5
|
+
"noEmit": true,
|
|
6
|
+
"composite": false,
|
|
7
|
+
"incremental": false,
|
|
8
|
+
"paths": {
|
|
9
|
+
"@antelopejs/interface-database": ["../src/index.ts"],
|
|
10
|
+
"@antelopejs/interface-database/*": ["../src/*"]
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"include": ["../src/**/*.ts", "./**/*.ts"]
|
|
14
|
+
}
|