@voxgig/build 4.3.0 → 4.11.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 +51 -2
- package/build.ts +20 -0
- package/dist/api/api_gen.d.ts +7 -0
- package/dist/api/api_gen.js +370 -0
- package/dist/api/api_gen.js.map +1 -0
- package/dist/build.d.ts +16 -1
- package/dist/build.js +17 -1
- package/dist/build.js.map +1 -1
- package/dist/doc/doc_gen.d.ts +8 -0
- package/dist/doc/doc_gen.js +397 -0
- package/dist/doc/doc_gen.js.map +1 -0
- package/dist/env/web/web_gen.js +159 -1
- package/dist/env/web/web_gen.js.map +1 -1
- package/dist/shape/ent.js +4 -2
- package/dist/shape/ent.js.map +1 -1
- package/env/web/web_gen.ts +174 -1
- package/package.json +6 -9
- package/tm/web/backend/env/shared/seed.ts.frag +23 -0
- package/tm/web/backend/env/web/api.ts.frag +157 -0
- package/tm/web/backend/env/web/web.ts.frag +37 -4
- package/tm/web/backend/srv/api/api-srv.ts.frag +4 -0
- package/tm/web/backend/srv/api/expose.ts.frag +33 -0
- package/tm/web/backend/srv/api/get_info.ts.frag +6 -0
- package/tm/web/backend/srv/api/on_ent.ts.frag +124 -0
- package/tm/web/backend/srv/auth/apikey_util.ts.frag +10 -0
- package/tm/web/backend/srv/auth/auth-srv.ts.frag +1 -0
- package/tm/web/backend/srv/auth/change_pass.ts.frag +12 -0
- package/tm/web/backend/srv/auth/create_apikey.ts.frag +33 -0
- package/tm/web/backend/srv/auth/get_info.ts.frag +1 -0
- package/tm/web/backend/srv/auth/list_apikey.ts.frag +15 -0
- package/tm/web/backend/srv/auth/load_auth.ts.frag +10 -1
- package/tm/web/backend/srv/auth/remind_pass.ts.frag +35 -0
- package/tm/web/backend/srv/auth/revoke_apikey.ts.frag +23 -0
- package/tm/web/backend/srv/auth/signin_user.ts.frag +12 -2
- package/tm/web/backend/srv/auth/signout_user.ts.frag +11 -1
- package/tm/web/backend/srv/auth/update_user.ts.frag +15 -0
- package/tm/web/backend/srv/auth/user_util.ts.frag +21 -0
- package/tm/web/backend/srv/auth/web_change_pass.ts.frag +16 -0
- package/tm/web/backend/srv/auth/web_create_apikey.ts.frag +15 -0
- package/tm/web/backend/srv/auth/web_list_apikey.ts.frag +11 -0
- package/tm/web/backend/srv/auth/web_load_auth.ts.frag +1 -0
- package/tm/web/backend/srv/auth/web_remind_pass.ts.frag +7 -0
- package/tm/web/backend/srv/auth/web_revoke_apikey.ts.frag +14 -0
- package/tm/web/backend/srv/auth/web_signin_user.ts.frag +1 -0
- package/tm/web/backend/srv/auth/web_signout_user.ts.frag +11 -3
- package/tm/web/backend/srv/auth/web_update_user.ts.frag +11 -0
- package/tm/web/backend/srv/ent/access.ts.frag +179 -0
- package/tm/web/backend/srv/ent/cmd_list.ts.frag +86 -0
- package/tm/web/backend/srv/ent/cmd_load.ts.frag +56 -0
- package/tm/web/backend/srv/ent/cmd_remove.ts.frag +60 -0
- package/tm/web/backend/srv/ent/cmd_save.ts.frag +141 -0
- package/tm/web/backend/srv/ent/ent-srv.ts.frag +4 -0
- package/tm/web/backend/srv/ent/get_info.ts.frag +6 -0
- package/tm/web/backend/srv/ent/web_cmd_list.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_load.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_remove.ts.frag +17 -0
- package/tm/web/backend/srv/ent/web_cmd_save.ts.frag +17 -0
- package/tm/web/backend/test/unit/env/web/api-router.test.ts.frag +315 -0
- package/tm/web/backend/test/unit/env/web/surface.test.ts.frag +109 -0
- package/tm/web/backend/test/unit/srv/api/api.setup.ts.frag +162 -0
- package/tm/web/backend/test/unit/srv/api/api.test.ts.frag +225 -0
- package/tm/web/backend/test/unit/srv/auth/apikey.test.ts.frag +152 -0
- package/tm/web/backend/test/unit/srv/auth/auth.setup.ts.frag +40 -0
- package/tm/web/backend/test/unit/srv/auth/session.test.ts.frag +189 -0
- package/tm/web/backend/test/unit/srv/ent/ent.setup.ts.frag +53 -0
- package/tm/web/backend/test/unit/srv/ent/proxy.test.ts.frag +109 -0
- package/tm/web/docs/explanation/web-architecture.md.frag +50 -0
- package/tm/web/docs/how-to/add-a-custom-entity-view.md.frag +53 -0
- package/tm/web/docs/how-to/change-the-theme.md.frag +63 -0
- package/tm/web/docs/how-to/customise-the-web-app.md.frag +68 -0
- package/tm/web/docs/how-to/use-the-api.md.frag +53 -0
- package/tm/web/docs/reference/web-app.md.frag +98 -0
- package/tm/web/web/AGENTS.md.frag +52 -0
- package/tm/web/web/e2e/smoke.spec.js.frag +13 -11
- package/tm/web/web/index.html.frag +3 -0
- package/tm/web/web/package.json.frag +4 -1
- package/tm/web/web/playwright.config.js.frag +6 -1
- package/tm/web/web/src/api.js.frag +92 -0
- package/tm/web/web/src/bus.js.frag +46 -23
- package/tm/web/web/src/cmp/admin.js.frag +292 -193
- package/tm/web/web/src/cmp/admin.md.frag +51 -0
- package/tm/web/web/src/cmp/app.js.frag +12 -15
- package/tm/web/web/src/cmp/app.md.frag +28 -0
- package/tm/web/web/src/cmp/auth.js.frag +69 -41
- package/tm/web/web/src/cmp/auth.md.frag +36 -0
- package/tm/web/web/src/cmp/public.js.frag +58 -0
- package/tm/web/web/src/cmp/public.md.frag +26 -0
- package/tm/web/web/src/cmp/settings.js.frag +178 -0
- package/tm/web/web/src/cmp/settings.md.frag +30 -0
- package/tm/web/web/src/cmp/shell.js.frag +231 -0
- package/tm/web/web/src/cmp/shell.md.frag +49 -0
- package/tm/web/web/src/cmp/view/custom-view.js.frag +47 -0
- package/tm/web/web/src/cmp/view/custom-view.md.frag +31 -0
- package/tm/web/web/src/custom.css.frag +8 -0
- package/tm/web/web/src/customise.js.frag +30 -0
- package/tm/web/web/src/hooks.js.frag +79 -0
- package/tm/web/web/src/main.js.frag +17 -4
- package/tm/web/web/src/model.js.frag +162 -0
- package/tm/web/web/src/style.css.frag +135 -21
- package/tm/web/web/src/theme.js.frag +82 -0
package/README.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @voxgig/build
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Code generation for Voxgig system projects. Reads the compiled
|
|
4
|
+
[voxgig-model](https://github.com/voxgig/model) (`model.json`) and generates
|
|
5
|
+
deployment artifacts and application code with the
|
|
6
|
+
[jostraca](https://github.com/jostraca/jostraca) templating library:
|
|
4
7
|
|
|
8
|
+
- **EnvLambda** — AWS Lambda deployment templates (Serverless function
|
|
9
|
+
defs, per-service handlers, resources).
|
|
10
|
+
- **EnvGen** — per-environment deployment artifacts (`local`, `basic`,
|
|
11
|
+
`docker`, `vm`, `aws`, `azure`, `cloudflare`, `web`) driven by the
|
|
12
|
+
model's `main: env:` declarations.
|
|
13
|
+
- **EnvWeb** — a full model-driven enterprise web app: SPA frontend
|
|
14
|
+
(public site, login, app shell, generic entity CRUD with relationship
|
|
15
|
+
navigation, settings), backend web runner, auth + generic entity
|
|
16
|
+
services, design themes, customisation hooks, and custom entity views.
|
|
17
|
+
- **Docs** — model-driven documentation for generated projects: mermaid
|
|
18
|
+
ER / message-flow / system-map diagrams and per-service READMEs,
|
|
19
|
+
regenerated on every model-build.
|
|
20
|
+
- **Api** — a strict-JSON REST API defined by the model (`main.api`):
|
|
21
|
+
uniform entity paths, API-key auth, an OpenAPI spec generated from the
|
|
22
|
+
entity field definitions, and generated request-validation shapes.
|
|
5
23
|
|
|
24
|
+
Generation is normally driven by `npm run model-build` inside a project
|
|
25
|
+
created with `npm create @voxgig/system` — you rarely call this package
|
|
26
|
+
directly.
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
Organised by the [Diátaxis](https://diataxis.fr) framework:
|
|
31
|
+
|
|
32
|
+
- **Tutorial**: [From model to generated app](docs/tutorial.md)
|
|
33
|
+
- **How-to guides**:
|
|
34
|
+
- [Customise generation templates](docs/how-to/customise-templates.md)
|
|
35
|
+
- [Generate the web app (EnvWeb)](docs/how-to/generate-the-web-app.md)
|
|
36
|
+
- [Add a target environment](docs/how-to/add-an-environment.md)
|
|
37
|
+
- **Reference**:
|
|
38
|
+
- [Public API](docs/reference/api.md)
|
|
39
|
+
- [EnvWeb: files, slots, model inputs](docs/reference/envweb.md)
|
|
40
|
+
- **Explanation**: [Design](docs/explanation/design.md)
|
|
41
|
+
|
|
42
|
+
Working on this repo with an AI agent? See [AGENTS.md](AGENTS.md).
|
|
43
|
+
|
|
44
|
+
## Develop
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install
|
|
48
|
+
npm run build # tsc -> dist/ (dist is committed; always rebuild before commit)
|
|
49
|
+
npm test # node:test + coverage thresholds
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT. Copyright (c) Voxgig Ltd.
|
package/build.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { resources_yml } from './env/lambda/res_yml'
|
|
|
19
19
|
|
|
20
20
|
import { env_gen, ENV_FILES, ENV_SRC, KINDS as ENV_KINDS } from './env/env_gen'
|
|
21
21
|
import { web_gen, WEB_FILES } from './env/web/web_gen'
|
|
22
|
+
import { doc_gen } from './doc/doc_gen'
|
|
23
|
+
import { api_gen } from './api/api_gen'
|
|
22
24
|
|
|
23
25
|
import {
|
|
24
26
|
generate, empty, TM,
|
|
@@ -55,6 +57,22 @@ const EnvWeb = {
|
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
|
|
60
|
+
// Docs: model-driven documentation (mermaid ER/message-flow/system-map
|
|
61
|
+
// diagrams + per-service READMEs), regenerated every model-build. See
|
|
62
|
+
// doc/doc_gen.ts.
|
|
63
|
+
const Docs = {
|
|
64
|
+
doc_gen,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
// Api: the strict-JSON REST API (model main.api) - OpenAPI spec from the
|
|
69
|
+
// entity field definitions + generated request-validation shapes. See
|
|
70
|
+
// api/api_gen.ts.
|
|
71
|
+
const Api = {
|
|
72
|
+
api_gen,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
58
76
|
// Fragment tooling (used by voxgig-system template list/eject/diff, and
|
|
59
77
|
// available to project code).
|
|
60
78
|
const Fragments = {
|
|
@@ -69,6 +87,8 @@ export {
|
|
|
69
87
|
EnvLambda,
|
|
70
88
|
EnvGen,
|
|
71
89
|
EnvWeb,
|
|
90
|
+
Docs,
|
|
91
|
+
Api,
|
|
72
92
|
Fragments,
|
|
73
93
|
|
|
74
94
|
// Building blocks for ejected project templates (src/gen/<name>.ts
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright © 2026 Voxgig Ltd, MIT License. */
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.api_gen = void 0;
|
|
8
|
+
// Api: generation for the strict-JSON REST API (model main.api).
|
|
9
|
+
// Regenerated on every model-build (pure functions of the model,
|
|
10
|
+
// content-diffed, AUTO-GENERATED):
|
|
11
|
+
//
|
|
12
|
+
// backend/gen/api/openapi.json OpenAPI 3.1 spec; schemas from the
|
|
13
|
+
// entity field definitions (an entity
|
|
14
|
+
// response schema plus <Name>Create /
|
|
15
|
+
// <Name>Update request schemas, which
|
|
16
|
+
// mirror the gubu shapes below)
|
|
17
|
+
// backend/gen/api/openapi.yaml the same spec in YAML - the format
|
|
18
|
+
// most SDK/codegen tools (incl.
|
|
19
|
+
// @voxgig/sdkgen) expect
|
|
20
|
+
// backend/src/srv/api/valid_gen.ts gubu shapes per exposed entity op,
|
|
21
|
+
// used by the api service to validate
|
|
22
|
+
// request bodies (strict: closed)
|
|
23
|
+
//
|
|
24
|
+
// The JSON and YAML are the SAME object (openapi(model)), just serialized
|
|
25
|
+
// twice - they never diverge.
|
|
26
|
+
//
|
|
27
|
+
// Exposure rules (same as the api service's expose.ts): application
|
|
28
|
+
// entities are exposed by default, the sys zone never is, per-entity
|
|
29
|
+
// overrides live under main.api.ent.
|
|
30
|
+
const fs_1 = __importDefault(require("fs"));
|
|
31
|
+
const path_1 = __importDefault(require("path"));
|
|
32
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
33
|
+
// Server-managed fields: never client-writable (and readOnly in OpenAPI).
|
|
34
|
+
const MANAGED = ['id', 'owner_id', 't_c', 't_m'];
|
|
35
|
+
function apiConf(model) {
|
|
36
|
+
const api = (model.main && model.main.api) || {};
|
|
37
|
+
return {
|
|
38
|
+
active: false !== api.active && null != model.main.api,
|
|
39
|
+
prefix: api.prefix || '/api',
|
|
40
|
+
version: api.version || 'v1',
|
|
41
|
+
ent: api.ent || {},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function exposed(model) {
|
|
45
|
+
const conf = apiConf(model);
|
|
46
|
+
const out = [];
|
|
47
|
+
const zones = (model.main && model.main.ent) || {};
|
|
48
|
+
for (const zone of Object.keys(zones)) {
|
|
49
|
+
if ('sys' === zone) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
for (const name of Object.keys(zones[zone])) {
|
|
53
|
+
const canon = zone + '/' + name;
|
|
54
|
+
const entconf = conf.ent[canon];
|
|
55
|
+
if (entconf && false === entconf.active) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
out.push({ canon, zone, name, def: zones[zone][name] || {} });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out.sort((a, b) => a.canon < b.canon ? -1 : 1);
|
|
62
|
+
}
|
|
63
|
+
function fieldsOf(def) {
|
|
64
|
+
const out = [];
|
|
65
|
+
const fields = def.field || {};
|
|
66
|
+
for (const fname of Object.keys(fields).sort()) {
|
|
67
|
+
const f = fields[fname] || {};
|
|
68
|
+
const valid = null == f.valid ? '' : String(f.valid);
|
|
69
|
+
out.push({
|
|
70
|
+
name: fname,
|
|
71
|
+
kind: ['String', 'Number', 'Boolean'].includes(f.kind) ? f.kind : 'String',
|
|
72
|
+
required: !MANAGED.includes(fname) && !/Skip/.test(valid),
|
|
73
|
+
managed: MANAGED.includes(fname),
|
|
74
|
+
label: f.label || fname,
|
|
75
|
+
ref: f.ref,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
// ---- valid_gen.ts ----
|
|
81
|
+
function validGenTs(model) {
|
|
82
|
+
const ents = exposed(model);
|
|
83
|
+
const lines = [];
|
|
84
|
+
lines.push('/* AUTO-GENERATED from the model by @voxgig/build (api_gen) - do not edit.');
|
|
85
|
+
lines.push(' Regenerated on every model-build. */');
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push('// Request-body validation shapes for the REST API, derived from the');
|
|
88
|
+
lines.push('// entity field definitions. Shapes are CLOSED (strict JSON: unknown');
|
|
89
|
+
lines.push('// fields are rejected); server-managed fields (' + MANAGED.join(', ') + ')');
|
|
90
|
+
lines.push('// are excluded entirely. create requires the required fields; update');
|
|
91
|
+
lines.push('// accepts any subset (partial).');
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push('module.exports = function makeShapes(Gubu: any) {');
|
|
94
|
+
lines.push(' const { Skip } = Gubu');
|
|
95
|
+
lines.push(' const shapes: any = {}');
|
|
96
|
+
lines.push('');
|
|
97
|
+
for (const e of ents) {
|
|
98
|
+
const flds = fieldsOf(e.def).filter((f) => !f.managed);
|
|
99
|
+
const create = flds.map((f) => ` ${f.name}: ${f.required ? f.kind : `Skip(${f.kind})`},`);
|
|
100
|
+
const update = flds.map((f) => ` ${f.name}: Skip(${f.kind}),`);
|
|
101
|
+
lines.push(` shapes['${e.canon}'] = {`);
|
|
102
|
+
lines.push(' create: Gubu({');
|
|
103
|
+
lines.push(...create);
|
|
104
|
+
lines.push(' }),');
|
|
105
|
+
lines.push(' update: Gubu({');
|
|
106
|
+
lines.push(...update);
|
|
107
|
+
lines.push(' }),');
|
|
108
|
+
lines.push(' }');
|
|
109
|
+
lines.push('');
|
|
110
|
+
}
|
|
111
|
+
lines.push(' return shapes');
|
|
112
|
+
lines.push('}');
|
|
113
|
+
lines.push('');
|
|
114
|
+
return lines.join('\n');
|
|
115
|
+
}
|
|
116
|
+
// ---- openapi.json ----
|
|
117
|
+
const KINDTYPE = {
|
|
118
|
+
String: 'string', Number: 'number', Boolean: 'boolean',
|
|
119
|
+
};
|
|
120
|
+
function openapi(model) {
|
|
121
|
+
const conf = apiConf(model);
|
|
122
|
+
const core = (model.main.conf && model.main.conf.core) || {};
|
|
123
|
+
const ents = exposed(model);
|
|
124
|
+
const schemas = {
|
|
125
|
+
Error: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
additionalProperties: false,
|
|
128
|
+
properties: {
|
|
129
|
+
error: {
|
|
130
|
+
type: 'object',
|
|
131
|
+
additionalProperties: true,
|
|
132
|
+
properties: {
|
|
133
|
+
code: { type: 'string' },
|
|
134
|
+
message: { type: 'string' },
|
|
135
|
+
details: { type: 'array', items: { type: 'object' } },
|
|
136
|
+
},
|
|
137
|
+
required: ['code'],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: ['error'],
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
const paths = {};
|
|
144
|
+
for (const e of ents) {
|
|
145
|
+
const sname = schemaName(e.canon);
|
|
146
|
+
const flds = fieldsOf(e.def);
|
|
147
|
+
const properties = {};
|
|
148
|
+
const required = [];
|
|
149
|
+
for (const f of flds) {
|
|
150
|
+
properties[f.name] = { type: KINDTYPE[f.kind], title: f.label };
|
|
151
|
+
if (f.managed) {
|
|
152
|
+
properties[f.name].readOnly = true;
|
|
153
|
+
}
|
|
154
|
+
if (f.ref) {
|
|
155
|
+
properties[f.name].description = 'Reference to ' + f.ref;
|
|
156
|
+
}
|
|
157
|
+
if (f.required) {
|
|
158
|
+
required.push(f.name);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
schemas[sname] = {
|
|
162
|
+
type: 'object',
|
|
163
|
+
additionalProperties: false,
|
|
164
|
+
properties,
|
|
165
|
+
...(required.length ? { required } : {}),
|
|
166
|
+
};
|
|
167
|
+
// Request bodies are NOT the entity schema. The entity schema describes
|
|
168
|
+
// what comes back - it carries the server-managed fields, and its
|
|
169
|
+
// `required` list is the create contract. What the server ACCEPTS is
|
|
170
|
+
// narrower, and differs per op (see validGenTs, which derives the
|
|
171
|
+
// enforcing gubu shapes from these same fields):
|
|
172
|
+
//
|
|
173
|
+
// create managed fields rejected; required fields required
|
|
174
|
+
// update managed fields rejected; everything optional (partial)
|
|
175
|
+
//
|
|
176
|
+
// Marking the managed fields `readOnly` on the entity schema is not
|
|
177
|
+
// enough on its own. It is the correct OpenAPI signal, but a closed
|
|
178
|
+
// schema (`additionalProperties: false`) plus `readOnly` is a
|
|
179
|
+
// combination most codegen tools ignore - they emit a request type with
|
|
180
|
+
// every property - so a generated client sends `id` and the closed gubu
|
|
181
|
+
// shape rejects the whole body with 400. Separate request schemas say
|
|
182
|
+
// the same thing in a form every tool understands.
|
|
183
|
+
const writable = flds.filter((f) => !f.managed);
|
|
184
|
+
const writableProps = {};
|
|
185
|
+
for (const f of writable) {
|
|
186
|
+
writableProps[f.name] = { type: KINDTYPE[f.kind], title: f.label };
|
|
187
|
+
if (f.ref) {
|
|
188
|
+
writableProps[f.name].description = 'Reference to ' + f.ref;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const writableRequired = writable.filter((f) => f.required).map((f) => f.name);
|
|
192
|
+
schemas[sname + 'Create'] = {
|
|
193
|
+
type: 'object',
|
|
194
|
+
additionalProperties: false,
|
|
195
|
+
properties: writableProps,
|
|
196
|
+
...(writableRequired.length ? { required: writableRequired } : {}),
|
|
197
|
+
};
|
|
198
|
+
schemas[sname + 'Update'] = {
|
|
199
|
+
type: 'object',
|
|
200
|
+
additionalProperties: false,
|
|
201
|
+
properties: writableProps,
|
|
202
|
+
};
|
|
203
|
+
const ref = { $ref: '#/components/schemas/' + sname };
|
|
204
|
+
const createRef = { $ref: '#/components/schemas/' + sname + 'Create' };
|
|
205
|
+
const updateRef = { $ref: '#/components/schemas/' + sname + 'Update' };
|
|
206
|
+
const errRef = { $ref: '#/components/schemas/Error' };
|
|
207
|
+
const errResponses = {
|
|
208
|
+
'400': { description: 'Invalid request', content: { 'application/json': { schema: errRef } } },
|
|
209
|
+
'401': { description: 'Missing or invalid API key', content: { 'application/json': { schema: errRef } } },
|
|
210
|
+
'403': { description: 'Forbidden', content: { 'application/json': { schema: errRef } } },
|
|
211
|
+
'404': { description: 'Not found', content: { 'application/json': { schema: errRef } } },
|
|
212
|
+
};
|
|
213
|
+
const itemResult = (desc) => ({
|
|
214
|
+
description: desc,
|
|
215
|
+
content: {
|
|
216
|
+
'application/json': {
|
|
217
|
+
schema: {
|
|
218
|
+
type: 'object', additionalProperties: false,
|
|
219
|
+
properties: { item: ref }, required: ['item'],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
const base = '/' + conf.version + '/' + e.canon;
|
|
225
|
+
paths[base] = {
|
|
226
|
+
get: {
|
|
227
|
+
operationId: opId('list', e), summary: 'List ' + e.name + ' entities',
|
|
228
|
+
tags: [e.canon],
|
|
229
|
+
parameters: flds.map((f) => ({
|
|
230
|
+
name: f.name, in: 'query', required: false,
|
|
231
|
+
schema: { type: KINDTYPE[f.kind] },
|
|
232
|
+
description: 'Filter by ' + f.name,
|
|
233
|
+
})),
|
|
234
|
+
responses: {
|
|
235
|
+
'200': {
|
|
236
|
+
description: 'Matching ' + e.name + ' entities',
|
|
237
|
+
content: {
|
|
238
|
+
'application/json': {
|
|
239
|
+
schema: {
|
|
240
|
+
type: 'object', additionalProperties: false,
|
|
241
|
+
properties: { items: { type: 'array', items: ref } },
|
|
242
|
+
required: ['items'],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
...errResponses,
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
post: {
|
|
251
|
+
operationId: opId('create', e), summary: 'Create a ' + e.name,
|
|
252
|
+
tags: [e.canon],
|
|
253
|
+
requestBody: {
|
|
254
|
+
required: true,
|
|
255
|
+
content: { 'application/json': { schema: createRef } },
|
|
256
|
+
},
|
|
257
|
+
responses: { '201': itemResult('The created ' + e.name), ...errResponses },
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
paths[base + '/{id}'] = {
|
|
261
|
+
parameters: [{
|
|
262
|
+
name: 'id', in: 'path', required: true, schema: { type: 'string' },
|
|
263
|
+
}],
|
|
264
|
+
get: {
|
|
265
|
+
operationId: opId('load', e), summary: 'Load one ' + e.name,
|
|
266
|
+
tags: [e.canon],
|
|
267
|
+
responses: { '200': itemResult('The ' + e.name), ...errResponses },
|
|
268
|
+
},
|
|
269
|
+
put: {
|
|
270
|
+
operationId: opId('update', e), summary: 'Update a ' + e.name + ' (partial)',
|
|
271
|
+
tags: [e.canon],
|
|
272
|
+
requestBody: {
|
|
273
|
+
required: true,
|
|
274
|
+
content: { 'application/json': { schema: updateRef } },
|
|
275
|
+
},
|
|
276
|
+
responses: { '200': itemResult('The updated ' + e.name), ...errResponses },
|
|
277
|
+
},
|
|
278
|
+
delete: {
|
|
279
|
+
operationId: opId('remove', e), summary: 'Remove a ' + e.name,
|
|
280
|
+
tags: [e.canon],
|
|
281
|
+
responses: {
|
|
282
|
+
'200': {
|
|
283
|
+
description: 'Removed',
|
|
284
|
+
content: {
|
|
285
|
+
'application/json': {
|
|
286
|
+
schema: {
|
|
287
|
+
type: 'object', additionalProperties: false,
|
|
288
|
+
properties: { ok: { type: 'boolean' }, id: { type: 'string' } },
|
|
289
|
+
required: ['ok', 'id'],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
...errResponses,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
openapi: '3.1.0',
|
|
301
|
+
info: {
|
|
302
|
+
title: (core.name || 'system') + ' API',
|
|
303
|
+
version: conf.version,
|
|
304
|
+
description: 'Strict-JSON REST API, generated from the model. ' +
|
|
305
|
+
'Uniform entity paths: <zone>/<name>[/<id>].',
|
|
306
|
+
},
|
|
307
|
+
servers: [{ url: conf.prefix }],
|
|
308
|
+
security: [{ bearerAuth: [] }],
|
|
309
|
+
components: {
|
|
310
|
+
securitySchemes: {
|
|
311
|
+
bearerAuth: {
|
|
312
|
+
type: 'http', scheme: 'bearer',
|
|
313
|
+
description: 'API access key (create one under Settings & security)',
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
schemas,
|
|
317
|
+
},
|
|
318
|
+
paths,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
// Uniform operation ids for SDK generation: list_proj_project, ...
|
|
322
|
+
function opId(op, e) {
|
|
323
|
+
return op + '_' + e.zone + '_' + e.name;
|
|
324
|
+
}
|
|
325
|
+
function schemaName(canon) {
|
|
326
|
+
return canon.split('/').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
|
|
327
|
+
}
|
|
328
|
+
function writeIfChanged(dest, content) {
|
|
329
|
+
const existing = fs_1.default.existsSync(dest) ? fs_1.default.readFileSync(dest, 'utf8') : null;
|
|
330
|
+
if (existing === content) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
|
|
334
|
+
fs_1.default.writeFileSync(dest, content);
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
// api_gen(model, spec): generate the REST API artifacts. No-op unless
|
|
338
|
+
// the model declares main.api. Returns { created }.
|
|
339
|
+
const api_gen = async (model, spec) => {
|
|
340
|
+
const created = [];
|
|
341
|
+
if (null == (model.main && model.main.api)) {
|
|
342
|
+
return { created };
|
|
343
|
+
}
|
|
344
|
+
const doc = openapi(model);
|
|
345
|
+
const spec_json = JSON.stringify(doc, null, 2) + '\n';
|
|
346
|
+
if (writeIfChanged(path_1.default.join(spec.root, 'backend', 'gen', 'api', 'openapi.json'), spec_json)) {
|
|
347
|
+
created.push('backend/gen/api/openapi.json');
|
|
348
|
+
}
|
|
349
|
+
// YAML of the SAME object. noRefs is REQUIRED: the spec reuses $ref
|
|
350
|
+
// objects (the Error schema ref appears in every error response, each
|
|
351
|
+
// entity ref in several operations), and without noRefs js-yaml emits
|
|
352
|
+
// anchors/aliases (&ref_0 / *ref_0) that most OpenAPI codegen tools -
|
|
353
|
+
// including @voxgig/sdkgen - do not resolve. lineWidth:-1 stops long
|
|
354
|
+
// strings (descriptions, the server url) being line-folded.
|
|
355
|
+
const spec_yaml = js_yaml_1.default.dump(doc, { noRefs: true, lineWidth: -1 });
|
|
356
|
+
if (writeIfChanged(path_1.default.join(spec.root, 'backend', 'gen', 'api', 'openapi.yaml'), spec_yaml)) {
|
|
357
|
+
created.push('backend/gen/api/openapi.yaml');
|
|
358
|
+
}
|
|
359
|
+
// The validation shapes land next to the api service - only when the
|
|
360
|
+
// service is implemented (its folder exists).
|
|
361
|
+
const srvapi = path_1.default.join(spec.root, 'backend', 'src', 'srv', 'api');
|
|
362
|
+
if (fs_1.default.existsSync(srvapi)) {
|
|
363
|
+
if (writeIfChanged(path_1.default.join(srvapi, 'valid_gen.ts'), validGenTs(model))) {
|
|
364
|
+
created.push('backend/src/srv/api/valid_gen.ts');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return { created: created.sort() };
|
|
368
|
+
};
|
|
369
|
+
exports.api_gen = api_gen;
|
|
370
|
+
//# sourceMappingURL=api_gen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api_gen.js","sourceRoot":"","sources":["../../api/api_gen.ts"],"names":[],"mappings":"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"}
|
package/dist/build.d.ts
CHANGED
|
@@ -56,10 +56,25 @@ declare const EnvWeb: {
|
|
|
56
56
|
out: string;
|
|
57
57
|
}[];
|
|
58
58
|
};
|
|
59
|
+
declare const Docs: {
|
|
60
|
+
doc_gen: (model: any, spec: {
|
|
61
|
+
root: string;
|
|
62
|
+
srvfolder?: string;
|
|
63
|
+
}) => Promise<{
|
|
64
|
+
created: string[];
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
declare const Api: {
|
|
68
|
+
api_gen: (model: any, spec: {
|
|
69
|
+
root: string;
|
|
70
|
+
}) => Promise<{
|
|
71
|
+
created: string[];
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
59
74
|
declare const Fragments: {
|
|
60
75
|
load: typeof loadFragment;
|
|
61
76
|
render: typeof renderFragment;
|
|
62
77
|
list: typeof listFragments;
|
|
63
78
|
folder: string;
|
|
64
79
|
};
|
|
65
|
-
export { EnvLambda, EnvGen, EnvWeb, Fragments, generate, empty, TM, loadFragment, renderFragment, MsgMetaShape, CoreConfShape, CloudConfShape, res_dynamo_yml, };
|
|
80
|
+
export { EnvLambda, EnvGen, EnvWeb, Docs, Api, Fragments, generate, empty, TM, loadFragment, renderFragment, MsgMetaShape, CoreConfShape, CloudConfShape, res_dynamo_yml, };
|
package/dist/build.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* Copyright © 2022-2026 Voxgig Ltd, MIT License. */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.res_dynamo_yml = exports.CloudConfShape = exports.CoreConfShape = exports.MsgMetaShape = exports.renderFragment = exports.loadFragment = exports.TM = exports.empty = exports.generate = exports.Fragments = exports.EnvWeb = exports.EnvGen = exports.EnvLambda = void 0;
|
|
4
|
+
exports.res_dynamo_yml = exports.CloudConfShape = exports.CoreConfShape = exports.MsgMetaShape = exports.renderFragment = exports.loadFragment = exports.TM = exports.empty = exports.generate = exports.Fragments = exports.Api = exports.Docs = exports.EnvWeb = exports.EnvGen = exports.EnvLambda = void 0;
|
|
5
5
|
// EnvLambda: generation of the Lambda deployment templates, built on the
|
|
6
6
|
// jostraca templating library. The templates live in env/lambda/, one file
|
|
7
7
|
// per output for convenience:
|
|
@@ -19,6 +19,8 @@ const srv_handler_1 = require("./env/lambda/srv_handler");
|
|
|
19
19
|
const res_yml_1 = require("./env/lambda/res_yml");
|
|
20
20
|
const env_gen_1 = require("./env/env_gen");
|
|
21
21
|
const web_gen_1 = require("./env/web/web_gen");
|
|
22
|
+
const doc_gen_1 = require("./doc/doc_gen");
|
|
23
|
+
const api_gen_1 = require("./api/api_gen");
|
|
22
24
|
const generate_1 = require("./env/lambda/generate");
|
|
23
25
|
Object.defineProperty(exports, "generate", { enumerable: true, get: function () { return generate_1.generate; } });
|
|
24
26
|
Object.defineProperty(exports, "empty", { enumerable: true, get: function () { return generate_1.empty; } });
|
|
@@ -54,6 +56,20 @@ const EnvWeb = {
|
|
|
54
56
|
files: web_gen_1.WEB_FILES,
|
|
55
57
|
};
|
|
56
58
|
exports.EnvWeb = EnvWeb;
|
|
59
|
+
// Docs: model-driven documentation (mermaid ER/message-flow/system-map
|
|
60
|
+
// diagrams + per-service READMEs), regenerated every model-build. See
|
|
61
|
+
// doc/doc_gen.ts.
|
|
62
|
+
const Docs = {
|
|
63
|
+
doc_gen: doc_gen_1.doc_gen,
|
|
64
|
+
};
|
|
65
|
+
exports.Docs = Docs;
|
|
66
|
+
// Api: the strict-JSON REST API (model main.api) - OpenAPI spec from the
|
|
67
|
+
// entity field definitions + generated request-validation shapes. See
|
|
68
|
+
// api/api_gen.ts.
|
|
69
|
+
const Api = {
|
|
70
|
+
api_gen: api_gen_1.api_gen,
|
|
71
|
+
};
|
|
72
|
+
exports.Api = Api;
|
|
57
73
|
// Fragment tooling (used by voxgig-system template list/eject/diff, and
|
|
58
74
|
// available to project code).
|
|
59
75
|
const Fragments = {
|
package/dist/build.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../build.ts"],"names":[],"mappings":";AAAA,oDAAoD;;;AAEpD,yEAAyE;AACzE,2EAA2E;AAC3E,8BAA8B;AAC9B,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,yEAAyE;AACzE,EAAE;AACF,yEAAyE;AACzE,2EAA2E;AAC3E,wEAAwE;AACxE,qCAAqC;AAErC,kDAA8C;AAC9C,0DAAsD;AACtD,kDAAoD;AAEpD,2CAA+E;AAC/E,+CAAsD;
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../build.ts"],"names":[],"mappings":";AAAA,oDAAoD;;;AAEpD,yEAAyE;AACzE,2EAA2E;AAC3E,8BAA8B;AAC9B,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,yEAAyE;AACzE,EAAE;AACF,yEAAyE;AACzE,2EAA2E;AAC3E,wEAAwE;AACxE,qCAAqC;AAErC,kDAA8C;AAC9C,0DAAsD;AACtD,kDAAoD;AAEpD,2CAA+E;AAC/E,+CAAsD;AACtD,2CAAuC;AACvC,2CAAuC;AAEvC,oDAG8B;AAqE5B,yFAvEA,mBAAQ,OAuEA;AACR,sFAxEU,gBAAK,OAwEV;AACL,mFAzEiB,aAAE,OAyEjB;AACF,6FAzEA,uBAAY,OAyEA;AACZ,+FA1Ec,yBAAc,OA0Ed;AAvEhB,qCAA0C;AAwExC,6FAxEO,kBAAY,OAwEP;AAvEd,uCAA4D;AAwE1D,8FAxEO,oBAAa,OAwEP;AACb,+FAzEsB,qBAAc,OAyEtB;AAxEhB,yDAAqD;AAyEnD,+FAzEO,+BAAc,OAyEP;AAtEhB,MAAM,SAAS,GAAG;IAChB,OAAO,EAAP,iBAAO;IACP,WAAW,EAAX,yBAAW;IACX,aAAa,EAAb,uBAAa;CACd,CAAA;AAgDC,8BAAS;AA7CX,wEAAwE;AACxE,yDAAyD;AACzD,MAAM,MAAM,GAAG;IACb,OAAO,EAAP,iBAAO;IACP,KAAK,EAAE,mBAAS;IAChB,QAAQ,EAAE,iBAAO;IACjB,KAAK,EAAE,CAAC,GAAG,eAAS,EAAE,KAAK,CAAC;CAC7B,CAAA;AAuCC,wBAAM;AApCR,oEAAoE;AACpE,wDAAwD;AACxD,MAAM,MAAM,GAAG;IACb,OAAO,EAAP,iBAAO;IACP,KAAK,EAAE,mBAAS;CACjB,CAAA;AAgCC,wBAAM;AA7BR,uEAAuE;AACvE,sEAAsE;AACtE,kBAAkB;AAClB,MAAM,IAAI,GAAG;IACX,OAAO,EAAP,iBAAO;CACR,CAAA;AAyBC,oBAAI;AAtBN,yEAAyE;AACzE,sEAAsE;AACtE,kBAAkB;AAClB,MAAM,GAAG,GAAG;IACV,OAAO,EAAP,iBAAO;CACR,CAAA;AAkBC,kBAAG;AAfL,wEAAwE;AACxE,8BAA8B;AAC9B,MAAM,SAAS,GAAG;IAChB,IAAI,EAAE,uBAAY;IAClB,MAAM,EAAE,yBAAc;IACtB,IAAI,EAAE,wBAAa;IACnB,MAAM,EAAE,iBAAM;CACf,CAAA;AASC,8BAAS"}
|