@dudousxd/nestjs-catalog 0.1.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/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/catalog.controller.d.ts +8 -0
- package/dist/catalog.controller.js +482 -0
- package/dist/catalog.decorators.d.ts +37 -0
- package/dist/catalog.decorators.js +50 -0
- package/dist/catalog.environment.d.ts +442 -0
- package/dist/catalog.environment.js +645 -0
- package/dist/catalog.events.d.ts +179 -0
- package/dist/catalog.events.js +110 -0
- package/dist/catalog.module.d.ts +5 -0
- package/dist/catalog.module.js +71 -0
- package/dist/catalog.options.d.ts +79 -0
- package/dist/catalog.options.js +4 -0
- package/dist/catalog.overlay-store.d.ts +25 -0
- package/dist/catalog.overlay-store.js +44 -0
- package/dist/catalog.overlay-store.token.d.ts +1 -0
- package/dist/catalog.overlay-store.token.js +4 -0
- package/dist/catalog.pipeline.d.ts +800 -0
- package/dist/catalog.pipeline.js +606 -0
- package/dist/catalog.principal.d.ts +209 -0
- package/dist/catalog.principal.js +245 -0
- package/dist/catalog.query-cache.d.ts +25 -0
- package/dist/catalog.query-cache.js +0 -0
- package/dist/catalog.query.d.ts +76 -0
- package/dist/catalog.query.js +64 -0
- package/dist/catalog.registry.base.d.ts +21 -0
- package/dist/catalog.registry.base.js +17 -0
- package/dist/catalog.registry.d.ts +44 -0
- package/dist/catalog.registry.js +359 -0
- package/dist/catalog.service.d.ts +115 -0
- package/dist/catalog.service.js +366 -0
- package/dist/catalog.store.d.ts +419 -0
- package/dist/catalog.store.js +175 -0
- package/dist/catalog.types.d.ts +165 -0
- package/dist/catalog.types.js +19 -0
- package/dist/catalog.workspace.d.ts +426 -0
- package/dist/catalog.workspace.js +87 -0
- package/dist/client.d.ts +86 -0
- package/dist/client.js +83 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +109 -0
- package/dist/stores/mikro-orm-read.store.d.ts +20 -0
- package/dist/stores/mikro-orm-read.store.js +120 -0
- package/dist/transform-runner.d.ts +54 -0
- package/dist/transform-runner.js +280 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Davide Carvalho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# @dudousxd/nestjs-catalog
|
|
2
|
+
|
|
3
|
+
A metadata registry for NestJS. It reads your ORM and gives you back your data
|
|
4
|
+
model as data: object types, their properties, their relations — queryable over
|
|
5
|
+
HTTP, and labelled by humans rather than by whoever named the class.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @dudousxd/nestjs-catalog
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Most applications describe their model three or four times: once in the entity,
|
|
14
|
+
once in a filter definition, once in a table component, once in whatever the
|
|
15
|
+
admin screen is. Each copy drifts.
|
|
16
|
+
|
|
17
|
+
This library takes the position that **structure should be derived and semantics
|
|
18
|
+
should be declared.** Column names, SQL types, nullability and foreign keys are
|
|
19
|
+
read off the ORM, because the database already knows them. What the database
|
|
20
|
+
cannot know — what a thing is called, what it means, what unit it is in, who may
|
|
21
|
+
see it — is declared once and served to everything downstream.
|
|
22
|
+
|
|
23
|
+
## Mount it
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { CatalogModule } from "@dudousxd/nestjs-catalog";
|
|
27
|
+
|
|
28
|
+
@Module({
|
|
29
|
+
imports: [
|
|
30
|
+
CatalogModule.forRoot({
|
|
31
|
+
path: "catalog",
|
|
32
|
+
// No guard ships by default. An endpoint that enumerates every table in
|
|
33
|
+
// your database should never be open because a library decided so.
|
|
34
|
+
imports: [AuthModule],
|
|
35
|
+
guards: [RolesGuard],
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
})
|
|
39
|
+
export class AppModule {}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`imports` is not decoration: Nest builds a guard from the injector of the module
|
|
43
|
+
that *declares* the controller, and this library generates its own. Whatever
|
|
44
|
+
your guards inject has to be resolvable from there.
|
|
45
|
+
|
|
46
|
+
## Declare what the database cannot know
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
@CatalogType({
|
|
50
|
+
displayName: "Vehicle",
|
|
51
|
+
group: "Fleet",
|
|
52
|
+
icon: "🚚",
|
|
53
|
+
description: "One registered vehicle. Work orders and funding hang off it.",
|
|
54
|
+
})
|
|
55
|
+
@Entity()
|
|
56
|
+
export class Mvr {
|
|
57
|
+
@CatalogProperty({ displayName: "Risk Score", unit: "0–100" })
|
|
58
|
+
@Property({ fieldName: "Risk Score" })
|
|
59
|
+
riskScore?: number;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Entities with no decorators still appear — with a name derived from the class,
|
|
64
|
+
which is exactly the point: the console shows you which types nobody has named
|
|
65
|
+
yet.
|
|
66
|
+
|
|
67
|
+
## The HTTP surface
|
|
68
|
+
|
|
69
|
+
| Method | Path | What it does |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `GET` | `/catalog` | The whole model, as data |
|
|
72
|
+
| `GET` | `/catalog/graph` | Nodes and edges, for drawing it |
|
|
73
|
+
| `GET` | `/catalog/types/:name` | One type |
|
|
74
|
+
| `PATCH` | `/catalog/types/:name` | Rename, regroup, re-describe |
|
|
75
|
+
| `PATCH` | `/catalog/types/:name/properties/:property` | Label, unit, description, visibility |
|
|
76
|
+
| `POST` | `/catalog/reset` | Drop every runtime edit |
|
|
77
|
+
| `GET` | `/catalog/objects/:name` | Read rows of any catalogued type |
|
|
78
|
+
|
|
79
|
+
Everything a `PATCH` can change is presentation-only. **No route in this library
|
|
80
|
+
issues DDL, and the catalog never writes to your tables.** Runtime edits go to an
|
|
81
|
+
overlay store (a JSON file by default; bring your own via `overlayStore`).
|
|
82
|
+
|
|
83
|
+
`GET /catalog/objects/:name` validates the type name, the sort column and the
|
|
84
|
+
searched columns against the catalog before anything reaches SQL, selects only
|
|
85
|
+
the columns the catalog says are visible, and caps the page size.
|
|
86
|
+
|
|
87
|
+
## Build your own endpoints, or use ours
|
|
88
|
+
|
|
89
|
+
The built-in controller is a convenience, not the interface. Pass
|
|
90
|
+
`controller: false` and inject `CatalogService` — by class, there is no token to
|
|
91
|
+
import — to publish whatever HTTP surface your app already uses:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
@Controller("data-model")
|
|
95
|
+
export class MyController {
|
|
96
|
+
constructor(private readonly catalog: CatalogService) {}
|
|
97
|
+
|
|
98
|
+
@Get() model() { return this.catalog.getSnapshot(); }
|
|
99
|
+
@Get("graph") graph() { return this.catalog.getGraph(); }
|
|
100
|
+
@Get(":type") rows(@Param("type") t: string, @Query() q) {
|
|
101
|
+
return this.catalog.readObjects(t, q);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Everything the built-in controller does is on that one service — the model, the
|
|
107
|
+
graph, presentation edits, reads, and the snapshot list. The registry and the
|
|
108
|
+
store stay injectable for anything more unusual, but an ordinary endpoint should
|
|
109
|
+
not need them.
|
|
110
|
+
|
|
111
|
+
## Build your own UI, or use ours
|
|
112
|
+
|
|
113
|
+
The endpoints are the product. Import `@dudousxd/nestjs-catalog/client` for the
|
|
114
|
+
response types and route builders — no NestJS, no ORM, browser-safe — and write
|
|
115
|
+
whatever screens you want.
|
|
116
|
+
|
|
117
|
+
If you would rather not, [`@dudousxd/nestjs-catalog-react`](../nestjs-catalog-react)
|
|
118
|
+
ships a model manager and a generic object explorer that you drop into your own
|
|
119
|
+
app shell.
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Type } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Built as a factory rather than a plain class because the route prefix and the
|
|
4
|
+
* guards both come from `forRoot`. A library that hardcodes either one forces
|
|
5
|
+
* every host app to accept its idea of auth, which for an endpoint that
|
|
6
|
+
* enumerates every table in the database is not a reasonable default.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createCatalogController(path: string, guards: Type<unknown>[], decorators?: ClassDecorator[]): Type<unknown>;
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createCatalogController = createCatalogController;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const catalog_query_cache_1 = require("./catalog.query-cache");
|
|
18
|
+
const catalog_registry_base_1 = require("./catalog.registry.base");
|
|
19
|
+
const catalog_service_1 = require("./catalog.service");
|
|
20
|
+
const catalog_workspace_1 = require("./catalog.workspace");
|
|
21
|
+
/**
|
|
22
|
+
* Built as a factory rather than a plain class because the route prefix and the
|
|
23
|
+
* guards both come from `forRoot`. A library that hardcodes either one forces
|
|
24
|
+
* every host app to accept its idea of auth, which for an endpoint that
|
|
25
|
+
* enumerates every table in the database is not a reasonable default.
|
|
26
|
+
*/
|
|
27
|
+
function createCatalogController(path, guards, decorators = []) {
|
|
28
|
+
let CatalogController = class CatalogController {
|
|
29
|
+
registry;
|
|
30
|
+
service;
|
|
31
|
+
traces;
|
|
32
|
+
constructor(registry, service, traces) {
|
|
33
|
+
this.registry = registry;
|
|
34
|
+
this.service = service;
|
|
35
|
+
this.traces = traces;
|
|
36
|
+
}
|
|
37
|
+
requireTraces() {
|
|
38
|
+
if (!this.traces) {
|
|
39
|
+
throw new common_1.BadRequestException('This catalog has no trace store, so the audit trail can be listed but not grouped into traces.');
|
|
40
|
+
}
|
|
41
|
+
return this.traces;
|
|
42
|
+
}
|
|
43
|
+
/** The whole ontology, as data. */
|
|
44
|
+
snapshot() {
|
|
45
|
+
return this.registry.getSnapshot();
|
|
46
|
+
}
|
|
47
|
+
/** Nodes and edges, for drawing it. */
|
|
48
|
+
graph() {
|
|
49
|
+
return this.registry.getGraph();
|
|
50
|
+
}
|
|
51
|
+
type(name) {
|
|
52
|
+
const type = this.registry.getType(name);
|
|
53
|
+
if (!type)
|
|
54
|
+
throw new common_1.NotFoundException(`Unknown object type: ${name}`);
|
|
55
|
+
return type;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Tier 0. Renames a type, regroups it, changes its icon. No migration, no
|
|
59
|
+
* deploy, no engineer.
|
|
60
|
+
*/
|
|
61
|
+
async patchType(name, body) {
|
|
62
|
+
const updated = await this.registry.patchType(name, body);
|
|
63
|
+
if (!updated)
|
|
64
|
+
throw new common_1.NotFoundException(`Unknown object type: ${name}`);
|
|
65
|
+
return updated;
|
|
66
|
+
}
|
|
67
|
+
/** Tier 0, one property at a time. */
|
|
68
|
+
async patchProperty(name, property, body) {
|
|
69
|
+
const updated = await this.registry.patchProperty(name, property, body);
|
|
70
|
+
if (!updated) {
|
|
71
|
+
throw new common_1.NotFoundException(`Unknown property: ${name}.${property}`);
|
|
72
|
+
}
|
|
73
|
+
return updated;
|
|
74
|
+
}
|
|
75
|
+
/** Drops every tier-0 edit and falls back to what the ORM says. */
|
|
76
|
+
async reset() {
|
|
77
|
+
await this.registry.resetOverlay();
|
|
78
|
+
return this.registry.getSnapshot();
|
|
79
|
+
}
|
|
80
|
+
/** One generic read endpoint for every type in the catalog. */
|
|
81
|
+
objects(name, page, size, search, sort, dir, snapshot) {
|
|
82
|
+
return this.service.readObjects(name, {
|
|
83
|
+
page: page ? Number(page) : undefined,
|
|
84
|
+
size: size ? Number(size) : undefined,
|
|
85
|
+
search,
|
|
86
|
+
sort,
|
|
87
|
+
dir: dir === 'desc' ? 'desc' : 'asc',
|
|
88
|
+
snapshot,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** What an ad-hoc query may select from, and whether it can run at all. */
|
|
92
|
+
queryRelations() {
|
|
93
|
+
return this.service.queryRelations();
|
|
94
|
+
}
|
|
95
|
+
/** Run one read-only statement. */
|
|
96
|
+
runQuery(body) {
|
|
97
|
+
return this.service.runQuery(body ?? { sql: '' });
|
|
98
|
+
}
|
|
99
|
+
/** Everything a saved query or dashboard needs to know is here. */
|
|
100
|
+
workspaceCapabilities() {
|
|
101
|
+
return {
|
|
102
|
+
workspace: this.service.workspaceAvailable(),
|
|
103
|
+
...this.service.capabilities(),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
savedQueries() {
|
|
107
|
+
return this.service.listSavedQueries();
|
|
108
|
+
}
|
|
109
|
+
saveSavedQuery(body) {
|
|
110
|
+
return this.service.saveQuery(body, body?.createdBy ?? 'console');
|
|
111
|
+
}
|
|
112
|
+
savedQuery(id) {
|
|
113
|
+
return this.service.getSavedQuery(id);
|
|
114
|
+
}
|
|
115
|
+
patchSavedQuery(id, body) {
|
|
116
|
+
return this.service.updateSavedQuery(id, body);
|
|
117
|
+
}
|
|
118
|
+
removeSavedQuery(id) {
|
|
119
|
+
return this.service.deleteSavedQuery(id).then((deleted) => ({ deleted }));
|
|
120
|
+
}
|
|
121
|
+
/** Run a saved query, honouring the cache TTL it was saved with. */
|
|
122
|
+
runSavedQuery(id, body) {
|
|
123
|
+
return this.service.runSavedQuery(id, body?.maxRows);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The same result as CSV.
|
|
127
|
+
*
|
|
128
|
+
* A GET, not a POST, so it can be a plain link — a download that only works
|
|
129
|
+
* from JavaScript cannot be pasted into a mail or a scheduled job.
|
|
130
|
+
*/
|
|
131
|
+
async exportSavedQuery(id, response) {
|
|
132
|
+
const { savedQuery, result } = await this.service.runSavedQuery(id);
|
|
133
|
+
const filename = `${savedQuery.name.replace(/[^A-Za-z0-9_-]+/g, '-')}.csv`;
|
|
134
|
+
response.setHeader('content-type', 'text/csv; charset=utf-8');
|
|
135
|
+
response.setHeader('content-disposition', `attachment; filename="${filename}"`);
|
|
136
|
+
return (0, catalog_query_cache_1.toCsv)(result);
|
|
137
|
+
}
|
|
138
|
+
dashboards() {
|
|
139
|
+
return this.service.listDashboards();
|
|
140
|
+
}
|
|
141
|
+
createDashboard(body) {
|
|
142
|
+
return this.service.saveDashboard(body, body?.createdBy ?? 'console');
|
|
143
|
+
}
|
|
144
|
+
dashboard(id) {
|
|
145
|
+
return this.service.getDashboard(id);
|
|
146
|
+
}
|
|
147
|
+
patchDashboard(id, body) {
|
|
148
|
+
return this.service.updateDashboard(id, body);
|
|
149
|
+
}
|
|
150
|
+
removeDashboard(id) {
|
|
151
|
+
return this.service.deleteDashboard(id).then((deleted) => ({ deleted }));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* What this caller may embed.
|
|
155
|
+
*
|
|
156
|
+
* A discovery endpoint so a consuming frontend can list what it is allowed
|
|
157
|
+
* to render rather than being told the ids out of band.
|
|
158
|
+
*/
|
|
159
|
+
embeddable() {
|
|
160
|
+
return this.service.listEmbeddable();
|
|
161
|
+
}
|
|
162
|
+
/** A whole dashboard, every chart resolved and ready to draw. */
|
|
163
|
+
embedDashboard(id) {
|
|
164
|
+
return this.service.embedDashboard(id);
|
|
165
|
+
}
|
|
166
|
+
/** One chart, for a consumer that wants to place it itself. */
|
|
167
|
+
embedChart(id) {
|
|
168
|
+
return this.service.embedChart(id);
|
|
169
|
+
}
|
|
170
|
+
/** The audit trail: what happened, to what, by whom. */
|
|
171
|
+
events(event, typeName, principalId, since, limit) {
|
|
172
|
+
return this.service.listEvents({
|
|
173
|
+
event,
|
|
174
|
+
typeName,
|
|
175
|
+
principalId,
|
|
176
|
+
since,
|
|
177
|
+
limit: limit ? Number(limit) : undefined,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The same trail, grouped into causal stories.
|
|
182
|
+
*
|
|
183
|
+
* Declared after `events` and before anything with a parameter in that
|
|
184
|
+
* position, so `traces` is matched as the literal it is. Nest resolves
|
|
185
|
+
* routes in declaration order, and a `:something` registered first would
|
|
186
|
+
* swallow this path and answer it with a lookup for an event named
|
|
187
|
+
* "traces" — a 404 nobody could explain.
|
|
188
|
+
*
|
|
189
|
+
* `event` and `outcome` pick *which traces* come back, never which spans:
|
|
190
|
+
* a trace returned with only its matching events would be a story with
|
|
191
|
+
* pages missing that still looked complete.
|
|
192
|
+
*/
|
|
193
|
+
traceList(typeName, principalId, event, outcome, since, limit, offset) {
|
|
194
|
+
return this.requireTraces().listTraces({
|
|
195
|
+
typeName,
|
|
196
|
+
principalId,
|
|
197
|
+
event,
|
|
198
|
+
outcome: parseOutcomes(outcome),
|
|
199
|
+
since,
|
|
200
|
+
limit: limit ? Number(limit) : undefined,
|
|
201
|
+
offset: offset ? Number(offset) : undefined,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/** One story in full, by correlation id. */
|
|
205
|
+
async trace(id) {
|
|
206
|
+
const found = await this.requireTraces().getTrace(id);
|
|
207
|
+
if (!found) {
|
|
208
|
+
throw new common_1.NotFoundException(`No trace ${id} in the audit trail.`);
|
|
209
|
+
}
|
|
210
|
+
return found;
|
|
211
|
+
}
|
|
212
|
+
/** Every load of this type: when, by whom, how many rows. */
|
|
213
|
+
snapshots(name) {
|
|
214
|
+
return this.service.listSnapshots(name);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
__decorate([
|
|
218
|
+
(0, common_1.Get)(),
|
|
219
|
+
__metadata("design:type", Function),
|
|
220
|
+
__metadata("design:paramtypes", []),
|
|
221
|
+
__metadata("design:returntype", void 0)
|
|
222
|
+
], CatalogController.prototype, "snapshot", null);
|
|
223
|
+
__decorate([
|
|
224
|
+
(0, common_1.Get)('graph'),
|
|
225
|
+
__metadata("design:type", Function),
|
|
226
|
+
__metadata("design:paramtypes", []),
|
|
227
|
+
__metadata("design:returntype", void 0)
|
|
228
|
+
], CatalogController.prototype, "graph", null);
|
|
229
|
+
__decorate([
|
|
230
|
+
(0, common_1.Get)('types/:name'),
|
|
231
|
+
__param(0, (0, common_1.Param)('name')),
|
|
232
|
+
__metadata("design:type", Function),
|
|
233
|
+
__metadata("design:paramtypes", [String]),
|
|
234
|
+
__metadata("design:returntype", void 0)
|
|
235
|
+
], CatalogController.prototype, "type", null);
|
|
236
|
+
__decorate([
|
|
237
|
+
(0, common_1.Patch)('types/:name'),
|
|
238
|
+
__param(0, (0, common_1.Param)('name')),
|
|
239
|
+
__param(1, (0, common_1.Body)()),
|
|
240
|
+
__metadata("design:type", Function),
|
|
241
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
242
|
+
__metadata("design:returntype", Promise)
|
|
243
|
+
], CatalogController.prototype, "patchType", null);
|
|
244
|
+
__decorate([
|
|
245
|
+
(0, common_1.Patch)('types/:name/properties/:property'),
|
|
246
|
+
__param(0, (0, common_1.Param)('name')),
|
|
247
|
+
__param(1, (0, common_1.Param)('property')),
|
|
248
|
+
__param(2, (0, common_1.Body)()),
|
|
249
|
+
__metadata("design:type", Function),
|
|
250
|
+
__metadata("design:paramtypes", [String, String, Object]),
|
|
251
|
+
__metadata("design:returntype", Promise)
|
|
252
|
+
], CatalogController.prototype, "patchProperty", null);
|
|
253
|
+
__decorate([
|
|
254
|
+
(0, common_1.Post)('reset'),
|
|
255
|
+
__metadata("design:type", Function),
|
|
256
|
+
__metadata("design:paramtypes", []),
|
|
257
|
+
__metadata("design:returntype", Promise)
|
|
258
|
+
], CatalogController.prototype, "reset", null);
|
|
259
|
+
__decorate([
|
|
260
|
+
(0, common_1.Get)('objects/:name'),
|
|
261
|
+
__param(0, (0, common_1.Param)('name')),
|
|
262
|
+
__param(1, (0, common_1.Query)('page')),
|
|
263
|
+
__param(2, (0, common_1.Query)('size')),
|
|
264
|
+
__param(3, (0, common_1.Query)('search')),
|
|
265
|
+
__param(4, (0, common_1.Query)('sort')),
|
|
266
|
+
__param(5, (0, common_1.Query)('dir')),
|
|
267
|
+
__param(6, (0, common_1.Query)('snapshot')),
|
|
268
|
+
__metadata("design:type", Function),
|
|
269
|
+
__metadata("design:paramtypes", [String, String, String, String, String, String, String]),
|
|
270
|
+
__metadata("design:returntype", void 0)
|
|
271
|
+
], CatalogController.prototype, "objects", null);
|
|
272
|
+
__decorate([
|
|
273
|
+
(0, common_1.Get)('query/relations'),
|
|
274
|
+
__metadata("design:type", Function),
|
|
275
|
+
__metadata("design:paramtypes", []),
|
|
276
|
+
__metadata("design:returntype", void 0)
|
|
277
|
+
], CatalogController.prototype, "queryRelations", null);
|
|
278
|
+
__decorate([
|
|
279
|
+
(0, common_1.Post)('query'),
|
|
280
|
+
__param(0, (0, common_1.Body)()),
|
|
281
|
+
__metadata("design:type", Function),
|
|
282
|
+
__metadata("design:paramtypes", [Object]),
|
|
283
|
+
__metadata("design:returntype", void 0)
|
|
284
|
+
], CatalogController.prototype, "runQuery", null);
|
|
285
|
+
__decorate([
|
|
286
|
+
(0, common_1.Get)('workspace/capabilities'),
|
|
287
|
+
__metadata("design:type", Function),
|
|
288
|
+
__metadata("design:paramtypes", []),
|
|
289
|
+
__metadata("design:returntype", void 0)
|
|
290
|
+
], CatalogController.prototype, "workspaceCapabilities", null);
|
|
291
|
+
__decorate([
|
|
292
|
+
(0, common_1.Get)('saved-queries'),
|
|
293
|
+
__metadata("design:type", Function),
|
|
294
|
+
__metadata("design:paramtypes", []),
|
|
295
|
+
__metadata("design:returntype", void 0)
|
|
296
|
+
], CatalogController.prototype, "savedQueries", null);
|
|
297
|
+
__decorate([
|
|
298
|
+
(0, common_1.Post)('saved-queries'),
|
|
299
|
+
__param(0, (0, common_1.Body)()),
|
|
300
|
+
__metadata("design:type", Function),
|
|
301
|
+
__metadata("design:paramtypes", [Object]),
|
|
302
|
+
__metadata("design:returntype", void 0)
|
|
303
|
+
], CatalogController.prototype, "saveSavedQuery", null);
|
|
304
|
+
__decorate([
|
|
305
|
+
(0, common_1.Get)('saved-queries/:id'),
|
|
306
|
+
__param(0, (0, common_1.Param)('id')),
|
|
307
|
+
__metadata("design:type", Function),
|
|
308
|
+
__metadata("design:paramtypes", [String]),
|
|
309
|
+
__metadata("design:returntype", void 0)
|
|
310
|
+
], CatalogController.prototype, "savedQuery", null);
|
|
311
|
+
__decorate([
|
|
312
|
+
(0, common_1.Patch)('saved-queries/:id'),
|
|
313
|
+
__param(0, (0, common_1.Param)('id')),
|
|
314
|
+
__param(1, (0, common_1.Body)()),
|
|
315
|
+
__metadata("design:type", Function),
|
|
316
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
317
|
+
__metadata("design:returntype", void 0)
|
|
318
|
+
], CatalogController.prototype, "patchSavedQuery", null);
|
|
319
|
+
__decorate([
|
|
320
|
+
(0, common_1.Delete)('saved-queries/:id'),
|
|
321
|
+
__param(0, (0, common_1.Param)('id')),
|
|
322
|
+
__metadata("design:type", Function),
|
|
323
|
+
__metadata("design:paramtypes", [String]),
|
|
324
|
+
__metadata("design:returntype", void 0)
|
|
325
|
+
], CatalogController.prototype, "removeSavedQuery", null);
|
|
326
|
+
__decorate([
|
|
327
|
+
(0, common_1.Post)('saved-queries/:id/run'),
|
|
328
|
+
__param(0, (0, common_1.Param)('id')),
|
|
329
|
+
__param(1, (0, common_1.Body)()),
|
|
330
|
+
__metadata("design:type", Function),
|
|
331
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
332
|
+
__metadata("design:returntype", void 0)
|
|
333
|
+
], CatalogController.prototype, "runSavedQuery", null);
|
|
334
|
+
__decorate([
|
|
335
|
+
(0, common_1.Get)('saved-queries/:id/export.csv'),
|
|
336
|
+
__param(0, (0, common_1.Param)('id')),
|
|
337
|
+
__param(1, (0, common_1.Res)({ passthrough: true })),
|
|
338
|
+
__metadata("design:type", Function),
|
|
339
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
340
|
+
__metadata("design:returntype", Promise)
|
|
341
|
+
], CatalogController.prototype, "exportSavedQuery", null);
|
|
342
|
+
__decorate([
|
|
343
|
+
(0, common_1.Get)('dashboards'),
|
|
344
|
+
__metadata("design:type", Function),
|
|
345
|
+
__metadata("design:paramtypes", []),
|
|
346
|
+
__metadata("design:returntype", void 0)
|
|
347
|
+
], CatalogController.prototype, "dashboards", null);
|
|
348
|
+
__decorate([
|
|
349
|
+
(0, common_1.Post)('dashboards'),
|
|
350
|
+
__param(0, (0, common_1.Body)()),
|
|
351
|
+
__metadata("design:type", Function),
|
|
352
|
+
__metadata("design:paramtypes", [Object]),
|
|
353
|
+
__metadata("design:returntype", void 0)
|
|
354
|
+
], CatalogController.prototype, "createDashboard", null);
|
|
355
|
+
__decorate([
|
|
356
|
+
(0, common_1.Get)('dashboards/:id'),
|
|
357
|
+
__param(0, (0, common_1.Param)('id')),
|
|
358
|
+
__metadata("design:type", Function),
|
|
359
|
+
__metadata("design:paramtypes", [String]),
|
|
360
|
+
__metadata("design:returntype", void 0)
|
|
361
|
+
], CatalogController.prototype, "dashboard", null);
|
|
362
|
+
__decorate([
|
|
363
|
+
(0, common_1.Patch)('dashboards/:id'),
|
|
364
|
+
__param(0, (0, common_1.Param)('id')),
|
|
365
|
+
__param(1, (0, common_1.Body)()),
|
|
366
|
+
__metadata("design:type", Function),
|
|
367
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
368
|
+
__metadata("design:returntype", void 0)
|
|
369
|
+
], CatalogController.prototype, "patchDashboard", null);
|
|
370
|
+
__decorate([
|
|
371
|
+
(0, common_1.Delete)('dashboards/:id'),
|
|
372
|
+
__param(0, (0, common_1.Param)('id')),
|
|
373
|
+
__metadata("design:type", Function),
|
|
374
|
+
__metadata("design:paramtypes", [String]),
|
|
375
|
+
__metadata("design:returntype", void 0)
|
|
376
|
+
], CatalogController.prototype, "removeDashboard", null);
|
|
377
|
+
__decorate([
|
|
378
|
+
(0, common_1.Get)('embed'),
|
|
379
|
+
__metadata("design:type", Function),
|
|
380
|
+
__metadata("design:paramtypes", []),
|
|
381
|
+
__metadata("design:returntype", void 0)
|
|
382
|
+
], CatalogController.prototype, "embeddable", null);
|
|
383
|
+
__decorate([
|
|
384
|
+
(0, common_1.Get)('embed/dashboards/:id'),
|
|
385
|
+
__param(0, (0, common_1.Param)('id')),
|
|
386
|
+
__metadata("design:type", Function),
|
|
387
|
+
__metadata("design:paramtypes", [String]),
|
|
388
|
+
__metadata("design:returntype", void 0)
|
|
389
|
+
], CatalogController.prototype, "embedDashboard", null);
|
|
390
|
+
__decorate([
|
|
391
|
+
(0, common_1.Get)('embed/charts/:id'),
|
|
392
|
+
__param(0, (0, common_1.Param)('id')),
|
|
393
|
+
__metadata("design:type", Function),
|
|
394
|
+
__metadata("design:paramtypes", [String]),
|
|
395
|
+
__metadata("design:returntype", void 0)
|
|
396
|
+
], CatalogController.prototype, "embedChart", null);
|
|
397
|
+
__decorate([
|
|
398
|
+
(0, common_1.Get)('events'),
|
|
399
|
+
__param(0, (0, common_1.Query)('event')),
|
|
400
|
+
__param(1, (0, common_1.Query)('type')),
|
|
401
|
+
__param(2, (0, common_1.Query)('principal')),
|
|
402
|
+
__param(3, (0, common_1.Query)('since')),
|
|
403
|
+
__param(4, (0, common_1.Query)('limit')),
|
|
404
|
+
__metadata("design:type", Function),
|
|
405
|
+
__metadata("design:paramtypes", [String, String, String, String, String]),
|
|
406
|
+
__metadata("design:returntype", void 0)
|
|
407
|
+
], CatalogController.prototype, "events", null);
|
|
408
|
+
__decorate([
|
|
409
|
+
(0, common_1.Get)('events/traces'),
|
|
410
|
+
__param(0, (0, common_1.Query)('type')),
|
|
411
|
+
__param(1, (0, common_1.Query)('principal')),
|
|
412
|
+
__param(2, (0, common_1.Query)('event')),
|
|
413
|
+
__param(3, (0, common_1.Query)('outcome')),
|
|
414
|
+
__param(4, (0, common_1.Query)('since')),
|
|
415
|
+
__param(5, (0, common_1.Query)('limit')),
|
|
416
|
+
__param(6, (0, common_1.Query)('offset')),
|
|
417
|
+
__metadata("design:type", Function),
|
|
418
|
+
__metadata("design:paramtypes", [String, String, String, Object, String, String, String]),
|
|
419
|
+
__metadata("design:returntype", void 0)
|
|
420
|
+
], CatalogController.prototype, "traceList", null);
|
|
421
|
+
__decorate([
|
|
422
|
+
(0, common_1.Get)('events/traces/:id'),
|
|
423
|
+
__param(0, (0, common_1.Param)('id')),
|
|
424
|
+
__metadata("design:type", Function),
|
|
425
|
+
__metadata("design:paramtypes", [String]),
|
|
426
|
+
__metadata("design:returntype", Promise)
|
|
427
|
+
], CatalogController.prototype, "trace", null);
|
|
428
|
+
__decorate([
|
|
429
|
+
(0, common_1.Get)('objects/:name/snapshots'),
|
|
430
|
+
__param(0, (0, common_1.Param)('name')),
|
|
431
|
+
__metadata("design:type", Function),
|
|
432
|
+
__metadata("design:paramtypes", [String]),
|
|
433
|
+
__metadata("design:returntype", void 0)
|
|
434
|
+
], CatalogController.prototype, "snapshots", null);
|
|
435
|
+
CatalogController = __decorate([
|
|
436
|
+
(0, common_1.Controller)(path),
|
|
437
|
+
__param(2, (0, common_1.Optional)()),
|
|
438
|
+
__param(2, (0, common_1.Inject)(catalog_workspace_1.CATALOG_TRACE_STORE)),
|
|
439
|
+
__metadata("design:paramtypes", [catalog_registry_base_1.CatalogRegistry,
|
|
440
|
+
catalog_service_1.CatalogService, Object])
|
|
441
|
+
], CatalogController);
|
|
442
|
+
if (guards.length > 0) {
|
|
443
|
+
(0, common_1.UseGuards)(...guards)(CatalogController);
|
|
444
|
+
}
|
|
445
|
+
for (const decorate of decorators) {
|
|
446
|
+
decorate(CatalogController);
|
|
447
|
+
}
|
|
448
|
+
return CatalogController;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* `?outcome=failed`, `?outcome=failed,incomplete`, `?outcome=a&outcome=b`.
|
|
452
|
+
*
|
|
453
|
+
* Two behaviours here are load-bearing and neither is obvious.
|
|
454
|
+
*
|
|
455
|
+
* An unrecognised outcome is dropped rather than passed down as a filter that
|
|
456
|
+
* matches nothing, and if nothing recognisable is left the filter goes away
|
|
457
|
+
* entirely. Returning an empty list for a typo would read as "no traces
|
|
458
|
+
* failed", which is the most expensive wrong answer this endpoint could give.
|
|
459
|
+
*
|
|
460
|
+
* A single outcome comes back as a bare string rather than a one-element array.
|
|
461
|
+
* `TraceQuery.outcome` accepts both, but a trace store written against the
|
|
462
|
+
* earlier contract expects only the string and would compare its column to an
|
|
463
|
+
* array — matching nothing, and reporting it as a clean empty page. So the
|
|
464
|
+
* array form is only ever produced when the caller genuinely asked for more
|
|
465
|
+
* than one, which is also the only case where an older store could not have
|
|
466
|
+
* answered anyway.
|
|
467
|
+
*/
|
|
468
|
+
function parseOutcomes(raw) {
|
|
469
|
+
if (raw === undefined)
|
|
470
|
+
return undefined;
|
|
471
|
+
const candidates = (Array.isArray(raw) ? raw : [raw]).flatMap((value) => String(value).split(','));
|
|
472
|
+
const outcomes = [];
|
|
473
|
+
for (const candidate of candidates) {
|
|
474
|
+
const trimmed = candidate.trim();
|
|
475
|
+
if ((0, catalog_workspace_1.isCatalogTraceOutcome)(trimmed) && !outcomes.includes(trimmed)) {
|
|
476
|
+
outcomes.push(trimmed);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (outcomes.length === 0)
|
|
480
|
+
return undefined;
|
|
481
|
+
return outcomes.length === 1 ? outcomes[0] : outcomes;
|
|
482
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
export declare const CATALOG_TYPE_META: unique symbol;
|
|
3
|
+
export declare const CATALOG_PROPERTY_META: unique symbol;
|
|
4
|
+
export interface CatalogTypeOptions {
|
|
5
|
+
displayName?: string;
|
|
6
|
+
pluralDisplayName?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
icon?: string;
|
|
9
|
+
group?: string;
|
|
10
|
+
titleProperty?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CatalogPropertyOptions {
|
|
13
|
+
displayName?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
hidden?: boolean;
|
|
16
|
+
order?: number;
|
|
17
|
+
classification?: string;
|
|
18
|
+
unit?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Declares the semantics the database cannot know: what this entity is called
|
|
22
|
+
* in the business, which section it belongs to, and what to show when it
|
|
23
|
+
* appears as a link somewhere else.
|
|
24
|
+
*
|
|
25
|
+
* Structure is never declared here — it is read off the ORM. An entity with no
|
|
26
|
+
* `@CatalogType` still appears in the catalog, just with a name derived from
|
|
27
|
+
* its class and no group.
|
|
28
|
+
*/
|
|
29
|
+
export declare function CatalogType(options?: CatalogTypeOptions): ClassDecorator;
|
|
30
|
+
/**
|
|
31
|
+
* Enriches one property. Everything it sets is tier 0 — the overlay can
|
|
32
|
+
* override any of it at runtime without a migration, which is precisely why
|
|
33
|
+
* these live in metadata rather than in the column definition.
|
|
34
|
+
*/
|
|
35
|
+
export declare function CatalogProperty(options?: CatalogPropertyOptions): PropertyDecorator;
|
|
36
|
+
export declare function readTypeOptions(target: unknown): CatalogTypeOptions;
|
|
37
|
+
export declare function readPropertyOptions(target: unknown): Record<string, CatalogPropertyOptions>;
|