@antelopejs/interface-dms-builder 0.2.4 → 0.3.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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <a href="https://www.npmjs.com/package/@antelopejs/interface-dms-builder"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-dms-builder?style=for-the-badge&labelColor=000000&color=000000"></a>
6
6
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-dms-builder?style=for-the-badge&labelColor=000000&color=000000"></a>
7
- <a href="https://discord.gg/sjK28QHrA7"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
7
+ <a href="https://discord.gg/g4pFt6eXqu"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
8
8
  <a href="https://dms.antelopejs.com/docs/modules/builder/extending"><img alt="Documentation" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
9
9
 
10
10
  The AntelopeJS interface to edit a DMS project's pages, blocks, resources and queries from code.
@@ -61,13 +61,15 @@ if (!block.ok) {
61
61
 
62
62
  The interface declares this peer dependency:
63
63
 
64
- <!-- docs-compat: ./package.json#peerDependencies -->
64
+ <!-- antelopejs-docs-compat:start -->
65
65
 
66
- | Package | Range |
67
- | --- | --- |
68
- | `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` |
66
+ | Package | Supported | Tested with |
67
+ | ---------------------------- | ---------------- | ----------- |
68
+ | `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` | — |
69
69
 
70
- The 0.2 interface is implemented by `@antelopejs/dms-builder` 0.4, used with DMS 0.9. These implementation versions are not peer dependencies of the interface package. The implementation runs in development only, unless its [`api.enabled`](https://dms.antelopejs.com/docs/modules/builder/configuration) option forces it.
70
+ <!-- antelopejs-docs-compat:end -->
71
+
72
+ The implementation runs in development only, unless its [`api.enabled`](https://dms.antelopejs.com/docs/modules/builder/configuration) option forces it.
71
73
 
72
74
  ## Documentation
73
75
 
@@ -1,3 +1,4 @@
1
+ import type { ColumnDisplay, TabsChoice } from "./mapping";
1
2
  import type { ResourceRef } from "./resources";
2
3
  import type { OpResult } from "./results";
3
4
  export type ProposedKind = "fact" | "default";
@@ -37,6 +38,7 @@ export interface ProposedField {
37
38
  filterable: Proposed<boolean>;
38
39
  sortable: Proposed<boolean>;
39
40
  searchable: Proposed<boolean>;
41
+ display?: Proposed<ColumnDisplay>;
40
42
  relation?: ProposedRelationColumn;
41
43
  profile?: ProposedColumnProfile;
42
44
  }
@@ -56,16 +58,19 @@ export interface ProposedResource {
56
58
  writable: Proposed<boolean>;
57
59
  label: Proposed<string | null>;
58
60
  title: Proposed<string>;
61
+ tabs?: Proposed<TabsChoice>;
59
62
  fields: ProposedField[];
60
63
  leftOut: ProposedLeftOut[];
61
64
  writes: ProposedWrites;
65
+ rows?: number;
66
+ profiledRows?: number;
62
67
  }
63
68
  export interface ProposedExclusion {
64
69
  relation: string;
65
70
  reason: string;
66
71
  rule: string;
67
72
  }
68
- export type OpenItemKind = "schemas" | "purpose" | "label" | "reference";
73
+ export type OpenItemKind = "schemas" | "purpose" | "label" | "reference" | "field-type";
69
74
  export interface OpenItemOption {
70
75
  value: unknown;
71
76
  title: string;
@@ -79,6 +84,7 @@ export interface OpenItem {
79
84
  options: OpenItemOption[];
80
85
  proposed: unknown;
81
86
  resource?: ProposedResource;
87
+ open?: OpenItem[];
82
88
  }
83
89
  export interface ProposalDatabase {
84
90
  engine: string;
@@ -1 +1 @@
1
- {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AAoLlD,QAAA,eAAe,GAC1B,IAAA,kCAAiB,GAEd,CAAC;AAMO,QAAA,WAAW,GACtB,IAAA,kCAAiB,GAAoD,CAAC;AAc3D,QAAA,UAAU,GACrB,IAAA,kCAAiB,GAEd,CAAC"}
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../src/analysis.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA8MlD,QAAA,eAAe,GAC1B,IAAA,kCAAiB,GAEd,CAAC;AAMO,QAAA,WAAW,GACtB,IAAA,kCAAiB,GAAoD,CAAC;AAc3D,QAAA,UAAU,GACrB,IAAA,kCAAiB,GAEd,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { BlockPath, PageRef } from "./addressing";
2
+ import type { QueryPreviewRequest } from "./drafts";
3
+ import type { AddQueryInput, QueryPreview, QueryRef } from "./queries";
4
+ import type { AddBlockInput, CreatePageInput } from "./operations";
5
+ import type { ResourceRef } from "./resources";
6
+ import type { MutationOpts, OpResult } from "./results";
7
+ export interface ComposeQuery extends Omit<AddQueryInput, "name"> {
8
+ name?: string;
9
+ into?: string;
10
+ args?: QueryPreviewRequest["args"];
11
+ }
12
+ export interface ComposeBlock {
13
+ name: string;
14
+ type: string;
15
+ config?: AddBlockInput["config"];
16
+ controller?: ResourceRef;
17
+ slot?: string;
18
+ meta?: AddBlockInput["meta"];
19
+ query?: ComposeQuery;
20
+ children?: ComposeBlock[];
21
+ }
22
+ export interface ComposePlacement {
23
+ before?: string;
24
+ after?: string;
25
+ }
26
+ export interface ComposePageInput {
27
+ page?: PageRef;
28
+ create?: CreatePageInput;
29
+ parent?: BlockPath;
30
+ placement?: ComposePlacement;
31
+ blocks: ComposeBlock[];
32
+ preview?: boolean;
33
+ args?: QueryPreviewRequest["args"];
34
+ tenant?: string;
35
+ }
36
+ export interface ComposedBlock {
37
+ pointer: string;
38
+ path: BlockPath;
39
+ query?: QueryRef;
40
+ route?: string;
41
+ }
42
+ export interface ComposePreviewBlock {
43
+ pointer: string;
44
+ block: string;
45
+ status: "ok" | "empty" | "zero" | "error";
46
+ preview?: QueryPreview;
47
+ error?: string;
48
+ }
49
+ export interface ComposePageData {
50
+ page: PageRef;
51
+ written: boolean;
52
+ blocks: ComposedBlock[];
53
+ previews: ComposePreviewBlock[];
54
+ }
55
+ export declare const ComposePage: (input: ComposePageInput, opts?: MutationOpts | undefined) => Promise<OpResult<ComposePageData>>;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ComposePage = void 0;
4
+ const interface_core_1 = require("@antelopejs/interface-core");
5
+ exports.ComposePage = (0, interface_core_1.InterfaceFunction)();
6
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA2GlD,QAAA,WAAW,GACtB,IAAA,kCAAiB,GAKd,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./addressing";
2
2
  export * from "./analysis";
3
3
  export * from "./catalog";
4
+ export * from "./compose";
4
5
  export * from "./drafts";
5
6
  export * from "./mapping";
6
7
  export * from "./operations";
@@ -9,4 +10,5 @@ export * from "./queries";
9
10
  export * from "./resources";
10
11
  export * from "./results";
11
12
  export * from "./structure";
13
+ export * from "./table-view";
12
14
  export * from "./values";
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./addressing"), exports);
18
18
  __exportStar(require("./analysis"), exports);
19
19
  __exportStar(require("./catalog"), exports);
20
+ __exportStar(require("./compose"), exports);
20
21
  __exportStar(require("./drafts"), exports);
21
22
  __exportStar(require("./mapping"), exports);
22
23
  __exportStar(require("./operations"), exports);
@@ -25,5 +26,6 @@ __exportStar(require("./queries"), exports);
25
26
  __exportStar(require("./resources"), exports);
26
27
  __exportStar(require("./results"), exports);
27
28
  __exportStar(require("./structure"), exports);
29
+ __exportStar(require("./table-view"), exports);
28
30
  __exportStar(require("./values"), exports);
29
31
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,6CAA2B;AAC3B,4CAA0B;AAC1B,2CAAyB;AACzB,4CAA0B;AAC1B,+CAA6B;AAC7B,4CAA0B;AAC1B,4CAA0B;AAC1B,8CAA4B;AAC5B,4CAA0B;AAC1B,8CAA4B;AAC5B,2CAAyB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,6CAA2B;AAC3B,4CAA0B;AAC1B,4CAA0B;AAC1B,2CAAyB;AACzB,4CAA0B;AAC1B,+CAA6B;AAC7B,4CAA0B;AAC1B,4CAA0B;AAC1B,8CAA4B;AAC5B,4CAA0B;AAC1B,8CAA4B;AAC5B,+CAA6B;AAC7B,2CAAyB"}
package/dist/mapping.d.ts CHANGED
@@ -1,6 +1,23 @@
1
1
  import type { CategoryRef, PageRef } from "./addressing";
2
2
  import type { ResourceRef } from "./resources";
3
3
  import type { OpResult } from "./results";
4
+ import type { TableViewSettings } from "./table-view";
5
+ export type DisplayTone = "neutral" | "primary" | "secondary" | "success" | "warning" | "error" | "info";
6
+ export interface BadgeItem {
7
+ value: string;
8
+ label: string;
9
+ tone: DisplayTone;
10
+ }
11
+ export type ColumnDisplay = {
12
+ kind: "badge";
13
+ items: BadgeItem[];
14
+ } | {
15
+ kind: "price";
16
+ currency?: string;
17
+ };
18
+ export interface TabsChoice {
19
+ column: string;
20
+ }
4
21
  export interface MappedField {
5
22
  column: string;
6
23
  declared?: string;
@@ -9,16 +26,22 @@ export interface MappedField {
9
26
  filterable?: boolean;
10
27
  sortable?: boolean;
11
28
  searchable?: boolean;
12
- relation?: string;
29
+ display?: ColumnDisplay | null;
30
+ relation?: string | null;
13
31
  }
14
32
  export interface MappedResource {
15
33
  relation: string;
16
34
  className?: string;
17
35
  label?: string;
18
36
  title?: string;
37
+ icon?: string;
38
+ description?: string;
39
+ order?: number;
40
+ tableOptions?: TableViewSettings;
19
41
  tableView: boolean;
20
42
  writable: boolean;
21
43
  fields: MappedField[];
44
+ tabs?: TabsChoice | null;
22
45
  }
23
46
  export interface ExcludedRelation {
24
47
  relation: string;
@@ -1 +1 @@
1
- {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AAmGlD,QAAA,YAAY,GACvB,IAAA,kCAAiB,GAEd,CAAC"}
1
+ {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AAgMlD,QAAA,YAAY,GACvB,IAAA,kCAAiB,GAEd,CAAC"}
package/dist/project.d.ts CHANGED
@@ -10,6 +10,7 @@ export interface ModuleEntryInput {
10
10
  env?: Record<string, string>;
11
11
  importOverrides?: ImportOverrideInput[];
12
12
  exportPriority?: Record<string, number>;
13
+ disabledExports?: string[];
13
14
  }
14
15
  export interface AddModuleStep {
15
16
  op: "AddModule";
@@ -26,15 +27,28 @@ export interface CreateLocalModuleStep {
26
27
  id: string;
27
28
  path: string;
28
29
  entry?: ModuleEntryInput;
30
+ connection?: string;
29
31
  }
30
- export type ProjectConfigStep = AddModuleStep | ConfigureModuleStep | CreateLocalModuleStep;
32
+ export interface RemoveModuleStep {
33
+ op: "RemoveModule";
34
+ id: string;
35
+ }
36
+ export interface RemoveConnectionStep {
37
+ op: "RemoveConnection";
38
+ interface: string;
39
+ id: string;
40
+ }
41
+ export type ProjectConfigStep = AddModuleStep | ConfigureModuleStep | CreateLocalModuleStep | RemoveModuleStep | RemoveConnectionStep;
31
42
  export interface ProjectConfigOpts {
32
43
  dryRun?: boolean;
33
44
  }
45
+ export interface CreateLocalModuleOpts extends ProjectConfigOpts {
46
+ connection?: string;
47
+ }
34
48
  export interface ProjectConfigResult {
35
49
  files: string[];
36
50
  }
37
51
  export declare const ConfigureProject: (steps: ProjectConfigStep[], opts?: ProjectConfigOpts | undefined) => Promise<OpResult<ProjectConfigResult>>;
38
52
  export declare const AddModule: (id: string, entry: ModuleEntryInput, opts?: ProjectConfigOpts | undefined) => Promise<OpResult<ProjectConfigResult>>;
39
53
  export declare const ConfigureModule: (id: string, entry: ModuleEntryInput, opts?: ProjectConfigOpts | undefined) => Promise<OpResult<ProjectConfigResult>>;
40
- export declare const CreateLocalModule: (id: string, path: string, entry?: ModuleEntryInput | undefined, opts?: ProjectConfigOpts | undefined) => Promise<OpResult<ProjectConfigResult>>;
54
+ export declare const CreateLocalModule: (id: string, path: string, entry?: ModuleEntryInput | undefined, opts?: CreateLocalModuleOpts | undefined) => Promise<OpResult<ProjectConfigResult>>;
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA8ElD,QAAA,gBAAgB,GAC3B,IAAA,kCAAiB,GAKd,CAAC;AAGO,QAAA,SAAS,GACpB,IAAA,kCAAiB,GAMd,CAAC;AAGO,QAAA,eAAe,GAC1B,IAAA,kCAAiB,GAMd,CAAC;AAGO,QAAA,iBAAiB,GAC5B,IAAA,kCAAiB,GAOd,CAAC"}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA6GlD,QAAA,gBAAgB,GAC3B,IAAA,kCAAiB,GAKd,CAAC;AAGO,QAAA,SAAS,GACpB,IAAA,kCAAiB,GAMd,CAAC;AAGO,QAAA,eAAe,GAC1B,IAAA,kCAAiB,GAMd,CAAC;AAGO,QAAA,iBAAiB,GAC5B,IAAA,kCAAiB,GAOd,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA0MlD,QAAA,kBAAkB,GAC7B,IAAA,kCAAiB,GAEd,CAAC;AAEO,QAAA,QAAQ,GACnB,IAAA,kCAAiB,GAMd,CAAC;AAEO,QAAA,cAAc,GACzB,IAAA,kCAAiB,GAMd,CAAC;AAEO,QAAA,WAAW,GACtB,IAAA,kCAAiB,GAEd,CAAC"}
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA2MlD,QAAA,kBAAkB,GAC7B,IAAA,kCAAiB,GAEd,CAAC;AAEO,QAAA,QAAQ,GACnB,IAAA,kCAAiB,GAMd,CAAC;AAEO,QAAA,cAAc,GACzB,IAAA,kCAAiB,GAMd,CAAC;AAEO,QAAA,WAAW,GACtB,IAAA,kCAAiB,GAEd,CAAC"}
package/dist/results.d.ts CHANGED
@@ -14,7 +14,7 @@ export interface TypecheckError {
14
14
  message: string;
15
15
  }
16
16
  export interface OpWarning {
17
- code: "query_opaque" | "query_kept" | "index_clamped" | "datatype_fallback" | "data_not_dropped" | "route_changed" | "data_kept" | "page_not_written" | "relation_missing";
17
+ code: "query_opaque" | "query_kept" | "index_clamped" | "datatype_fallback" | "data_not_dropped" | "route_changed" | "data_kept" | "page_not_written" | "relation_missing" | "display_not_proposed" | "module_not_loaded" | "setting_dropped" | "relation_skipped" | "display_dropped" | "field_flag_set" | "label_changed";
18
18
  message: string;
19
19
  }
20
20
  export type DuplicateScope = "page" | "siblings" | "category" | "resource" | "query" | "module";
@@ -0,0 +1,74 @@
1
+ import type { BlockPath } from "./addressing";
2
+ import type { MutationOpts, OpResult } from "./results";
3
+ export type TableDisplayId = "table" | "kanban" | "cards" | "grouped";
4
+ export interface KanbanSetting {
5
+ groupBy: string;
6
+ draggable?: boolean;
7
+ }
8
+ export interface GroupedSetting {
9
+ groupBy: string;
10
+ by?: "value" | "day" | "week";
11
+ collapsible?: boolean;
12
+ count?: boolean;
13
+ }
14
+ export interface CardsSetting {
15
+ fields?: string[];
16
+ }
17
+ export type ViewFilterMode = "is" | "is_not" | "include" | "exclude" | "contains" | "not_contains" | "greater_than" | "less_than" | "greater_than_or_equal_to" | "less_than_or_equal_to" | "is_empty" | "is_not_empty";
18
+ export interface ViewFilterSetting {
19
+ column: string;
20
+ mode?: ViewFilterMode;
21
+ value?: string | string[];
22
+ }
23
+ export interface ViewSetting {
24
+ id: string;
25
+ label: string;
26
+ icon?: string;
27
+ count?: boolean;
28
+ filters?: ViewFilterSetting[];
29
+ search?: string;
30
+ sort?: {
31
+ column: string;
32
+ desc?: boolean;
33
+ }[];
34
+ columns?: {
35
+ visible?: string[];
36
+ hidden?: string[];
37
+ order?: string[];
38
+ };
39
+ display?: TableDisplayId;
40
+ density?: "default" | "compact";
41
+ }
42
+ export interface ViewsSetting {
43
+ items: ViewSetting[];
44
+ layout?: "tabs" | "strip" | "menu";
45
+ defaultView?: string;
46
+ userViews?: boolean;
47
+ }
48
+ export interface FooterSummarySetting {
49
+ label: string;
50
+ op: "sum" | "count";
51
+ field?: string;
52
+ where?: {
53
+ column: string;
54
+ op: "equals" | "notEquals" | "in" | "notIn";
55
+ value: string | number | boolean | (string | number | boolean)[];
56
+ };
57
+ }
58
+ export interface FooterSetting {
59
+ summary?: FooterSummarySetting[];
60
+ }
61
+ export interface TableViewSettings {
62
+ kanban?: KanbanSetting | null;
63
+ grouped?: GroupedSetting | null;
64
+ cards?: CardsSetting | null;
65
+ defaultDisplay?: TableDisplayId | null;
66
+ views?: ViewsSetting | null;
67
+ footer?: FooterSetting | null;
68
+ }
69
+ export interface ConfigureTableViewOpts extends MutationOpts {
70
+ block?: string;
71
+ }
72
+ export declare const ConfigureTableView: (page: string, settings: TableViewSettings, opts?: ConfigureTableViewOpts | undefined) => Promise<OpResult<{
73
+ path: BlockPath;
74
+ }>>;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConfigureTableView = void 0;
4
+ const interface_core_1 = require("@antelopejs/interface-core");
5
+ exports.ConfigureTableView = (0, interface_core_1.InterfaceFunction)();
6
+ //# sourceMappingURL=table-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-view.js","sourceRoot":"","sources":["../src/table-view.ts"],"names":[],"mappings":";;;AAAA,+DAA+D;AA2IlD,QAAA,kBAAkB,GAC7B,IAAA,kCAAiB,GAMd,CAAC"}
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- A DMS project can show pages over a database it does not own: a client's PostgreSQL, read and written live through a postgres module in external mode. The builder takes such a database in four moves, each an operation:
5
+ A DMS project can show pages over a database it does not own: a client's PostgreSQL, MongoDB or RethinkDB, read and written live through that engine's module (`@antelopejs/postgres`, `@antelopejs/mongodb`, `@antelopejs/rethinkdb`) in external mode. The builder takes such a database in four moves, each an operation:
6
6
 
7
- 1. **Wire it** into `antelope.config.ts` with `ConfigureProject`: the provider module, a local module for its tables, and the builder's catalog connection.
7
+ 1. **Wire it** into `antelope.config.ts` with `ConfigureProject`: the provider module, a local module for its tables, the builder's catalog connection, and the DMS module's pin to the project's own database.
8
8
  2. **Analyse it** with `AnalyseDatabase`, which reads the catalog and proposes a mapping by fixed rules. It writes nothing.
9
9
  3. **Decide** the proposal: a person in the builder's *Database* workspace, or an assistant through these operations.
10
10
  4. **Map it** with `MapResources`, which writes table classes, controllers and TableView pages into the module bound to the database.
@@ -18,11 +18,23 @@ The builder reaches the database through `@antelopejs/interface-database-catalog
18
18
  | Step | Writes |
19
19
  | --- | --- |
20
20
  | `AddModule` | a module entry, refusing an id that exists |
21
- | `ConfigureModule` | merges config, `importOverrides` (pins and connection ids), `exportPriority` and env variable names into an entry |
22
- | `CreateLocalModule` | scaffolds a local module and adds its entry |
21
+ | `ConfigureModule` | merges config, `importOverrides` (pins and connection ids), `exportPriority`, `disabledExports` and env variable names into an entry |
22
+ | `CreateLocalModule` | scaffolds a local module and adds its entry; with `connection`, its record names that connection |
23
+ | `RemoveModule` | removes an entry and the `envOverrides` entries supplying its config, refusing one another module still reads through; its files stay |
24
+ | `RemoveConnection` | removes a named connection to an interface from every module's `importOverrides` |
23
25
 
24
26
  `env` takes variable names, never values: each is added to `envOverrides`, and to `.env.example` when the project has one.
25
27
 
28
+ The provider's entry depends on its engine:
29
+
30
+ | Engine | Package | `env` | `disabledExports` |
31
+ | --- | --- | --- | --- |
32
+ | PostgreSQL | `@antelopejs/postgres` | `url` | none |
33
+ | MongoDB | `@antelopejs/mongodb` | `url` and `database` | `@antelopejs/interface-mongodb` |
34
+ | RethinkDB | `@antelopejs/rethinkdb` | `url`, the database as its path | `@antelopejs/interface-rethinkdb` |
35
+
36
+ `disabledExports` keeps core from ever offering the external instance's raw client, so a module that needs the DMS's own MongoDB or RethinkDB client never gets the client's database instead.
37
+
26
38
  ```typescript
27
39
  import { ConfigureProject } from "@antelopejs/interface-dms-builder";
28
40
 
@@ -36,20 +48,22 @@ const result = await ConfigureProject([
36
48
  source: { type: "package", package: "@antelopejs/postgres", version: "^0.2.0" },
37
49
  config: { mode: "external", statementTimeoutMs: 30000 },
38
50
  env: { url: "ERP_DATABASE_URL" },
51
+ exportPriority: { "@antelopejs/interface-database": -1 },
39
52
  },
40
53
  },
41
54
  {
42
55
  op: "CreateLocalModule",
43
56
  id: "erp-tables",
44
57
  path: "modules/erp-tables",
58
+ connection: "erp",
45
59
  entry: {
46
60
  importOverrides: [{ interface: "@antelopejs/interface-database", source: "postgres-erp" }],
47
61
  },
48
62
  },
49
63
  {
50
64
  op: "ConfigureModule",
51
- id: "mongodb",
52
- entry: { exportPriority: { "@antelopejs/interface-database": 1 } },
65
+ id: "dms",
66
+ entry: { importOverrides: [{ interface: "@antelopejs/interface-database", source: "mongodb" }] },
53
67
  },
54
68
  {
55
69
  op: "ConfigureModule",
@@ -59,8 +73,21 @@ const result = await ConfigureProject([
59
73
  ]);
60
74
  ```
61
75
 
76
+ - The provider's `exportPriority` of `-1` keeps the project's own database the default for every module that does not pin `interface-database`.
77
+ - The tables module pins `interface-database` to the provider, and uses the DMS. Core passes that pin on to the DMS, which every other module reaches with the default, so the DMS pins the project's own database to settle it. A DMS that already pins `interface-database` needs nothing more.
78
+
62
79
  The connection exists once the project restarts with the variable set.
63
80
 
81
+ ### Remove a Connection
82
+
83
+ Removing a connection takes its catalog connections and its tables module out of the configuration, with `RemoveConnection` and `RemoveModule`. The tables module's folder is deleted with it. The database itself is untouched.
84
+
85
+ The tables module is the one whose `dmsBuilder.database` record names the connection, which `CreateLocalModule` writes with `connection` (the single-step `CreateLocalModule` takes it in its options). Another module that reads through the provider is never removed or deleted.
86
+
87
+ The provider goes too, with the variables that supply its configuration in `envOverrides`, `.env` and `.env.example`, only when it was added for this connection alone: its `exportPriority` for `@antelopejs/interface-database` is below the project's own database, and no module or connection the removal leaves names it. Otherwise it stays, since it may be the project's own database.
88
+
89
+ A removal is all or nothing: the folders are moved aside and `.env` is edited first, and both are put back when the configuration cannot be written.
90
+
64
91
  ## Analyse a Database
65
92
 
66
93
  `AnalyseDatabase({ connection, module, schemas? })` starts a job and answers it at once: profiling a database takes a while. `GetAnalysis(id)` answers the job's `progress` while it runs, then its `proposal`, or its `error`.
@@ -100,18 +127,33 @@ A **fact** follows from the catalog with no judgement. A **default** is a judgem
100
127
  | R2 | no key | stats only: table class and model, no page |
101
128
  | R3 | no rows | excluded |
102
129
  | R4 | a framework's or a migration tool's table | excluded |
103
- | R5 | keyed by its two foreign keys | stats only |
130
+ | R5 | keyed by its two references | stats only |
104
131
  | R6 | keyed, with rows and a label | TableView |
105
132
  | R7 | keyed, no label, referenced | TableView labelled by its key, and a `label` question |
106
133
  | R8 | keyed, no label, not referenced, with a time axis | stats only |
107
134
  | R9 | anything else | a `purpose` question, nothing proposed |
108
135
  | L1, L2 | the label by name, then by order; titles from short comments, else from names | |
109
- | F1–F5 | columns no type reads and secrets left out; the first declaration; declared foreign keys and references found whole as relation columns | |
136
+ | F1–F5 | columns no type reads and secrets left out; the first declaration; references the database declares, and references found whole, as relation columns | |
137
+ | D1 | a `string` column the profile calls enum-like, of 2 to 10 categories that cover every non-null row | `display`: a badge, one toned item per value, by rows |
138
+ | D2 | a `number` column whose name ends in `total`, `amount`, `price`, `cost`, `revenue` or `subtotal` | `display`: a price |
139
+ | D3 | a relation column the database did not comment | `label`: the target's title in singular, "Customer" not "Customer id"; `MapResources` takes it too when a field leaves `label` out |
140
+ | T1 | of the filterable badge columns of 2 to 6 values, the first named like `status`, `state`, `stage` or `type`, else the one with the fewest values | `tabs` on the resource |
110
141
  | W1 | every TableView | read-only |
111
142
 
143
+ A badge's tones come from the value: `paid`, `active`, `done`, `completed`, `shipped`, `delivered` and `approved` are `success`; `pending`, `draft`, `processing`, `waiting` and `review` are `warning`; `failed`, `cancelled`, `canceled`, `refunded`, `rejected`, `error` and `blocked` are `error`; `new` and `open` are `info`. Other values take `primary`, `secondary`, `info` and `neutral` in turn. A price uses the DataType's default currency.
144
+
112
145
  Each field also carries the declarations its column `accepts`, and its profile's aggregates: traits, nulls, distinct values, range and categories. Never a row.
113
146
 
114
- What the rules could not decide is in `open`, each item with its options and what the proposal holds meanwhile. A `purpose` item carries the `resource` it would map, should the answer be to map it.
147
+ Each resource also carries its `rows`, counted by the profile or estimated, when either is known.
148
+
149
+ What the rules could not decide is in `open`, each item with its options and what the proposal holds meanwhile:
150
+
151
+ - A `label` item offers every column a field reads, the key among them.
152
+ - A `reference` item offers a plain column, the target its values were found in, and every TableView whose single-column key accepts the column.
153
+ - A `purpose` item carries the `resource` it would map, should the answer be to map it, and in its own `open` the questions that resource leaves: they are asked only once the answer maps it.
154
+ - A `field-type` item asks which declaration reads a field of several native types.
155
+
156
+ A document database declares no field types, so its catalog reads them from a sample. When no type covers enough of a field's values (`int 60% | string 40%`), the field is left out and a `field-type` item asks which declaration reads it: its options are a declaration per type seen, and leaving it out. The `_id` of a collection without a key is never declared, so it is left out and asks nothing. `MapResources` accepts the declaration it answers. MongoDB and RethinkDB declare no references either, so F5 finds them all.
115
157
 
116
158
  ## Map Relations
117
159
 
@@ -133,14 +175,27 @@ const result = await MapResources({
133
175
  fields: [{ column: "name", searchable: true }, { column: "segment", filterable: true }],
134
176
  },
135
177
  ],
136
- excluded: [{ relation: "erp.audit_log", reason: "a log" }],
178
+ excluded: [{ relation: "erp.schema_migrations", reason: "a framework's or a migration tool's table" }],
137
179
  });
138
180
  ```
139
181
 
140
- - **Names are the database's own.** `_id` is declared only where the relation has a key, and nothing creates or alters a table.
182
+ - **Names are the database's own.** `_id` is declared only where the relation has a key, and nothing creates or alters a table. A MongoDB collection's key is `_id` itself, which is declared once.
141
183
  - **Relation columns** point at a relation of the batch, or one the module mapped before, by its single-column key.
142
- - **Mapping again** a relation already mapped rewrites it from the new choices. Its page is created, kept or removed as `tableView` says.
143
- - **`excluded`** is recorded in the module's `package.json`, so the next analysis does not propose those relations. Mapping one later removes it from the record.
184
+ - **A relation column's `label`** is written as sent. Left out, it keeps the label its controller holds if its relation was mapped before and held the column; otherwise it takes D3, the title of the relation it points at in singular ("Customer", not "Customer id"), as the target is titled by this call, by its page if it was mapped before, else by its name humanized. A column the database commented with a short first sentence takes that sentence instead (L2). A column that is not a relation column takes its name, humanized. A call that never sends a `label` therefore gets the analysis's labels on a first mapping and loses nothing on a remap.
185
+ - **A column's `display`** is `{ kind: "badge", items: [{ value, label, tone }] }` on a `string` column, or `{ kind: "price" }` on a `number` one. A relation column takes none. Left out, the column keeps the display it has if its relation was mapped before, read back from its controller as `GetMapping` reads it; if the relation is new, it takes the display D1 or D2 proposes. `null` writes the column plain, over any display it had. A call that never sends a `display` therefore gets the proposal on a first mapping and loses nothing on a remap.
186
+ - **Where a proposal comes from.** `MapResources` reads the profile of a new relation through the catalog connection at map time, since D1 needs it. When the profile cannot be read, it takes the proposal from the connection's last `AnalyseDatabase`; when there is none either, D2 still applies by the column's name, no column becomes a badge, and the result warns `display_not_proposed`. A display that does not suit the column's declaration, such as a badge on a column declared `number`, is dropped when it was kept or proposed, and refused only when sent.
187
+ - **A TableView's `tabs`**: `tabs: { column }` writes "All" and a tab per value of that column's badge to the TableView, over the `tabs` it holds; the column must be filterable and drawn as a badge. Left out, a page already mapped keeps its tabs, and a new relation takes the tabs T1 proposes among its filterable badge columns: a column left unfilterable is not tabbed. `null` writes none: it adds no tabs to a new relation, and removes from a page already mapped the tabs a mapping wrote from a badge, as `display: null` removes the badge. Tabs edited by hand are kept.
188
+ - **Mapping again** a relation already mapped rewrites it from the new choices. Its page is created, kept or removed as `tableView` says, and takes the new `title`. The module's relation columns pointing at it take its new `label`.
189
+ - **The module's `package.json`** records the connection and the `excluded` tables with their reasons, so the next analysis does not propose those tables. Mapping one later removes it from the record:
190
+
191
+ ```json
192
+ "dmsBuilder": {
193
+ "database": {
194
+ "connection": "erp",
195
+ "excludedTables": { "erp.schema_migrations": "a framework's or a migration tool's table" }
196
+ }
197
+ }
198
+ ```
144
199
  - **In a mapped module**, `CreateResource` and `AddField` are refused, since its tables are the database's. `DeleteResource` keeps the client's rows and warns `data_kept`.
145
200
 
146
201
  ## Next Steps
package/docs/6.queries.md CHANGED
@@ -228,7 +228,7 @@ await AddQuery("/dashboard", {
228
228
  | `op` | `"count" \| "sum" \| "avg" \| "min" \| "max"` | What to measure in each group. Defaults to `count`. |
229
229
  | `field` | `string` | The numeric field the measure reads. Required for every op but `count`. |
230
230
  | `groupBy` | `string` | The field the rows are grouped by. Required. |
231
- | `bucket` | `"day" \| "month" \| "quarter" \| "year"` | Groups a date field by period instead of by exact value. Only for a date field. |
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 for a date field. |
232
232
  | `timezone` | `string` | IANA zone the period is computed in. |
233
233
  | `orderBy` | `"group" \| "measure"` | Order the points by their group (a timeline) or by what was measured (a ranking). Defaults to `group`. |
234
234
  | `direction` | `"asc" \| "desc"` | Defaults to `asc`. |
@@ -39,7 +39,7 @@ Reads that use the envelope — `GetPageStructure`, `GetResourceStructure` — r
39
39
  | --- | --- | --- |
40
40
  | `path` | `string` | The file's absolute path. |
41
41
  | `kind` | `"create" \| "modify" \| "delete"` | What happened to the file. |
42
- | `diff` | `string` | A textual diff of the change. Empty for a deletion. |
42
+ | `diff` | `string` | The change as unified diff hunks (`@@ -a,b +c,d @@`, then lines prefixed with a space, `-` or `+`), with no file headers. Empty for a deletion. |
43
43
 
44
44
  A single operation usually touches more than one file — creating a page writes the page file and rewrites the barrel that exports it; creating a resource writes three files and rewrites a barrel.
45
45
 
@@ -66,6 +66,7 @@ if (result.ok) {
66
66
  | `index_clamped` | An `AddBlock` or `MoveBlock` index fell outside the sibling range and was clamped into it. |
67
67
  | `datatype_fallback` | A field's DataType is not one the builder maps, so it is handled as a string. |
68
68
  | `data_not_dropped` | `DeleteResource` removed the source but could not clear the resource's table data. |
69
+ | `display_not_proposed` | `MapResources` mapped a relation whose columns left `display` out, and neither a profile nor an analysis was there to propose a badge from, so its text columns were written plain. |
69
70
 
70
71
  ```typescript
71
72
  import { AddBlock } from "@antelopejs/interface-dms-builder";
@@ -4,7 +4,11 @@
4
4
  "rootDir": "..",
5
5
  "composite": false,
6
6
  "incremental": false,
7
- "noEmit": true
7
+ "noEmit": true,
8
+ "paths": {
9
+ "@antelopejs/interface-dms-builder": ["../src/index.ts"],
10
+ "@antelopejs/interface-dms-builder/*": ["../src/*"]
11
+ }
8
12
  },
9
13
  "include": ["../src/**/*.ts", "**/*.ts"]
10
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/interface-dms-builder",
3
- "version": "0.2.4",
3
+ "version": "0.3.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",