@antelopejs/interface-dms-builder 0.3.0 → 0.4.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 -17
- package/dist/catalog.d.ts +7 -7
- package/dist/drafts.d.ts +10 -9
- package/dist/drafts.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mapping.d.ts +5 -0
- package/dist/mapping.js +2 -1
- package/dist/mapping.js.map +1 -1
- package/dist/operation-map.d.ts +66 -0
- package/dist/operation-map.js +57 -0
- package/dist/operation-map.js.map +1 -0
- package/dist/operations.d.ts +6 -5
- package/dist/operations.js.map +1 -1
- package/dist/project.d.ts +3 -2
- package/dist/project.js.map +1 -1
- package/dist/queries.d.ts +12 -11
- package/dist/resources.d.ts +3 -3
- package/dist/results.d.ts +4 -4
- package/dist/structure.d.ts +5 -4
- package/dist/values.d.ts +14 -10
- package/docs/1.introduction.md +28 -22
- package/docs/10.a-chart-without-code.md +47 -71
- package/docs/11.external-databases.md +94 -70
- package/docs/2.addressing-and-structure.md +48 -40
- package/docs/3.catalog.md +105 -49
- package/docs/4.page-operations.md +74 -60
- package/docs/5.resources.md +97 -71
- package/docs/6.queries.md +94 -95
- package/docs/7.values.md +38 -32
- package/docs/8.results-and-errors.md +62 -45
- package/docs/9.drafts-and-preview.md +84 -48
- package/docs/images/captures/editor-selected.webp +0 -0
- package/docs/tsconfig.json +4 -0
- package/package.json +2 -2
- package/docs/images/editor-selected.png +0 -0
package/docs/6.queries.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: "Queries"
|
|
3
|
+
description: "The operations that add, configure, read back and remove a query, a page route that answers a count, an aggregate or a series of points computed from a resource."
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
##
|
|
7
|
+
## Overview
|
|
4
8
|
|
|
5
|
-
A query gives a page
|
|
9
|
+
A query gives a page a count, a total or a series of points to display. This example adds a query to an existing `/books` page that counts the rows of the `book` resource:
|
|
6
10
|
|
|
7
11
|
```typescript
|
|
8
12
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -19,21 +23,17 @@ if (result.ok) {
|
|
|
19
23
|
}
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
After the backend reloads, a signed-in user with access to `/books` can request
|
|
26
|
+
`AddQuery` writes a model method and a page route. It adds no visible block. After the backend reloads, a signed-in user with access to `/books` can request the route through the frontend, and the route answers `{ "value": N }`, where `N` is the number of rows. A block reads the answer through the returned `route`, as its `fetchUrl`. An editor that previews before it saves uses [PreviewQuery and page drafts](./9.drafts-and-preview.md) instead of a call to `AddQuery` on each change.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
The route carries the page's own permission, through a parameter decorator on that route alone. A user who can open the page can read what the query derives, and no other route on the controller changes.
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
A query is addressed by a `QueryRef`, lives on a page, and reads from a resource. A template decides what a query can compute. You pick one by id and fill in its parameters, and the builder emits the database chain.
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
`GetPageStructure` reports the queries it finds on a page in `PageStructure.queries`, whether the builder wrote them or a person did.
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
## Address a query
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## Address a Query
|
|
35
|
-
|
|
36
|
-
`QueryRef` is `<pageRef>@<name>`, e.g. `/books@inStockCount`. The page part says where the route lives; the resource the query reads is recorded in the query itself, not in its ref.
|
|
36
|
+
`QueryRef` is `<pageRef>@<name>`, for example `/books@inStockCount`. The page part says where the route lives. The query itself records the resource it reads, and the ref does not.
|
|
37
37
|
|
|
38
38
|
```typescript
|
|
39
39
|
import type { QueryRef } from "@antelopejs/interface-dms-builder";
|
|
@@ -41,9 +41,9 @@ import type { QueryRef } from "@antelopejs/interface-dms-builder";
|
|
|
41
41
|
const query: QueryRef = "/books@inStockCount";
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
## List the
|
|
44
|
+
## List the templates
|
|
45
45
|
|
|
46
|
-
`ListQueryTemplates(resourceType?)` returns the templates
|
|
46
|
+
`ListQueryTemplates(resourceType?)` returns the templates registered now. It is a plain read of a live registry, so the ids it returns are the ones to use.
|
|
47
47
|
|
|
48
48
|
```typescript
|
|
49
49
|
import { ListQueryTemplates } from "@antelopejs/interface-dms-builder";
|
|
@@ -54,32 +54,29 @@ for (const template of templates) {
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
`QueryTemplateDescriptor
|
|
57
|
+
`QueryTemplateDescriptor` has these fields:
|
|
58
58
|
|
|
59
59
|
| Field | Type | Description |
|
|
60
60
|
| --- | --- | --- |
|
|
61
|
-
| `id` | `string` | The template id,
|
|
61
|
+
| `id` | `string` | The template id, such as `count` or `aggregate`. `AddQueryInput.template` takes it. |
|
|
62
62
|
| `resourceType` | `"database-table"` | The kind of resource the template reads. |
|
|
63
63
|
| `title` | `string` | A human-readable title. |
|
|
64
|
-
| `description` | `string` | What the template computes. |
|
|
65
|
-
| `output` | `QueryOutputKind` | The shape the query's route
|
|
64
|
+
| `description` | `string` | What the template computes. Optional. |
|
|
65
|
+
| `output` | `QueryOutputKind` | The shape the query's route answers with. |
|
|
66
66
|
| `params` | `ConfigSchema` | The template's parameter schema, in the same vocabulary as the block catalog. |
|
|
67
67
|
|
|
68
|
-
`QueryOutputKind`
|
|
68
|
+
`QueryOutputKind` names what the route answers with:
|
|
69
69
|
|
|
70
70
|
| Kind | Response | Templates |
|
|
71
71
|
| --- | --- | --- |
|
|
72
|
-
| `"scalar"` | `{ value }
|
|
73
|
-
| `"series"` | `{ series }
|
|
72
|
+
| `"scalar"` | `{ value }`, one number | `count`, `aggregate` |
|
|
73
|
+
| `"series"` | `{ series }`, one `{ x, y }` point per group | `series` |
|
|
74
74
|
|
|
75
|
-
A series point carries its group as `x` and
|
|
76
|
-
group is a number that sorts — `2026`, `202601`, `20260114` — rather than a label:
|
|
77
|
-
the group is also what the points are ordered by, and a label belongs to a
|
|
78
|
-
response that knows the reader's locale.
|
|
75
|
+
A series point carries its group as `x` and the measure as `y`. A bucketed group is a number that sorts, such as `2026`, `202601` or `20260114`, and not a label. The points are ordered by the group, and a label would need the reader's locale, which the route does not know.
|
|
79
76
|
|
|
80
|
-
|
|
77
|
+
`params` reads like a block's `config` schema, described in [Catalog](./3.catalog.md). It lists the keys that `AddQueryInput.params` accepts. A key the template does not declare fails with `invalid_config`, and so does an unknown template id.
|
|
81
78
|
|
|
82
|
-
## Add a
|
|
79
|
+
## Add a query
|
|
83
80
|
|
|
84
81
|
```typescript
|
|
85
82
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -98,7 +95,7 @@ if (result.ok) {
|
|
|
98
95
|
}
|
|
99
96
|
```
|
|
100
97
|
|
|
101
|
-
`AddQueryInput
|
|
98
|
+
`AddQueryInput` has these options:
|
|
102
99
|
|
|
103
100
|
| Option | Type | Description |
|
|
104
101
|
| --- | --- | --- |
|
|
@@ -107,30 +104,30 @@ if (result.ok) {
|
|
|
107
104
|
| `template` | `string` | A `QueryTemplateDescriptor.id`. Required. |
|
|
108
105
|
| `params` | `Record<string, QueryParamValue>` | The template's parameters. |
|
|
109
106
|
| `endpoint` | `string` | The route path relative to the page, served at `/dms/pages<pageRef><endpoint>`. Defaults to `/stats/<kebab-name>`. |
|
|
110
|
-
| `response` | `
|
|
107
|
+
| `response` | `QueryArrangement` | How the answer is arranged for the block that reads it. Defaults to what the template computes. |
|
|
111
108
|
| `compare` | `boolean` | Also answer the preceding period, so a card can show a variation. |
|
|
112
109
|
|
|
113
|
-
The result carries
|
|
110
|
+
The result carries two addresses. `query` is the `QueryRef` that configures or removes the query. `route` is the URL a block fetches from, the endpoint under the page at `/dms/pages<pageRef><endpoint>`, which `pageRoutePath(pageRef, endpoint)` from `@antelopejs/interface-dms/page` also builds. The DMS serves the routes of a page controller under its own `/dms/pages` prefix, because the page's own path is the page.
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
The operation enforces these rules:
|
|
116
113
|
|
|
117
|
-
- `name` must be a camelCase identifier,
|
|
118
|
-
- `endpoint` must start with `/`,
|
|
119
|
-
-
|
|
114
|
+
- `name` must be a camelCase identifier, or the operation fails with `invalid_config`. A name that matches a member of the page or of the model fails with `duplicate_name` in the `query` scope.
|
|
115
|
+
- `endpoint` must start with `/`, or the operation fails with `invalid_config`. An endpoint that another route on the page already uses fails with `duplicate_name`. Any other path is allowed, because read-back identifies a query by its shape and not by its URL.
|
|
116
|
+
- A model method of that name may already exist and compile a different chain. The builder then emits the model method under a free suffixed name, so the queries that share the original keep their behavior.
|
|
120
117
|
|
|
121
|
-
## Filter the
|
|
118
|
+
## Filter the rows
|
|
122
119
|
|
|
123
|
-
`QueryFilter` is one
|
|
120
|
+
`QueryFilter` is one condition of a template's `where`. Filters combine with AND.
|
|
124
121
|
|
|
125
122
|
| Field | Type | Description |
|
|
126
123
|
| --- | --- | --- |
|
|
127
124
|
| `field` | `string` | A field of the resource. It must exist and must not be opaque. |
|
|
128
125
|
| `op` | `FilterOp` | The comparison. |
|
|
129
|
-
| `value` | `QueryParamValue` | A plain scalar
|
|
126
|
+
| `value` | `QueryParamValue` | A plain scalar written into the chain, or a `QueryParamBinding`. A scalar must match the field's type. |
|
|
130
127
|
|
|
131
|
-
`FilterOp` is `"eq" | "ne" | "gt" | "ge" | "lt" | "le"`. The ordering operators (`gt`, `ge`, `lt
|
|
128
|
+
`FilterOp` is `"eq" | "ne" | "gt" | "ge" | "lt" | "le"`. The ordering operators (`gt`, `ge`, `lt` and `le`) fail with `invalid_config` on `boolean` and `select` fields.
|
|
132
129
|
|
|
133
|
-
`QueryParamValue` is `
|
|
130
|
+
`QueryParamValue` is a `ConfigValue`, which is plain data. An `ExprValue` anywhere in a query's params fails with `invalid_config`, because a raw expression would put code the builder cannot check inside a model body. [Value encodings](./7.values.md) describes the other sentinels.
|
|
134
131
|
|
|
135
132
|
```typescript
|
|
136
133
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -147,9 +144,9 @@ await AddQuery("/books", {
|
|
|
147
144
|
});
|
|
148
145
|
```
|
|
149
146
|
|
|
150
|
-
## Bind a
|
|
147
|
+
## Bind a value at request time
|
|
151
148
|
|
|
152
|
-
`QueryParamBinding` replaces a
|
|
149
|
+
`QueryParamBinding` replaces a fixed filter value with a value each request supplies. It becomes a parameter of the model method and a declared `@Parameter` on the route.
|
|
153
150
|
|
|
154
151
|
```typescript
|
|
155
152
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -172,12 +169,12 @@ await AddQuery("/books", {
|
|
|
172
169
|
| `$param.name` | `string` | A camelCase identifier. The route exposes the value under this name. |
|
|
173
170
|
| `$param.in` | `"query" \| "param"` | Where the route reads the value from. Defaults to `"query"`. |
|
|
174
171
|
|
|
175
|
-
`name` and `in` describe the route, not the model
|
|
172
|
+
`name` and `in` describe the route, not the model. The route reads the value from that source, converts it with `Number(...)`, `new Date(...)` or a comparison with `"true"` for the field's type, and passes it by position to the model method. The model method names its own parameter from the template.
|
|
176
173
|
|
|
177
|
-
Two rules
|
|
174
|
+
Two rules apply:
|
|
178
175
|
|
|
179
|
-
- `in: "param"` reads a path segment, so the endpoint must declare a matching `:name`. The default `/stats
|
|
180
|
-
- Two filters
|
|
176
|
+
- `in: "param"` reads a path segment, so the endpoint must declare a matching `:name`. The default `/stats/...` endpoint has no segment, so a path-bound value needs an endpoint that declares one. A mismatch fails with `invalid_config`, and the builder emits no route.
|
|
177
|
+
- Two filters can bind the same parameter name only when they agree on its type and its source.
|
|
181
178
|
|
|
182
179
|
```typescript
|
|
183
180
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -199,9 +196,9 @@ await AddQuery("/books", {
|
|
|
199
196
|
});
|
|
200
197
|
```
|
|
201
198
|
|
|
202
|
-
## Measure per
|
|
199
|
+
## Measure per group
|
|
203
200
|
|
|
204
|
-
The `series` template answers one point per group
|
|
201
|
+
The `series` template answers one point per group instead of one number. A chart, a ranking and the other blocks that show several points read it. A series groups by a field's own value, or by a period of a date field.
|
|
205
202
|
|
|
206
203
|
```typescript
|
|
207
204
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -226,44 +223,44 @@ await AddQuery("/dashboard", {
|
|
|
226
223
|
| Option | Type | Description |
|
|
227
224
|
| --- | --- | --- |
|
|
228
225
|
| `op` | `"count" \| "sum" \| "avg" \| "min" \| "max"` | What to measure in each group. Defaults to `count`. |
|
|
229
|
-
| `field` | `string` | The numeric field the measure reads. Required for every op
|
|
230
|
-
| `groupBy` | `string` | The field the rows
|
|
231
|
-
| `bucket` | `"day" \| "week" \| "month" \| "quarter" \| "year"` | Groups a date field by period instead of by exact value. A week runs Monday to Sunday. Only
|
|
232
|
-
| `timezone` | `string` | IANA zone the period is computed in. |
|
|
233
|
-
| `orderBy` | `"group" \| "measure"` |
|
|
226
|
+
| `field` | `string` | The numeric field the measure reads. Required for every op except `count`. |
|
|
227
|
+
| `groupBy` | `string` | The field that groups the rows. Required. Without a `bucket`, it must be a text or numeric field. |
|
|
228
|
+
| `bucket` | `"day" \| "week" \| "month" \| "quarter" \| "year"` | Groups a date field by period instead of by exact value. A week runs from Monday to Sunday. Only valid on a date field. |
|
|
229
|
+
| `timezone` | `string` | The IANA zone the period is computed in. |
|
|
230
|
+
| `orderBy` | `"group" \| "measure"` | Orders the points by their group, for a timeline, or by the measure, for a ranking. Defaults to `group`. |
|
|
234
231
|
| `direction` | `"asc" \| "desc"` | Defaults to `asc`. |
|
|
235
|
-
| `limit` | `number` |
|
|
232
|
+
| `limit` | `number` | Keeps only the first N points after ordering, for a top N. A whole number from 1 to 1000. |
|
|
236
233
|
| `where` | `QueryFilter[]` | As for every other template. |
|
|
237
234
|
|
|
238
|
-
|
|
235
|
+
These rules apply to a series:
|
|
239
236
|
|
|
240
|
-
- **A series is always ordered.**
|
|
241
|
-
- **
|
|
242
|
-
- **A top N is an ordering plus a limit
|
|
237
|
+
- **A series is always ordered.** A database returns groups in its own order, and MongoDB returns them unordered, so the builder always emits an ordering. Without `orderBy` and `direction`, the points come as a timeline, by group, ascending.
|
|
238
|
+
- **Without `timezone`, periods are computed in UTC.** A row near a period boundary can then fall in a different bucket than in the reader's local time.
|
|
239
|
+
- **A top N is an ordering plus a limit,** such as `orderBy: "measure"`, `direction: "desc"` and `limit: 5`. It has no template of its own.
|
|
243
240
|
|
|
244
|
-
|
|
241
|
+
A week runs from Monday to Sunday, like an ISO week, and a week that straddles New Year stays one week. `AddQuery` refuses `bucket: "week"` with `invalid_config` when the installed `@antelopejs/interface-dms` cannot name a week for a reader. Bucket by day or month then, or update it.
|
|
245
242
|
|
|
246
|
-
A series reads back like any other query
|
|
243
|
+
A series reads back like any other query. `GetPageStructure` reports its `template` and the parameters that produced it, including the ordering the engine applies when you omit one. Configuring and removing it work as for a scalar query, and the model method goes with the route.
|
|
247
244
|
|
|
248
|
-
A chain edited past what the
|
|
245
|
+
A chain edited past what the templates accept reads as `opaque`, as everywhere else. It keeps its `endpoint`, `resource`, `modelMethod` and `output`. The builder lists and removes it, but never rewrites it.
|
|
249
246
|
|
|
250
|
-
## Arrange the
|
|
247
|
+
## Arrange the answer
|
|
251
248
|
|
|
252
|
-
One calculation feeds several kinds of block
|
|
249
|
+
One calculation feeds several kinds of block. `response` says how the route arranges its answer, so you describe the calculation only once.
|
|
253
250
|
|
|
254
251
|
| Shape | Answer | Read by |
|
|
255
252
|
| --- | --- | --- |
|
|
256
|
-
| `series` | `{ series }
|
|
257
|
-
| `card` | `{ value
|
|
258
|
-
| `value` | `{ value
|
|
259
|
-
| `items` | `{ items }
|
|
253
|
+
| `series` | `{ series }`, the points | A bare chart |
|
|
254
|
+
| `card` | `{ value?, series, previousValue?, delta?, comparisonSeries? }` | A chart card |
|
|
255
|
+
| `value` | `{ value?, previousValue?, delta?, sparkline? }` | A KPI card |
|
|
256
|
+
| `items` | `{ items }`, the points as ranked entries | A ranked list |
|
|
260
257
|
|
|
261
|
-
The
|
|
258
|
+
The `card`, `value` and `items` shapes come from the `chartCardData`, `kpiCardData` and `topListData` helpers that `@antelopejs/interface-dms/base` publishes. The generated route imports them. The route stays one expression, so a query with an arranged answer still reads back as the parameters that produced it.
|
|
262
259
|
|
|
263
|
-
Two rules
|
|
260
|
+
Two rules apply:
|
|
264
261
|
|
|
265
|
-
- **The headline figure
|
|
266
|
-
- **`compare` needs a period the route supplies.**
|
|
262
|
+
- **The headline figure comes from the points.** Counts and sums add up, and a min or a max is the min or the max of the group figures. An average is the average of the group averages. When the groups differ in size, that is not the average over all the rows.
|
|
263
|
+
- **`compare` needs a period that the route supplies.** The route calls the same model method again with the `compareFrom` and `compareTo` bounds of the request. The query must bind exactly two date values. A calculation with fixed dates has nothing to shift, so the builder emits no second call.
|
|
267
264
|
|
|
268
265
|
```typescript
|
|
269
266
|
import { AddQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -285,13 +282,13 @@ await AddQuery("/dashboard", {
|
|
|
285
282
|
response: "card",
|
|
286
283
|
compare: true,
|
|
287
284
|
});
|
|
288
|
-
// GET /dms/pages/dashboard/stats/revenue?from
|
|
285
|
+
// GET /dms/pages/dashboard/stats/revenue?from=...&to=...&compareFrom=...&compareTo=...
|
|
289
286
|
// { value, series, previousValue, delta, comparisonSeries }
|
|
290
287
|
```
|
|
291
288
|
|
|
292
|
-
## Configure a
|
|
289
|
+
## Configure a query
|
|
293
290
|
|
|
294
|
-
`ConfigureQuery(query, patch, opts?)`
|
|
291
|
+
`ConfigureQuery(query, patch, opts?)` compiles the query again from the merged patch and replaces the route and the model method. A field you leave out keeps the value read back from the file.
|
|
295
292
|
|
|
296
293
|
```typescript
|
|
297
294
|
import { ConfigureQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -301,13 +298,13 @@ await ConfigureQuery("/books@inStockCount", {
|
|
|
301
298
|
});
|
|
302
299
|
```
|
|
303
300
|
|
|
304
|
-
The patch is a `Partial<AddQueryInput
|
|
301
|
+
The patch is a `Partial<AddQueryInput>`. The operation applies its `resource`, `template`, `params` and `endpoint`. The query keeps the name in its `QueryRef`, so a `name` in the patch has no effect. `response` and `compare` in the patch also have no effect, and the route the operation writes has neither an arrangement nor a comparison.
|
|
305
302
|
|
|
306
|
-
A query that no longer reads back as an editable template fails with `opaque_target`.
|
|
303
|
+
A query that no longer reads back as an editable template fails with `opaque_target`. An opaque chain belongs to a person now, and a chain generated again from a template it no longer matches would undo their edit. To change such a query, remove it and add a new one.
|
|
307
304
|
|
|
308
|
-
## Remove a
|
|
305
|
+
## Remove a query
|
|
309
306
|
|
|
310
|
-
`RemoveQuery(query, opts?)` deletes the query's route from the page
|
|
307
|
+
`RemoveQuery(query, opts?)` deletes the query's route from the page. It deletes the model method too once nothing else calls it, and keeps a method that another query shares. It works on an opaque query as well.
|
|
311
308
|
|
|
312
309
|
```typescript
|
|
313
310
|
import { RemoveQuery } from "@antelopejs/interface-dms-builder";
|
|
@@ -315,31 +312,33 @@ import { RemoveQuery } from "@antelopejs/interface-dms-builder";
|
|
|
315
312
|
await RemoveQuery("/books@cheaperThan");
|
|
316
313
|
```
|
|
317
314
|
|
|
318
|
-
## Read a
|
|
315
|
+
## Read a query back
|
|
319
316
|
|
|
320
|
-
`QueryStructure` is a query as found on disk. Read-back is
|
|
317
|
+
`QueryStructure` is a query as found on disk. Read-back is structural. A route reads as a query when its shape and the chain it calls both parse. A hand-written route that matches the grammar is as editable as a generated one, and an edit that keeps the chain inside the grammar reads back again.
|
|
321
318
|
|
|
322
319
|
| Field | Type | Description |
|
|
323
320
|
| --- | --- | --- |
|
|
324
321
|
| `name` | `string` | The query name. Always present. |
|
|
325
|
-
| `endpoint` | `string` | The route path relative to the page's slug,
|
|
326
|
-
| `resource` | `ResourceRef` | The resource the route's model parameter points at. |
|
|
327
|
-
| `modelMethod` | `string` | The model method the route calls.
|
|
328
|
-
| `output` | `QueryOutputKind` | The output shape, read from the property the route's response carries
|
|
322
|
+
| `endpoint` | `string` | The route path relative to the page's slug, in the form `AddQueryInput.endpoint` takes. Always present. |
|
|
323
|
+
| `resource` | `ResourceRef` | The resource that the route's model parameter points at. |
|
|
324
|
+
| `modelMethod` | `string` | The model method the route calls. A query looks for a method with its own name or a numbered fork of it, so two queries with different names never share a method, even when their chains match. |
|
|
325
|
+
| `output` | `QueryOutputKind` | The output shape, read from the property the route's response carries. A query whose chain no longer parses still reports what it answers. |
|
|
326
|
+
| `response` | `QueryArrangement` | How the answer is arranged, when the route wraps the calculation in a helper. |
|
|
327
|
+
| `compare` | `boolean` | `true` when the route also answers the preceding period. |
|
|
329
328
|
| `template` | `string` | The template the chain was compiled from. |
|
|
330
|
-
| `params` | `Record<string,
|
|
331
|
-
| `routeParams` | `{ name: string; in: "query" \| "param" \| "header" }[]` | The route's declared parameters
|
|
332
|
-
| `opaque` | `boolean` | `true` when the query
|
|
333
|
-
| `opaqueReason` | `"unparseable_route" \| "unparseable_chain"` | Why
|
|
329
|
+
| `params` | `Record<string, QueryParamValue>` | The parameters that produced the chain. |
|
|
330
|
+
| `routeParams` | `{ name: string; in: "query" \| "param" \| "header" }[]` | The route's declared parameters. They are what a bound value's `name` and `in` are read from. `header` appears only on hand-written routes. |
|
|
331
|
+
| `opaque` | `boolean` | `true` when the query does not read back as an editable template. |
|
|
332
|
+
| `opaqueReason` | `"unparseable_route" \| "unparseable_chain"` | Why the query is opaque. Set when `opaque` is `true`. |
|
|
334
333
|
|
|
335
|
-
|
|
334
|
+
Each opaque reason names the half that failed:
|
|
336
335
|
|
|
337
|
-
- **`unparseable_route
|
|
338
|
-
- **`unparseable_chain
|
|
336
|
+
- **`unparseable_route`.** The GET method is not the `return { value: await model.m(...) }` shape the builder recognizes.
|
|
337
|
+
- **`unparseable_chain`.** The model method it calls is not a chain that any registered template accepts. It was hand-written, edited outside the grammar, or emitted by a newer builder.
|
|
339
338
|
|
|
340
|
-
A query that no longer parses still reports its `endpoint`, `resource` and `modelMethod
|
|
339
|
+
A query that no longer parses still reports its `endpoint`, `resource` and `modelMethod`. Only `template` and `params` are missing. The builder can always see and remove what it cannot edit.
|
|
341
340
|
|
|
342
|
-
```typescript
|
|
341
|
+
```typescript fragment
|
|
343
342
|
import { GetPageStructure } from "@antelopejs/interface-dms-builder";
|
|
344
343
|
import { pageRoutePath } from "@antelopejs/interface-dms/page";
|
|
345
344
|
|
|
@@ -350,6 +349,6 @@ if (result.ok) {
|
|
|
350
349
|
}
|
|
351
350
|
```
|
|
352
351
|
|
|
353
|
-
## Next
|
|
352
|
+
## Next steps
|
|
354
353
|
|
|
355
|
-
- [Value
|
|
354
|
+
- [Value encodings](./7.values.md) shows how to write configuration values that are not plain literals.
|
package/docs/7.values.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: "Value encodings"
|
|
3
|
+
description: "How the builder writes a configuration value to source: plain JSON as a literal, and the $dataType, $ref, $expr and $block sentinels for values that must reach the source as TypeScript."
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
## Overview
|
|
4
8
|
|
|
5
|
-
Configuration values are plain JSON by default. `AddBlockInput.config`, a `ConfigureBlock` patch
|
|
9
|
+
Configuration values are plain JSON by default. The builder writes `AddBlockInput.config`, a `ConfigureBlock` patch and a page's `permission` to source exactly as you pass them, as literals.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
The type of such a value is `ConfigValue`. It is a string, a number, a boolean, `null`, `undefined`, an array of `ConfigValue` items, or a `ConfigObject`, an object whose members are `ConfigValue` items. The compiler refuses a function or a class instance. `undefined` stands for an absent member. The builder skips it when it writes, and a `ConfigureBlock` patch reads it as "remove this key". The four encodings below are `ConfigObject` values.
|
|
12
|
+
|
|
13
|
+
Some options need TypeScript values instead of JSON literals. A form field needs a DataType instance, a relation needs a controller class, and a chart card needs a nested chart. The structured encodings below let the builder write those values and their imports. A raw `$expr` is the fallback for anything else, and page drafts and query parameters refuse it.
|
|
8
14
|
|
|
9
15
|
```typescript
|
|
10
16
|
import type {
|
|
@@ -15,26 +21,26 @@ import type {
|
|
|
15
21
|
} from "@antelopejs/interface-dms-builder";
|
|
16
22
|
```
|
|
17
23
|
|
|
18
|
-
## Choose an
|
|
24
|
+
## Choose an encoding
|
|
19
25
|
|
|
20
26
|
| You are writing | Encoding |
|
|
21
27
|
| --- | --- |
|
|
22
28
|
| A string, number, boolean, array or object | The value itself, as JSON. |
|
|
23
29
|
| A value the catalog marks `x-dataType` | `DataTypeValue` |
|
|
24
|
-
| A reference to a builder-generated resource
|
|
25
|
-
| A nested component in an option
|
|
30
|
+
| A reference to a class of a builder-generated resource | `RefValue` |
|
|
31
|
+
| A nested component in an option the catalog marks `x-component` | `BlockValue` |
|
|
26
32
|
| Anything else that must reach the source as code | `ExprValue` |
|
|
27
33
|
|
|
28
|
-
The catalog
|
|
34
|
+
The catalog says which option needs which encoding. An option marked `x-dataType` in a block's `ConfigSchema` takes a `DataTypeValue` and nothing else. See [Catalog](./3.catalog.md).
|
|
29
35
|
|
|
30
|
-
## Write a
|
|
36
|
+
## Write a `DataTypeValue`
|
|
31
37
|
|
|
32
|
-
`DataTypeValue` emits `new <DataType>(<config>)` and adds the import
|
|
38
|
+
`DataTypeValue` emits `new <DataType>(<config>)` and adds the import.
|
|
33
39
|
|
|
34
40
|
| Field | Type | Description |
|
|
35
41
|
| --- | --- | --- |
|
|
36
|
-
| `$dataType` | `string` | A `DataTypeDescriptor.id` from the catalog,
|
|
37
|
-
| `config` | `
|
|
42
|
+
| `$dataType` | `string` | A `DataTypeDescriptor.id` from the catalog, such as `"string"` or `"select"`. |
|
|
43
|
+
| `config` | `ConfigObject` | The DataType's options. Omit it for `new <DataType>()` with no argument. |
|
|
38
44
|
|
|
39
45
|
```typescript
|
|
40
46
|
import { AddBlock } from "@antelopejs/interface-dms-builder";
|
|
@@ -56,17 +62,17 @@ await AddBlock({
|
|
|
56
62
|
// emits: type: new DefaultDataTypes.StringType({ placeholder: "Name" })
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
|
|
65
|
+
A resource field's `dataType` takes the same encoding, as [Resources](./5.resources.md) describes.
|
|
60
66
|
|
|
61
|
-
A `$dataType` id the catalog does not list fails with `invalid_config
|
|
67
|
+
A `$dataType` id that the catalog does not list fails with `invalid_config`. The message names `GetCatalog` as the source of the valid ids.
|
|
62
68
|
|
|
63
|
-
## Reference a
|
|
69
|
+
## Reference a generated class
|
|
64
70
|
|
|
65
|
-
`RefValue` emits the bare class
|
|
71
|
+
`RefValue` emits the bare class name of a builder-generated resource and adds its import. It is the checked alternative to `ExprValue` for a class reference, such as the `dataApiController` of a `RelationType`.
|
|
66
72
|
|
|
67
73
|
| Field | Type | Description |
|
|
68
74
|
| --- | --- | --- |
|
|
69
|
-
| `$ref.resource` | `string` | A `ResourceRef
|
|
75
|
+
| `$ref.resource` | `string` | A `ResourceRef`, the resource's name, such as `"product"`. |
|
|
70
76
|
| `$ref.as` | `"dataApi" \| "table" \| "model"` | Which of the resource's classes to reference. Defaults to `"dataApi"`. |
|
|
71
77
|
|
|
72
78
|
```typescript
|
|
@@ -83,17 +89,17 @@ await AddField("order-line", {
|
|
|
83
89
|
// emits: dataApiController: productDataAPI (imported from the resource's data-api.ts)
|
|
84
90
|
```
|
|
85
91
|
|
|
86
|
-
`as`
|
|
92
|
+
`as` picks one of the three classes a resource generates. `"dataApi"` is the `DataController`, `"table"` is the `Table` and `"model"` is the `BasicDataModel`. An unknown resource fails with `invalid_config`, and the message names `ListResources` as the source of the valid refs.
|
|
87
93
|
|
|
88
|
-
`AddBlockInput.controller`
|
|
94
|
+
`AddBlockInput.controller` holds the same reference in a field of its own. A block type that leads with a controller takes a `ResourceRef` there, not a `RefValue` in its config. See [Page operations](./4.page-operations.md).
|
|
89
95
|
|
|
90
|
-
## Write a
|
|
96
|
+
## Write a raw expression
|
|
91
97
|
|
|
92
|
-
`ExprValue`
|
|
98
|
+
The builder emits an `ExprValue` as written. It is for a value the builder cannot model as structure.
|
|
93
99
|
|
|
94
100
|
| Field | Type | Description |
|
|
95
101
|
| --- | --- | --- |
|
|
96
|
-
| `$expr` | `string` | TypeScript source,
|
|
102
|
+
| `$expr` | `string` | TypeScript source, inserted into the emitted file as written. |
|
|
97
103
|
|
|
98
104
|
```typescript
|
|
99
105
|
import { ConfigureBlock } from "@antelopejs/interface-dms-builder";
|
|
@@ -103,16 +109,16 @@ await ConfigureBlock("/revenue#total", {
|
|
|
103
109
|
});
|
|
104
110
|
```
|
|
105
111
|
|
|
106
|
-
Two constraints
|
|
112
|
+
Two constraints apply:
|
|
107
113
|
|
|
108
|
-
- The
|
|
109
|
-
-
|
|
114
|
+
- The symbol the expression names must already be in scope in the target file. `$expr` adds no import.
|
|
115
|
+
- A query's parameters refuse `$expr`, because a query's values are data and a raw expression would put code the builder cannot check inside a model body. See [Queries](./6.queries.md).
|
|
110
116
|
|
|
111
|
-
|
|
117
|
+
A consumer that runs the builder in a restricted mode can refuse `$expr` entirely, because arbitrary code removes the checks that the structured encodings give. `RefValue` and `BlockValue` stay allowed in that mode, because the builder checks them. For a generated class, `RefValue` is the encoding to use.
|
|
112
118
|
|
|
113
|
-
## Nest a
|
|
119
|
+
## Nest a block in a config value
|
|
114
120
|
|
|
115
|
-
A few options hold a component
|
|
121
|
+
A few options hold a component instead of data, such as the `chart` of a `ChartCard`, which the catalog marks `x-component`. `BlockValue` writes such a component. The builder emits the nested factory call and adds its import.
|
|
116
122
|
|
|
117
123
|
```typescript
|
|
118
124
|
import type { BlockValue } from "@antelopejs/interface-dms-builder";
|
|
@@ -120,13 +126,13 @@ import type { BlockValue } from "@antelopejs/interface-dms-builder";
|
|
|
120
126
|
const chart: BlockValue = {
|
|
121
127
|
$block: { type: "ChartArea", config: { xaxisType: "datetime" } },
|
|
122
128
|
};
|
|
123
|
-
//
|
|
129
|
+
// emits: chart: ChartArea({ xaxisType: "datetime" })
|
|
124
130
|
```
|
|
125
131
|
|
|
126
|
-
`type` is a catalog key, like `AddBlockInput.type
|
|
132
|
+
`type` is a catalog key, like `AddBlockInput.type`. In a page draft, an unknown type fails with `invalid_config`. `AddBlock` and `ConfigureBlock` do not turn it into a result, and the call rejects with an `unknown block type` error. Read-back mirrors the write. A nested factory call whose callee is a known block type reads back as a `$block` sentinel, not as `$expr`, so the value stays editable.
|
|
127
133
|
|
|
128
|
-
Most nesting does
|
|
134
|
+
Most nesting does not go through config. It goes through `.child`, which `AddBlockInput.parent` or `BlockDraft.children` express. `$block` is only for an option that the catalog marks `x-component`.
|
|
129
135
|
|
|
130
|
-
## Next
|
|
136
|
+
## Next steps
|
|
131
137
|
|
|
132
|
-
- [Results and
|
|
138
|
+
- [Results and errors](./8.results-and-errors.md) shows how to branch on an operation's result and read its diagnostics.
|