@antelopejs/interface-dms-builder 0.4.1 → 0.5.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 +4 -4
- package/dist/addressing.d.ts +6 -0
- package/dist/analysis.d.ts +73 -0
- package/dist/analysis.js +18 -1
- package/dist/analysis.js.map +1 -1
- package/dist/catalog.d.ts +139 -1
- package/dist/compose.d.ts +44 -0
- package/dist/compose.js +7 -0
- package/dist/compose.js.map +1 -1
- package/dist/database.d.ts +230 -0
- package/dist/database.js +80 -0
- package/dist/database.js.map +1 -0
- package/dist/drafts.d.ts +61 -0
- package/dist/drafts.js +21 -0
- package/dist/drafts.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mapping.d.ts +122 -0
- package/dist/mapping.js +15 -0
- package/dist/mapping.js.map +1 -1
- package/dist/operation-map.d.ts +26 -1
- package/dist/operation-map.js +22 -0
- package/dist/operation-map.js.map +1 -1
- package/dist/operations.d.ts +56 -0
- package/dist/operations.js +18 -0
- package/dist/operations.js.map +1 -1
- package/dist/project.d.ts +63 -1
- package/dist/project.js +10 -0
- package/dist/project.js.map +1 -1
- package/dist/queries.d.ts +125 -0
- package/dist/resources.d.ts +132 -0
- package/dist/resources.js +9 -0
- package/dist/resources.js.map +1 -1
- package/dist/results.d.ts +49 -1
- package/dist/structure.d.ts +42 -0
- package/dist/table-view.d.ts +49 -0
- package/dist/table-view.js +7 -0
- package/dist/table-view.js.map +1 -1
- package/dist/theme.d.ts +144 -0
- package/dist/theme.js +46 -0
- package/dist/theme.js.map +1 -0
- package/dist/values.d.ts +64 -0
- package/dist/values.js +7 -0
- package/dist/values.js.map +1 -1
- package/docs/1.introduction.md +3 -0
- package/docs/10.a-chart-without-code.md +7 -7
- package/docs/11.external-databases.md +16 -11
- package/docs/11.theme-and-branding.md +135 -0
- package/docs/12.connect-a-database.md +87 -0
- package/docs/4.page-operations.md +1 -1
- package/package.json +15 -8
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Theme and branding"
|
|
3
|
+
description: "Read and write a project's theme, meaning its CSS variables, colors, logos and fonts, with GetTheme, SetTheme and CreateThemeLayer."
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
A project's look (its colors, corners, font and logos) lives in its own frontend layer, above the DMS, as plain files: a stylesheet of CSS variables, the `ui.colors` and `branding.logo` keys of its `app/app.config.ts`, and logo and font files under `public/`. That is the path the DMS theming guide describes, and the files stay hand-editable and versioned with the rest of the project.
|
|
10
|
+
|
|
11
|
+
`GetTheme` reads those files back, `SetTheme` writes them in one transaction, and `CreateThemeLayer` gives a project without a layer of its own the one a theme is written to. Like every other operation, they edit code, in development: nothing is stored in a database, and a theme reaches production with the next build.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import {
|
|
15
|
+
CreateThemeLayer,
|
|
16
|
+
GetTheme,
|
|
17
|
+
SetTheme,
|
|
18
|
+
} from "@antelopejs/interface-dms-builder";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Where the theme lives
|
|
22
|
+
|
|
23
|
+
The layer is the highest-priority frontend module the project registers from inside its root (the DMS and installed modules never are), unless the module config names one (`theme.layerPath`, relative to `projectRoot` or absolute). Inside it:
|
|
24
|
+
|
|
25
|
+
| File | Holds |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `app/assets/css/theme.css` | The `@font-face` rules of the fonts it hosts, then the variables, unlayered: `:root` for those both modes share, `:root:not(.dark)` for light, `.dark` for dark |
|
|
28
|
+
| `app/app.config.ts` | `ui.colors.<role>`, a palette per Nuxt UI color role, and `branding.logo.<slot>.<light\|dark>` |
|
|
29
|
+
| `public/branding/` | The uploaded logos, named after their content, and in `fonts/` the files of the fonts the theme hosts |
|
|
30
|
+
| `dms.frontend.ts` | The layer's entry, which imports the stylesheet |
|
|
31
|
+
|
|
32
|
+
The light rule is not plain `:root`: unlayered, a `:root` rule also matches the page in dark mode and beats the DMS's own dark values, which live in its `dms` cascade layer. A value set for light alone would show in dark too.
|
|
33
|
+
|
|
34
|
+
The DMS frontend loader applies a layer's `app/app.config.ts` on its own (the file's default export is spelled `defineDmsAppConfig({…})`), so the entry only imports the stylesheet. The `email` and `favicon` slots of `branding.logo`, and the `branding.defaults` an editor previews a return to the DMS look with, need a DMS that ships them.
|
|
35
|
+
|
|
36
|
+
A layer with a `layers/` directory is read differently by the loader: it ignores the module root and reads each layer in `layers/`, sorted by name, the last one winning a value or a public file two of them set. The theme then goes in that last layer (`layers/<name>/app/…` and `layers/<name>/public/branding/`), and the entry, which stays the module's, imports the stylesheet from there. `files` in `GetTheme`'s answer says which. A `layers/` directory with no layer in it is answered `unsupported`: nothing written in the module would be read.
|
|
37
|
+
|
|
38
|
+
## Read the theme
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { GetTheme } from "@antelopejs/interface-dms-builder";
|
|
42
|
+
|
|
43
|
+
async function readTheme() {
|
|
44
|
+
const result = await GetTheme();
|
|
45
|
+
if (!result.ok) {
|
|
46
|
+
if (result.error.code === "not_found") {
|
|
47
|
+
console.log(`no layer yet; CreateThemeLayer would create ${result.error.ref}`);
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const { variables, colors, logos, version } = result.data;
|
|
52
|
+
return { variables, colors, logos, version };
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`ThemeStructure` is the theme as the files hold it:
|
|
57
|
+
|
|
58
|
+
| Field | Type | Description |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `layer` | `ThemeLayer` | The layer's name and absolute source path. |
|
|
61
|
+
| `variables` | `ThemeVariableSets` | `shared`, `light` and `dark`: the custom properties each sets, by name. |
|
|
62
|
+
| `colors` | `ThemeColors` | The palette each role takes: `primary`, `success`, `warning`, `error`, `info`. |
|
|
63
|
+
| `logos` | `ThemeLogos` | The public path of each slot's file, per mode: `default`, `collapsed`, `email`, `favicon`. |
|
|
64
|
+
| `files` | `ThemeFiles` | The files above, relative to the layer. |
|
|
65
|
+
| `stylesheetImported` | `boolean` | Whether the entry imports the stylesheet already. |
|
|
66
|
+
| `version` | `string` | A content hash of the stylesheet, the app config and the entry, for `expectedVersion`. |
|
|
67
|
+
|
|
68
|
+
Only what the builder can read back is reported: a palette or a logo path written as a string literal. A value the file holds any other way is the author's, and reads as no override. That covers an expression (`primary: brand`, `process.env.BRAND ?? "violet"`), a shorthand (`{ primary }`), a method and an accessor. In an object with a spread (`{ ...base, primary: "violet" }`), the string literals are read, and every other key reads as no override, since the spread may carry it in. A stylesheet holding anything but rules of custom properties under those three selectors, and the `@font-face` rules the builder writes, is reported `unsupported` rather than flattened.
|
|
69
|
+
|
|
70
|
+
## Write the theme
|
|
71
|
+
|
|
72
|
+
`SetTheme` takes the whole theme, as it should be: what the draft leaves out goes back to the DMS default.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { GetTheme, SetTheme } from "@antelopejs/interface-dms-builder";
|
|
76
|
+
|
|
77
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/></svg>';
|
|
78
|
+
|
|
79
|
+
async function writeTheme() {
|
|
80
|
+
const theme = await GetTheme();
|
|
81
|
+
if (!theme.ok) return;
|
|
82
|
+
|
|
83
|
+
return SetTheme(
|
|
84
|
+
{
|
|
85
|
+
variables: {
|
|
86
|
+
shared: { "--ui-radius": "0.5rem" },
|
|
87
|
+
light: { "--ui-bg-muted": "#f6f5f2" },
|
|
88
|
+
dark: { "--ui-bg-muted": "#0c0b09" },
|
|
89
|
+
},
|
|
90
|
+
colors: { primary: "violet" },
|
|
91
|
+
logos: theme.data.logos,
|
|
92
|
+
uploads: [
|
|
93
|
+
{
|
|
94
|
+
slot: "default",
|
|
95
|
+
mode: "light",
|
|
96
|
+
contentType: "image/svg+xml",
|
|
97
|
+
base64Content: Buffer.from(svg).toString("base64"),
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{ expectedVersion: theme.data.version },
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **Code of the author's.** A palette or a logo path the app config holds in code of its own (see above) is left as written by a draft that leaves it out, as one read back from `GetTheme` does. A draft that sets it is refused with `unsupported`, its `detail` naming each key path (`ui.colors.primary`, `branding.logo.default.light`), and nothing is written: change it in the file itself. In an object with a spread, a draft may keep the literals as they were read, and changes none.
|
|
107
|
+
- **Uploads.** An upload sets its slot and mode, over whatever `logos` says there. SVG, PNG, JPEG, WebP or ICO, up to 512 KiB, its bytes checked against its type. The `email` slot takes a PNG or a JPEG alone, as an upload or as a path: Gmail and Outlook show no other image. The file is named `<slot>-<mode>-<hash>.<ext>`, so a new logo is a new URL no cache keeps serving under the old one; one file sent for several slots is written once. Of two uploads for one slot and mode, only the last is written.
|
|
108
|
+
- **SVG.** It is written sanitized: scripts, event handlers, `javascript:` links and embedded documents are taken out, and so is every reference to another file. That covers a link (`href`, `xlink:href`) on any element unless it points inside the file (`#id`), a `url()` that does not (`url(#gradient)` stays), `image-set()` and `@import`, in attributes, `style` and `<style>` alike. `<image>` and `<feImage>` may keep an embedded PNG, JPEG, GIF or WebP (`data:image/png;base64,…`), which loads nothing. The result carries an `svg_sanitized` warning when anything that runs or fetches was taken out; what drawing tools leave in their exports (`<metadata>`, `inkscape:*`, `sodipodi:*`) goes silently. What is left must be a single well-formed `<svg>`: an HTML page, two images, or an Illustrator export declaring its namespaces as DOCTYPE entities (`&ns_svg;`) is refused with `invalid_config`. Export it again as plain SVG. An SVG without its `xmlns`, as copied out of an HTML page, is given one.
|
|
109
|
+
- **Old logos.** A file the builder wrote and the theme no longer points at is deleted, the email logo's included. A file it did not name is never touched. An email already sent links to its logo by absolute URL, so it shows no header image once the file it names is deleted and deployed.
|
|
110
|
+
- **Fonts.** A variable whose value starts with one of the fonts below (`--font-sans: "Inter", ui-sans-serif, sans-serif`) gets that font hosted by the project: its files are downloaded from Fontsource through jsDelivr (`@fontsource-variable/<package>`, at a pinned release) into `public/branding/fonts/`, and the stylesheet declares them in `@font-face` rules, so the dashboard shows it the same everywhere and fetches nothing from elsewhere. Each font comes as variable WOFF2 files covering the Latin alphabet, its italics when it has some, and its `OFL.txt` license. A font named further down a stack is a fallback, and is not downloaded. The files are downloaded once: a save whose fonts are all in the layer needs no network, and one that cannot download a file is refused with `unsupported`, nothing written. A font file the builder downloaded and the theme no longer uses is deleted; one it did not name is never touched. The fonts, all under the SIL Open Font License: Inter, IBM Plex Sans, Public Sans, DM Sans, Manrope, Plus Jakarta Sans, Figtree, Atkinson Hyperlegible Next, Nunito and Source Serif 4.
|
|
111
|
+
- **Values.** A variable's name must be a custom property, its value must not be able to end its declaration (`;`, braces, comments, `url()`, `@import`, `!important`) nor leave a parenthesis, a bracket or a quote open (`calc(1rem` would swallow the rules after it), and a palette name must be one (`violet`, or `brand` for a project's own `--color-brand-50…950`). A logo path is rooted in `public/` (`/branding/logo.svg`), never `//host/…`.
|
|
112
|
+
- **Shape.** A draft that is not shaped as `ThemeDraft` says (no `variables`, a value that is not a string, an upload without its `base64Content`) is answered `invalid_config`, each issue's `pointer` naming the value.
|
|
113
|
+
- **The entry.** The first save that writes a stylesheet adds its import to the entry. In development that one change reloads the page; the stylesheet, the app config, new logos and new fonts are applied without one.
|
|
114
|
+
|
|
115
|
+
Every file is checked before any is written, and each is written whole, through a temporary file renamed over it. A write that fails half way puts back those already written and removes the directories it created, then throws the error that stopped it. `stale` comes back when the files changed since `expectedVersion` was read.
|
|
116
|
+
|
|
117
|
+
## Create the layer
|
|
118
|
+
|
|
119
|
+
A project without a frontend layer of its own gets `not_found` from `GetTheme`, its `ref` naming where the layer would go. A layer whose entry file is missing also gets `not_found`, its `ref` naming that file; `CreateThemeLayer` does not fix that case, it returns the layer unchanged. `CreateThemeLayer` creates it there (`frontend-vue/` beside the sources, with a `package.json` and an entry importing the stylesheet) and registers it from the project's exported `start()`, above the DMS:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import path from "node:path";
|
|
123
|
+
import { AddFrontendModule } from "@antelopejs/interface-dms/page";
|
|
124
|
+
|
|
125
|
+
export function start() {
|
|
126
|
+
AddFrontendModule({
|
|
127
|
+
name: "shop-frontend",
|
|
128
|
+
sourcePath: path.join(__dirname, "../frontend-vue"),
|
|
129
|
+
renderer: { name: "vue", version: "3" },
|
|
130
|
+
priority: 1,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`start()` is created, or made async, as it needs to be, and the edit is typechecked like any other; a registration that cannot be written takes the layer's files back with it. The layer's `package.json` declares the loader releases it runs on in its `engines` entry for `@antelopejs/dms-frontend`, and its app config, written by the first save that needs one, starts as `export default defineDmsAppConfig({})`. A project that has a layer keeps it, and nothing is written. The backend reloads with the registration; the frontend dev server reads its modules at startup, so it serves the layer once restarted.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Connect a database"
|
|
3
|
+
description: "The operations a connect flow drives to add a database the project does not own: list and check connections live, read the project's database state, write development values, repair, edit and remove a connection, and sample its rows."
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
A connect flow, such as **Start from an existing database** in `@antelopejs/dms-assistant`, drives these operations. They are served wherever the builder is, in development or under `builder.enabled`, and `CheckDatabaseConnection` answers in development only.
|
|
10
|
+
|
|
11
|
+
| Operation | Answers |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `ListDatabaseConnections()` | Each catalog connection as `{ id, engine?, version?, database?, role?, error? }`: what its provider's `Info()` answers, or why it did not. |
|
|
14
|
+
| `CheckDatabaseConnection({ engine, url })` | `{ reachable, latencyMs?, user?, canRead, canWrite, reason? }`. See [Check a connection before writing it](#check-a-connection-before-writing-it). |
|
|
15
|
+
| `GetDatabaseProject()` | `{ modules, self, ownDatabase?, databasePins, dmsDatabase?, interfaceDatabase?, drivers }`. See [What connecting a database writes](#what-connecting-a-database-writes). |
|
|
16
|
+
| `ConfigureProject(steps, { variables })` | As `ConfigureProject`, with the variables' development values written to `.env` first. See [Development values](#development-values). |
|
|
17
|
+
| `RepairDatabaseConnections({ dryRun? })` | The configuration change, or a success with no file when nothing is missing. See [Repair a connection made before](#repair-a-connection-made-before). |
|
|
18
|
+
| `GetDatabaseConnection(connection)` | `{ wiring, details? }`: the driver, the tables modules and the variables wired for the connection, and its URL's parts without the password. `not_found` when no module declares it. |
|
|
19
|
+
| `SetDatabaseConnectionUrl(connection, url)` | `{ variable }`, the variable the URL was written to. A URL with no password keeps the current one. |
|
|
20
|
+
| `RemoveDatabaseConnection(connection, { dryRun? })` | The configuration change. See [Remove a connection](./11.external-databases.md#remove-a-connection). |
|
|
21
|
+
| `ListEntryConnections(module)` | The `interface-database` and `interface-database-catalog` bindings of one module entry as `antelope.config.ts` writes them, `{ interface, source, id?, access? }` each, whether core loads that module or not, a `disabled: true` entry included. `access` is `read-write` only when the `dms-database` entry's `config.externalConnections.<id>.access` says so. `not_found` when no entry has that id, and `invalid_config` for an entry, `importOverrides` or binding written as a variable or a spread. It writes nothing. |
|
|
22
|
+
| `GetLatestAnalysis(connection)` | The connection's running analysis, or else the last to finish. `not_found` when none ran since the start. |
|
|
23
|
+
| `SampleDatabaseRows(connection, { relation, columns?, limit })` | The rows the provider's `SampleRows` answers. `not_found` for an unknown connection, and `invalid_config` with the provider's message when it refuses. |
|
|
24
|
+
|
|
25
|
+
## Check a connection before writing it
|
|
26
|
+
|
|
27
|
+
`CheckDatabaseConnection` tests a database before anything is written to `antelope.config.ts` or `.env`. The driver module is not configured at that point, so the builder opens the database itself with the engine's client library: `pg`, `mysql2`, `mongodb` or `rethinkdb-ts`. They are optional peer dependencies of `@antelopejs/dms`, required only when their engine is checked, first from the DMS and then from the project. When neither installs the client, the answer is `reachable: false` with a `reason` such as `Install pg to connect a PostgreSQL database.`
|
|
28
|
+
|
|
29
|
+
`DATABASE_ENGINE_SCHEMES` lists the engines the check takes, each with the URL schemes it accepts, such as `["postgres:", "postgresql:"]` for `postgres`. A form that offers engines can compare its own list with it.
|
|
30
|
+
|
|
31
|
+
Outside development the call fails with `unsupported` and opens nothing, since it connects to a host the caller names. An `engine` other than `postgres`, `mysql`, `mongodb` or `rethinkdb`, or a `url` whose scheme is not that engine's, fails with `invalid_config`, naming the fields and never their values.
|
|
32
|
+
|
|
33
|
+
The answer reads as follows:
|
|
34
|
+
|
|
35
|
+
- `reachable` is `true` when a server answered, even to refuse the sign-in. `latencyMs` is the round trip of the first query of the signed-in account, and `user` is the account the server reports.
|
|
36
|
+
- `canWrite` is `true` when the account may insert, update or delete in any relation it can see, as the catalog of the engine's driver decides. `canRead` is `true` when it may read any of them, or when it sees none. A MongoDB server without access control gives full privileges, whatever the database holds.
|
|
37
|
+
- `reason` is a short sentence for the person who connects, present when the database is unreachable, refuses the sign-in or cannot be read. It never holds the URL, the password or a query a driver echoes in its message.
|
|
38
|
+
|
|
39
|
+
`mysql` checks MySQL and MariaDB alike, with `mysql://` URLs, and reads the account's privileges as `@antelopejs/mysql` does: a grant on some columns of a relation only does not make it readable. A MySQL, MariaDB or RethinkDB URL with no database after the port is answered once the account has signed in, with `reachable: true`, `user`, no access and a `reason` saying the URL names no database.
|
|
40
|
+
|
|
41
|
+
The check ends within three times the connect timeout of five seconds, and closes every connection it opened, whether the database answers or the deadline passes first. It never waits for a server to close its end before it answers, and it shuts a socket still open a quarter of a second after its goodbye. For RethinkDB it deletes a key no row has to learn whether the account may write, so it changes no row.
|
|
42
|
+
|
|
43
|
+
## What connecting a database writes
|
|
44
|
+
|
|
45
|
+
A connect flow plans one `ConfigureProject` batch from `GetDatabaseProject()`. Its answer names `self`, the DMS's id in `antelope.config.ts`, `ownDatabase`, the project's default `interface-database` provider, `databasePins`, the modules to pin to it, and `dmsDatabase`, the id of the `dms-database` entry. `ownDatabase` and `dmsDatabase` are absent when the project has no such database or module. `interfaceDatabase` is the version of `@antelopejs/interface-database` core resolves for the project, and `drivers` gives each engine's driver package with a range of only its releases built for that version, such as `>=0.2.5 <0.4.0` for `@antelopejs/postgres` on 0.3.4. A range never reaches a release built for another line of the interface, which the project could not start with. An engine with no driver built for that version is absent from `drivers`.
|
|
46
|
+
|
|
47
|
+
`GetDatabaseProject` fails with `invalid_config` when the configuration does not say plainly which database is the default, or when a module that imports `interface-database` without providing it, or `dms-database` itself, is written as a version string or with a spread or a member under a computed name, so the builder cannot write its pin. An entry with a spread that pins `interface-database` in an `importOverrides` written after its last spread counts as pinned, since a later key wins.
|
|
48
|
+
|
|
49
|
+
The batch writes these steps in order:
|
|
50
|
+
|
|
51
|
+
1. `AddModule` for the engine's driver, at the package and range `drivers` gives, with its URL read from a variable.
|
|
52
|
+
2. `CreateLocalModule` for the module that holds the tables, pinned to the driver.
|
|
53
|
+
3. `ConfigureModule` on `self`, declaring the catalog connection under the connection's id.
|
|
54
|
+
4. `ConfigureModule` on `dms-database`, when the project has it and an own database. For a connection `erp` served by the driver `builder-db-erp` over a project whose own database is `mongodb`, the entry then reads:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const modules = {
|
|
58
|
+
"dms-database": {
|
|
59
|
+
importOverrides: [
|
|
60
|
+
{ interface: "@antelopejs/interface-database", source: "mongodb" },
|
|
61
|
+
{ interface: "@antelopejs/interface-database", source: "builder-db-erp", id: "erp" },
|
|
62
|
+
{ interface: "@antelopejs/interface-database-catalog", source: "builder-db-erp", id: "erp" },
|
|
63
|
+
],
|
|
64
|
+
config: { externalConnections: { erp: { access: "read" } } },
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The first `interface-database` entry without an id is the binding a plain import reaches, so the own database comes first. The connection keeps one id on both interfaces and in `externalConnections`.
|
|
70
|
+
5. One `ConfigureModule` for each module in `databasePins`. Once a second `interface-database` provider exists, core refuses to start a module that imports `interface-database` without a pin, so each one that does not provide it gets a pin to the own database.
|
|
71
|
+
|
|
72
|
+
`ConfigureModule` writes an override without an id ahead of the first named connection of its interface, and merges a nested `config` object key by key, so the connections already registered with `dms-database` stay. It replaces a scalar or an array held where it writes an object. A shorthand member of the key it writes, such as `url` in `config: { url }`, is replaced by an assignment when it writes a scalar or an array there, and the other members stay. It refuses the step with `invalid_config` where what it would replace is held in a way it cannot read, and writes nothing. That covers an object written into a shorthand member, whose value is a variable; a `config` written as a variable, such as `config: SHARED_CONFIG`, or as a shorthand `config`; an object with a spread, a variable or a call where it writes an object; and an object that holds a member under a computed name, a method or an accessor, since that member may be the key it writes. The refusal names the module and the key path, such as `config of module "dms-database"`, says it is held in a way the builder cannot read, and asks for a plain object literal with plain names there.
|
|
73
|
+
|
|
74
|
+
## Development values
|
|
75
|
+
|
|
76
|
+
`ConfigureProject(steps, { variables: [{ name, value }] })` writes each variable's development value into the project's `.env`, once a dry run has accepted the steps, then writes the configuration. Each variable must be one the steps map in their `env`. When `.env` refuses one, nothing is written, and when the configuration then fails, `.env` goes back to what it held. `.env` must be ignored by git. With `dryRun`, the variables are not written.
|
|
77
|
+
|
|
78
|
+
## Repair a connection made before
|
|
79
|
+
|
|
80
|
+
A project connected before the steps above has the catalog connection and nothing `dms-database` reads. `RepairDatabaseConnections()` adds what is missing for every catalog connection declared on the DMS's entry. It writes the `dms-database` overrides and the pins of step 5, and never `access`, so a value set in the `dms-database` console is kept. With `{ dryRun: true }` it answers the changes without writing them. It refuses as `GetDatabaseProject` does.
|
|
81
|
+
|
|
82
|
+
A configuration that already holds everything answers `{ ok: true, data: { files: [] }, changes: [] }` and is not written, so a second call changes nothing.
|
|
83
|
+
|
|
84
|
+
## Next steps
|
|
85
|
+
|
|
86
|
+
- [External databases](./11.external-databases.md) shows the `ConfigureProject` steps that wire a database, then its analysis and mapping.
|
|
87
|
+
- [Results and errors](./8.results-and-errors.md) shows how to branch on what every operation answers.
|
|
@@ -114,7 +114,7 @@ await CreatePage({
|
|
|
114
114
|
|
|
115
115
|
## Configure a page
|
|
116
116
|
|
|
117
|
-
`ConfigurePage(ref, patch, opts?)` patches the metadata of the page in place and returns its ref and the `version` of the page file it wrote. A draft held across the patch saves against that version. The patch is a `Partial<EditablePageMeta>`. Keys left out stay untouched, and a key set to `undefined` or `null` is removed from the options of the page. A page whose options are not a plain object literal fails with `unsupported`, because the builder needs a literal to rewrite.
|
|
117
|
+
`ConfigurePage(ref, patch, opts?)` patches the metadata of the page in place and returns its ref and the `version` of the page file it wrote. A draft held across the patch saves against that version. The patch is a `Partial<EditablePageMeta>`. Keys left out stay untouched, and a key set to `undefined` or `null` is removed from the options of the page. A key the page reads as absent, such as an `icon` written as code the builder cannot read, is left as written, because a `null` there echoes what was read. A page whose options are not a plain object literal fails with `unsupported`, because the builder needs a literal to rewrite.
|
|
118
118
|
|
|
119
119
|
```typescript
|
|
120
120
|
import { ConfigurePage } from "@antelopejs/interface-dms-builder";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-dms-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "AntelopeJS interface for the DMS builder: create and edit pages, blocks, resources and queries in a DMS project from another module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
@@ -11,18 +11,19 @@
|
|
|
11
11
|
],
|
|
12
12
|
"homepage": "https://dms.antelopejs.com/docs/modules/builder/extending",
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/AntelopeJS/dms
|
|
14
|
+
"url": "https://github.com/AntelopeJS/dms/issues"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/AntelopeJS/dms
|
|
19
|
+
"url": "git+https://github.com/AntelopeJS/dms.git",
|
|
20
20
|
"directory": "packages/interface-dms-builder"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"LICENSE",
|
|
24
24
|
"README.md",
|
|
25
25
|
"dist",
|
|
26
|
+
"!dist/.tsbuildinfo",
|
|
26
27
|
"docs"
|
|
27
28
|
],
|
|
28
29
|
"main": "dist/index.js",
|
|
@@ -46,19 +47,25 @@
|
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "rimraf dist && tsc",
|
|
48
49
|
"typecheck": "tsc --noEmit",
|
|
49
|
-
"release": "release-it",
|
|
50
|
-
"prepack": "pnpm run build"
|
|
50
|
+
"release": "pnpm run typecheck && release-it",
|
|
51
|
+
"prepack": "pnpm run build",
|
|
52
|
+
"knip": "knip --dependencies",
|
|
53
|
+
"knip:all": "knip"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
|
-
"@antelopejs/interface-core": ">=0.
|
|
56
|
+
"@antelopejs/interface-core": ">=0.5.1 <1.0.0",
|
|
57
|
+
"@types/node": "^24.0.0",
|
|
58
|
+
"knip": "^6.34.0",
|
|
54
59
|
"release-it": "^19.2.4",
|
|
55
60
|
"release-it-changelogen": "^0.1.0",
|
|
56
61
|
"rimraf": "^6.1.3",
|
|
57
62
|
"typescript": "^6.0.3"
|
|
58
63
|
},
|
|
59
64
|
"peerDependencies": {
|
|
60
|
-
"@antelopejs/interface-core": ">=0.
|
|
65
|
+
"@antelopejs/interface-core": ">=0.5.1 <1.0.0"
|
|
61
66
|
},
|
|
62
67
|
"packageManager": "pnpm@10.2.0",
|
|
63
|
-
"antelopeJs": {
|
|
68
|
+
"antelopeJs": {
|
|
69
|
+
"standalone": true
|
|
70
|
+
}
|
|
64
71
|
}
|