@antelopejs/interface-database-decorators 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -13
- package/dist/antelope.test.js +1 -1
- package/dist/common.d.ts +48 -2
- package/dist/common.js +51 -5
- package/dist/common.js.map +1 -1
- package/dist/database.js +18 -3
- package/dist/database.js.map +1 -1
- package/dist/internal/bound-schema.d.ts +8 -0
- package/dist/internal/bound-schema.js +14 -0
- package/dist/internal/bound-schema.js.map +1 -0
- package/dist/model.d.ts +21 -8
- package/dist/model.js +34 -15
- package/dist/model.js.map +1 -1
- package/dist/modifiers/autodate.d.ts +2 -2
- package/dist/modifiers/autodate.js.map +1 -1
- package/dist/modifiers/common.d.ts +37 -20
- package/dist/modifiers/common.js +81 -68
- package/dist/modifiers/common.js.map +1 -1
- package/dist/modifiers/encryption.js +26 -14
- package/dist/modifiers/encryption.js.map +1 -1
- package/dist/modifiers/hash.d.ts +46 -4
- package/dist/modifiers/hash.js +175 -14
- package/dist/modifiers/hash.js.map +1 -1
- package/dist/modifiers/internal/kinds.d.ts +15 -1
- package/dist/modifiers/internal/kinds.js +35 -0
- package/dist/modifiers/internal/kinds.js.map +1 -1
- package/dist/modifiers/internal/records.d.ts +40 -5
- package/dist/modifiers/internal/records.js +70 -1
- package/dist/modifiers/internal/records.js.map +1 -1
- package/dist/modifiers/localization.js +12 -7
- package/dist/modifiers/localization.js.map +1 -1
- package/dist/relation.d.ts +5 -0
- package/dist/relation.js +11 -0
- package/dist/relation.js.map +1 -1
- package/dist/schema.js +5 -6
- package/dist/schema.js.map +1 -1
- package/dist/table.d.ts +3 -2
- package/dist/table.js +29 -13
- package/dist/table.js.map +1 -1
- package/dist/tests/cross_instance.test.js +5 -2
- package/dist/tests/cross_instance.test.js.map +1 -1
- package/dist/tests/database.test.js +42 -2
- package/dist/tests/database.test.js.map +1 -1
- package/dist/tests/hash_upgrade.test.d.ts +1 -0
- package/dist/tests/hash_upgrade.test.js +132 -0
- package/dist/tests/hash_upgrade.test.js.map +1 -0
- package/dist/tests/helpers/hash-rows.d.ts +40 -0
- package/dist/tests/helpers/hash-rows.js +41 -0
- package/dist/tests/helpers/hash-rows.js.map +1 -0
- package/dist/tests/helpers/other-instance.js.map +1 -1
- package/dist/tests/integration.test.js +15 -4
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/metadata_inheritance.test.d.ts +1 -0
- package/dist/tests/metadata_inheritance.test.js +212 -0
- package/dist/tests/metadata_inheritance.test.js.map +1 -0
- package/dist/tests/modifiers/encryption_date_marker.test.d.ts +1 -0
- package/dist/tests/modifiers/encryption_date_marker.test.js +78 -0
- package/dist/tests/modifiers/encryption_date_marker.test.js.map +1 -0
- package/dist/tests/modifiers/hash.test.js +295 -0
- package/dist/tests/modifiers/hash.test.js.map +1 -1
- package/dist/tests/modifiers/inherited_names.test.d.ts +1 -0
- package/dist/tests/modifiers/inherited_names.test.js +96 -0
- package/dist/tests/modifiers/inherited_names.test.js.map +1 -0
- package/dist/tests/own_key_lookups.test.d.ts +1 -0
- package/dist/tests/own_key_lookups.test.js +49 -0
- package/dist/tests/own_key_lookups.test.js.map +1 -0
- package/dist/tests/partial_update.test.d.ts +1 -0
- package/dist/tests/partial_update.test.js +55 -0
- package/dist/tests/partial_update.test.js.map +1 -0
- package/dist/tests/row_forms.test.d.ts +1 -0
- package/dist/tests/row_forms.test.js +59 -0
- package/dist/tests/row_forms.test.js.map +1 -0
- package/dist/tests/stored_metadata.test.d.ts +1 -0
- package/dist/tests/stored_metadata.test.js +91 -0
- package/dist/tests/stored_metadata.test.js.map +1 -0
- package/dist/tests/subclass_schema_binding.test.d.ts +1 -0
- package/dist/tests/subclass_schema_binding.test.js +115 -0
- package/dist/tests/subclass_schema_binding.test.js.map +1 -0
- package/dist/tests/table.test.js +16 -0
- package/dist/tests/table.test.js.map +1 -1
- package/docs/1.introduction.md +24 -20
- package/docs/2.table-definitions.md +63 -41
- package/docs/3.table-modifiers.md +128 -98
- package/docs/4.data-models.md +63 -52
- package/docs/5.parameter-decoration.md +28 -24
- package/docs/tsconfig.json +3 -0
- package/package.json +10 -9
- package/skills/database-decorators-interface/SKILL.md +6 -3
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Table definitions
|
|
3
|
+
description: Reference for the Table class and the Field, Index, Relation, RegisterTable, and Fixture decorators, RegisterSchema, and Table.table.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
A table is a class that extends `Table`. Each field is a property declared with TypeScript's `declare` keyword and the `@Field` decorator. Further decorators add indexes, relations, and initial data.
|
|
4
8
|
|
|
5
|
-
## The Table
|
|
9
|
+
## The `Table` class
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
`Table` is the base class of every table definition. It declares `_id: string`, the primary key of every table.
|
|
8
12
|
|
|
9
13
|
```typescript
|
|
10
14
|
import { Table, Field } from "@antelopejs/interface-database-decorators";
|
|
@@ -18,9 +22,9 @@ class User extends Table {
|
|
|
18
22
|
}
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
### Extend with
|
|
25
|
+
### Extend with modifiers
|
|
22
26
|
|
|
23
|
-
The `Table.with()`
|
|
27
|
+
The static `Table.with()` method returns a subclass of the table class that holds the mixin methods of the modifiers it receives, such as encryption, hashing, or localization:
|
|
24
28
|
|
|
25
29
|
```typescript
|
|
26
30
|
import {
|
|
@@ -45,11 +49,11 @@ class SensitiveData extends Table.with(EncryptionModifier) {
|
|
|
45
49
|
}
|
|
46
50
|
```
|
|
47
51
|
|
|
48
|
-
Provide a high-entropy key through your secret manager
|
|
52
|
+
Provide a high-entropy key through your secret manager, and never a default key. See [Table modifiers](./3.table-modifiers.md#encryptionmodifier) for the key format and the date precision limit.
|
|
49
53
|
|
|
50
|
-
## The Index
|
|
54
|
+
## The `Index` decorator
|
|
51
55
|
|
|
52
|
-
The `Index` decorator marks a field as a database index.
|
|
56
|
+
The `Index` decorator marks a field as a database index. An indexed field serves lookups through `getAll()` and `between()` on the AQL table, and through the model's `getBy()`. `@Index` on a symbol-keyed property throws, because a database index names a field.
|
|
53
57
|
|
|
54
58
|
```typescript
|
|
55
59
|
import { Table, Field, Index } from "@antelopejs/interface-database-decorators";
|
|
@@ -72,13 +76,15 @@ class User extends Table {
|
|
|
72
76
|
| `crossInstance` | `boolean` | Also keep the index fast for queries that run across all instances. |
|
|
73
77
|
| `unique` | `boolean` | Refuse two rows of the instance with the same key. |
|
|
74
78
|
|
|
75
|
-
### Cross-
|
|
79
|
+
### Cross-instance indexes
|
|
76
80
|
|
|
77
|
-
Every index is scoped to the schema instance
|
|
81
|
+
Every index is scoped to the schema instance. Set `crossInstance: true` when queries across all instances (on the `CROSS_INSTANCE` instance) also use the index. The database driver then maintains an additional index for those queries.
|
|
78
82
|
|
|
79
|
-
The option never changes query results. Without it, cross-instance
|
|
83
|
+
The option never changes query results. Without it, a cross-instance query that names the index returns the same rows but runs slower, and the `@antelopejs/mongodb` and `@antelopejs/rethinkdb` drivers log a warning once per index. Leave it unset for an index that only queries within one instance use, because each extra index costs storage and write time.
|
|
80
84
|
|
|
81
85
|
```typescript
|
|
86
|
+
import { Field, Index, Table } from "@antelopejs/interface-database-decorators";
|
|
87
|
+
|
|
82
88
|
class User extends Table {
|
|
83
89
|
@Index({ crossInstance: true })
|
|
84
90
|
@Field("string")
|
|
@@ -88,11 +94,13 @@ class User extends Table {
|
|
|
88
94
|
|
|
89
95
|
For a compound index, setting `crossInstance: true` on any field of the group applies it to the whole group.
|
|
90
96
|
|
|
91
|
-
### Unique
|
|
97
|
+
### Unique indexes
|
|
92
98
|
|
|
93
|
-
Set `unique: true` to refuse a second row with the same key in one instance. The write that would store it throws `DuplicateKeyError` from `@antelopejs/interface-database`,
|
|
99
|
+
Set `unique: true` to refuse a second row with the same key in one instance. The write that would store it throws `DuplicateKeyError` from `@antelopejs/interface-database`, which names the index and its fields. Test for it with `IsDuplicateKeyError`. Rows of different instances may share a key.
|
|
94
100
|
|
|
95
101
|
```typescript
|
|
102
|
+
import { Field, Index, Table } from "@antelopejs/interface-database-decorators";
|
|
103
|
+
|
|
96
104
|
class Template extends Table {
|
|
97
105
|
@Index({ unique: true })
|
|
98
106
|
@Field("string")
|
|
@@ -100,9 +108,9 @@ class Template extends Table {
|
|
|
100
108
|
}
|
|
101
109
|
```
|
|
102
110
|
|
|
103
|
-
For a compound index, setting `unique: true` on any field of the group makes the whole group's key unique. Not every database module can enforce unique secondary
|
|
111
|
+
For a compound index, setting `unique: true` on any field of the group makes the whole group's key unique. Not every database module can enforce a unique secondary index. A module that cannot, such as `@antelopejs/rethinkdb`, refuses the schema instead of ignoring the option.
|
|
104
112
|
|
|
105
|
-
### Compound
|
|
113
|
+
### Compound indexes
|
|
106
114
|
|
|
107
115
|
Assign multiple fields to the same index group to create a compound index:
|
|
108
116
|
|
|
@@ -134,11 +142,11 @@ class UserActivity extends Table {
|
|
|
134
142
|
}
|
|
135
143
|
```
|
|
136
144
|
|
|
137
|
-
## The Field
|
|
145
|
+
## The `Field` decorator
|
|
138
146
|
|
|
139
|
-
`@Field(type)` declares the
|
|
147
|
+
`@Field(type)` declares the type of a property. The type goes unchanged into the `TableDefinition.fields` map that the database module receives. It accepts every member of the `FieldType` union, for example a string token such as `"string"` or `"number"`, an array, a nested record, or an io-ts codec.
|
|
140
148
|
|
|
141
|
-
```typescript
|
|
149
|
+
```typescript fragment
|
|
142
150
|
import * as t from "io-ts";
|
|
143
151
|
import {
|
|
144
152
|
Table,
|
|
@@ -161,13 +169,13 @@ class Order extends Table {
|
|
|
161
169
|
}
|
|
162
170
|
```
|
|
163
171
|
|
|
164
|
-
Properties without `@Field` are
|
|
172
|
+
Properties without `@Field` are left out of `fields`. `@Field` and `@Index` each write their own metadata, so they combine in either order.
|
|
165
173
|
|
|
166
|
-
## The Relation
|
|
174
|
+
## The `Relation` decorator
|
|
167
175
|
|
|
168
|
-
The `Relation` decorator declares a link from a field to another table. It is
|
|
176
|
+
The `Relation` decorator declares a link from a field to another table. It is metadata only. Database modules do not enforce it, with no foreign key constraint and no check that the target exists. Introspection tools read it to show the links between tables, for example in a schema diagram or to navigate to related records.
|
|
169
177
|
|
|
170
|
-
```typescript
|
|
178
|
+
```typescript fragment
|
|
171
179
|
import {
|
|
172
180
|
Table,
|
|
173
181
|
Field,
|
|
@@ -195,11 +203,11 @@ class Comment extends Table {
|
|
|
195
203
|
| `toField` | `string` | Target field name. Defaults to the target table's primary key. |
|
|
196
204
|
| `many` | `boolean` | The decorated field holds multiple target keys (many targets per source record). |
|
|
197
205
|
|
|
198
|
-
`@Relation`
|
|
206
|
+
`@Relation` writes its own metadata, so it combines with `@Field` and `@Index` in any order.
|
|
199
207
|
|
|
200
|
-
## The RegisterTable
|
|
208
|
+
## The `RegisterTable` decorator
|
|
201
209
|
|
|
202
|
-
The `RegisterTable` class decorator
|
|
210
|
+
The `RegisterTable` class decorator gives a table class its table name and its schema. `RegisterSchema` builds the schema definition from these registrations.
|
|
203
211
|
|
|
204
212
|
```typescript
|
|
205
213
|
import {
|
|
@@ -222,9 +230,9 @@ class User extends Table {
|
|
|
222
230
|
|
|
223
231
|
The first argument is the table name in the database, and the second is the schema name.
|
|
224
232
|
|
|
225
|
-
## The Fixture
|
|
233
|
+
## The `Fixture` decorator
|
|
226
234
|
|
|
227
|
-
The `Fixture` decorator defines
|
|
235
|
+
The `Fixture` decorator defines the rows to insert into an empty table. It receives a generator function that returns them.
|
|
228
236
|
|
|
229
237
|
```typescript
|
|
230
238
|
import {
|
|
@@ -246,13 +254,21 @@ class UserRole extends Table {
|
|
|
246
254
|
|
|
247
255
|
The generator function:
|
|
248
256
|
|
|
249
|
-
- Receives the table class
|
|
250
|
-
- Can return a single object, an array of objects, or a `Promise`
|
|
251
|
-
- Runs
|
|
257
|
+
- Receives the table class registered into the schema, which is the decorated class or a subclass of it.
|
|
258
|
+
- Can return a single object, an array of objects, or a `Promise` of either.
|
|
259
|
+
- Runs when `RegisterSchema` finds the table empty, so a later start inserts nothing again.
|
|
252
260
|
|
|
253
|
-
|
|
261
|
+
Only a table registered with `@RegisterTable` gets its fixture, from `RegisterSchema`. Each row goes through the same conversion as a model insert, so modifiers and the `insert` event apply. A row without an `_id` gets `<table>:fixture:<position>`, so two processes that start on the same empty table write one row between them.
|
|
262
|
+
|
|
263
|
+
### Async generators
|
|
264
|
+
|
|
265
|
+
```typescript fragment
|
|
266
|
+
import {
|
|
267
|
+
Field,
|
|
268
|
+
Fixture,
|
|
269
|
+
Table,
|
|
270
|
+
} from "@antelopejs/interface-database-decorators";
|
|
254
271
|
|
|
255
|
-
```typescript
|
|
256
272
|
@Fixture(async () => {
|
|
257
273
|
const defaults = await loadDefaultConfig();
|
|
258
274
|
return defaults.map((cfg) => ({
|
|
@@ -268,7 +284,7 @@ class SystemConfig extends Table {
|
|
|
268
284
|
|
|
269
285
|
## Inheritance
|
|
270
286
|
|
|
271
|
-
|
|
287
|
+
A table class can extend another table class. The subclass starts with a copy of its parent's table and schema names, fields, indexes, and fixture, so decorators on the subclass do not change the parent:
|
|
272
288
|
|
|
273
289
|
```typescript
|
|
274
290
|
import {
|
|
@@ -312,11 +328,11 @@ class Post extends BaseEntity {
|
|
|
312
328
|
}
|
|
313
329
|
```
|
|
314
330
|
|
|
315
|
-
## Register a
|
|
331
|
+
## Register a schema
|
|
316
332
|
|
|
317
|
-
The `RegisterSchema` function builds a `SchemaDefinition` from
|
|
333
|
+
The `RegisterSchema` function builds a `SchemaDefinition` from every table that `@RegisterTable` registered for the schema ID. It constructs the `Schema`, which the database module provisions, and binds each table class to it (see [Open a table from its class](#open-a-table-from-its-class)). Then it inserts the fixtures of the tables that are empty.
|
|
318
334
|
|
|
319
|
-
Call it in the module that declares the tables
|
|
335
|
+
Call it in the module that declares the tables. The binding then follows that module's database, whichever module later uses the classes.
|
|
320
336
|
|
|
321
337
|
```typescript
|
|
322
338
|
import { RegisterSchema } from "@antelopejs/interface-database-decorators";
|
|
@@ -330,13 +346,19 @@ await RegisterSchema("myapp");
|
|
|
330
346
|
| ---------- | -------- | ------------------------------------------------------------------ |
|
|
331
347
|
| `schemaId` | `string` | Schema id matching the `@RegisterTable(_, schemaId)` declarations. |
|
|
332
348
|
|
|
333
|
-
`RegisterSchema` returns `Promise<void>`.
|
|
349
|
+
`RegisterSchema` returns `Promise<void>`. It throws `No tables registered for schema '<schemaId>'` when no class registers into the schema.
|
|
334
350
|
|
|
335
|
-
## Open a
|
|
351
|
+
## Open a table from its class
|
|
336
352
|
|
|
337
353
|
`Table.table()` returns the AQL table a class describes, in the schema `RegisterSchema` bound it to:
|
|
338
354
|
|
|
339
|
-
```typescript
|
|
355
|
+
```typescript fragment
|
|
356
|
+
import {
|
|
357
|
+
RegisterSchema,
|
|
358
|
+
RegisterTable,
|
|
359
|
+
Table,
|
|
360
|
+
} from "@antelopejs/interface-database-decorators";
|
|
361
|
+
|
|
340
362
|
@RegisterTable("users", "myapp")
|
|
341
363
|
class User extends Table {
|
|
342
364
|
declare name: string;
|
|
@@ -349,4 +371,4 @@ User.table("tenant-1"); // instance "tenant-1" (CROSS_INSTANCE is accepted too)
|
|
|
349
371
|
User.table(db); // db.table("users") on a SchemaInstance you already hold
|
|
350
372
|
```
|
|
351
373
|
|
|
352
|
-
|
|
374
|
+
Given a `SchemaInstance`, `table()` takes the table from it as is, so pass an instance of the class's own schema. A class without `@RegisterTable` throws `<Class> has no table name`. A class that no `RegisterSchema` call bound, and whose schema name has no schema, throws `<Class> is not bound to a schema`.
|