@voxgig/build 4.4.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 +1 -0
- package/tm/web/web/package.json.frag +1 -0
- package/tm/web/web/playwright.config.js.frag +2 -1
- package/tm/web/web/src/api.js.frag +92 -0
- package/tm/web/web/src/bus.js.frag +14 -8
- package/tm/web/web/src/cmp/admin.js.frag +293 -212
- 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
|
@@ -0,0 +1,397 @@
|
|
|
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.doc_gen = void 0;
|
|
8
|
+
// Docs: model-driven documentation generation - mermaid diagrams and
|
|
9
|
+
// per-service READMEs derived from the compiled model. Everything here is
|
|
10
|
+
// a pure function of the model, so (like theme.css and views.js) the
|
|
11
|
+
// output is REGENERATED on every model-build, content-diffed to avoid
|
|
12
|
+
// needless writes, and marked AUTO-GENERATED (never hand-edit).
|
|
13
|
+
//
|
|
14
|
+
// Outputs (paths relative to the project root):
|
|
15
|
+
// docs/reference/entities.md entity tables + ER diagram (ref fields)
|
|
16
|
+
// docs/reference/messages.md per-service message tables + flow diagram
|
|
17
|
+
// docs/reference/system-map.md system architecture + dependency map
|
|
18
|
+
// backend/src/srv/<srv>/README.md one per implemented service
|
|
19
|
+
const fs_1 = __importDefault(require("fs"));
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const GENMARK = (what) => `<!-- AUTO-GENERATED from the model by @voxgig/build (${what}) - do not edit. -->`;
|
|
22
|
+
// Sanitize a canon or name into a mermaid-safe node id.
|
|
23
|
+
const mid = (s) => String(s).replace(/[^A-Za-z0-9_]/g, '_');
|
|
24
|
+
// ---- model walkers ----
|
|
25
|
+
// Leaf message patterns under a msg subtree. A leaf is a node with no
|
|
26
|
+
// child objects other than the '$' metadata entry. Returns
|
|
27
|
+
// [{ path: ['save','item'], meta: {file?} }].
|
|
28
|
+
function msgLeaves(node, prefix = []) {
|
|
29
|
+
const out = [];
|
|
30
|
+
const keys = Object.keys(node || {}).filter((k) => '$' !== k);
|
|
31
|
+
if (0 === keys.length && 0 < prefix.length) {
|
|
32
|
+
return [{ path: prefix, meta: (node && node.$) || {} }];
|
|
33
|
+
}
|
|
34
|
+
for (const k of keys) {
|
|
35
|
+
if (node[k] && 'object' === typeof node[k]) {
|
|
36
|
+
out.push(...msgLeaves(node[k], [...prefix, k]));
|
|
37
|
+
}
|
|
38
|
+
else if (0 < prefix.length) {
|
|
39
|
+
// Scalar child: treat the current node as a leaf with options.
|
|
40
|
+
return [{ path: prefix, meta: (node && node.$) || {} }];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
// Pattern string for a leaf under aim:<srv>: 'aim:todo,save:item'.
|
|
46
|
+
const patstr = (aim, path) => {
|
|
47
|
+
let s = 'aim:' + aim;
|
|
48
|
+
for (let i = 0; i < path.length; i += 2) {
|
|
49
|
+
s += ',' + path[i] + ':' + (path[i + 1] || '');
|
|
50
|
+
}
|
|
51
|
+
return s;
|
|
52
|
+
};
|
|
53
|
+
// Action file for a message by the MakeSrv convention: last pattern pair
|
|
54
|
+
// joined with '_' ('save','item' -> save_item), unless $.file overrides.
|
|
55
|
+
const actfile = (path, meta) => {
|
|
56
|
+
if (meta && meta.file) {
|
|
57
|
+
return String(meta.file).replace(/^\.\//, '');
|
|
58
|
+
}
|
|
59
|
+
const last = path.slice(-2);
|
|
60
|
+
return last.join('_');
|
|
61
|
+
};
|
|
62
|
+
// The services and their messages, from main.srv[].in + main.msg.aim.
|
|
63
|
+
function services(model) {
|
|
64
|
+
const srvs = model.main.srv || {};
|
|
65
|
+
const aim = (model.main.msg && model.main.msg.aim) || {};
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const name of Object.keys(srvs)) {
|
|
68
|
+
const srv = srvs[name];
|
|
69
|
+
const inaim = (srv.in && srv.in.aim) || {};
|
|
70
|
+
const own = [];
|
|
71
|
+
const routes = [];
|
|
72
|
+
for (const k of Object.keys(inaim)) {
|
|
73
|
+
if ('req' === k) {
|
|
74
|
+
const on = (inaim.req && inaim.req.on) || {};
|
|
75
|
+
for (const rk of Object.keys(on)) {
|
|
76
|
+
for (const leaf of msgLeaves((aim.req && aim.req.on && aim.req.on[rk]) || {})) {
|
|
77
|
+
routes.push({
|
|
78
|
+
raw: leaf, on: rk,
|
|
79
|
+
file: actfile(leaf.path, leaf.meta),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
for (const leaf of msgLeaves(aim[k] || {})) {
|
|
86
|
+
own.push({
|
|
87
|
+
pattern: patstr(k, leaf.path),
|
|
88
|
+
file: actfile(leaf.path, leaf.meta),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
out.push({ name, srv, msgs: own, routes });
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
// Entities with their fields and ref relationships.
|
|
98
|
+
function entities(model) {
|
|
99
|
+
const zones = model.main.ent || {};
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const zone of Object.keys(zones)) {
|
|
102
|
+
for (const name of Object.keys(zones[zone])) {
|
|
103
|
+
const def = zones[zone][name] || {};
|
|
104
|
+
const fields = [];
|
|
105
|
+
const refs = [];
|
|
106
|
+
for (const fname of Object.keys(def.field || {})) {
|
|
107
|
+
const f = def.field[fname] || {};
|
|
108
|
+
fields.push({ name: fname, kind: f.kind || 'String', ref: f.ref });
|
|
109
|
+
if (f.ref) {
|
|
110
|
+
refs.push({ field: fname, target: f.ref });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
out.push({
|
|
114
|
+
zone, name, canon: zone + '/' + name, fields, refs,
|
|
115
|
+
custom: !!(def.ux && 'custom' === def.ux.view),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
// ---- generators ----
|
|
122
|
+
function entitiesMd(model) {
|
|
123
|
+
const ents = entities(model);
|
|
124
|
+
const lines = [];
|
|
125
|
+
lines.push('# Reference: entities (generated)');
|
|
126
|
+
lines.push('');
|
|
127
|
+
lines.push(GENMARK('doc_gen'));
|
|
128
|
+
lines.push('');
|
|
129
|
+
lines.push('*Diátaxis: reference — the entity graph, derived from the model.*');
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push('## Entity relationship diagram');
|
|
132
|
+
lines.push('');
|
|
133
|
+
lines.push('Relationships come from `ref` fields (the field stores the id of the');
|
|
134
|
+
lines.push('target entity).');
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push('```mermaid');
|
|
137
|
+
lines.push('erDiagram');
|
|
138
|
+
for (const e of ents) {
|
|
139
|
+
lines.push(' ' + mid(e.canon) + ' {');
|
|
140
|
+
for (const f of e.fields) {
|
|
141
|
+
lines.push(' ' + mid(f.kind) + ' ' + mid(f.name) + (f.ref ? ' FK' : ''));
|
|
142
|
+
}
|
|
143
|
+
lines.push(' }');
|
|
144
|
+
}
|
|
145
|
+
for (const e of ents) {
|
|
146
|
+
for (const r of e.refs) {
|
|
147
|
+
lines.push(' ' + mid(r.target) + ' ||--o{ ' + mid(e.canon) + ' : "' + r.field + '"');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
lines.push('```');
|
|
151
|
+
lines.push('');
|
|
152
|
+
lines.push('(Entity ids are canons with `/` shown as `_`.)');
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push('## Entities');
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('| Canon | Fields | Relationships | UI |');
|
|
157
|
+
lines.push('|---|---|---|---|');
|
|
158
|
+
for (const e of ents) {
|
|
159
|
+
const flds = e.fields.map((f) => f.name).join(', ');
|
|
160
|
+
const refs = e.refs.map((r) => `${r.field} → ${r.target}`).join('<br>') || '—';
|
|
161
|
+
lines.push(`| \`${e.canon}\` | ${flds} | ${refs} | ${e.custom ? 'custom view' : 'generic admin'} |`);
|
|
162
|
+
}
|
|
163
|
+
lines.push('');
|
|
164
|
+
return lines.join('\n');
|
|
165
|
+
}
|
|
166
|
+
function messagesMd(model) {
|
|
167
|
+
const srvs = services(model);
|
|
168
|
+
const lines = [];
|
|
169
|
+
lines.push('# Reference: messages (generated)');
|
|
170
|
+
lines.push('');
|
|
171
|
+
lines.push(GENMARK('doc_gen'));
|
|
172
|
+
lines.push('');
|
|
173
|
+
lines.push('*Diátaxis: reference — services and the messages they answer, derived');
|
|
174
|
+
lines.push('from the model. Action files follow the MakeSrv convention (last');
|
|
175
|
+
lines.push('pattern pair: `save:item` → `save_item`).*');
|
|
176
|
+
lines.push('');
|
|
177
|
+
lines.push('## Message flow');
|
|
178
|
+
lines.push('');
|
|
179
|
+
lines.push('```mermaid');
|
|
180
|
+
lines.push('flowchart LR');
|
|
181
|
+
lines.push(' client([Clients / SPA])');
|
|
182
|
+
lines.push(' gateway{{gateway}}');
|
|
183
|
+
lines.push(' client -->|aim:* messages| gateway');
|
|
184
|
+
for (const s of srvs) {
|
|
185
|
+
lines.push(' ' + mid(s.name) + '[srv ' + s.name + ']');
|
|
186
|
+
const aims = Object.keys((s.srv.in && s.srv.in.aim) || {}).filter((k) => 'req' !== k);
|
|
187
|
+
for (const a of aims) {
|
|
188
|
+
lines.push(' gateway -->|aim:' + a + '| ' + mid(s.name));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
lines.push('```');
|
|
192
|
+
lines.push('');
|
|
193
|
+
for (const s of srvs) {
|
|
194
|
+
lines.push('## Service: ' + s.name);
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push('| Message | Action file |');
|
|
197
|
+
lines.push('|---|---|');
|
|
198
|
+
for (const m of s.msgs) {
|
|
199
|
+
lines.push('| `' + m.pattern + '` | `src/srv/' + s.name + '/' + m.file + '.ts` |');
|
|
200
|
+
}
|
|
201
|
+
lines.push('');
|
|
202
|
+
if (s.routes.length) {
|
|
203
|
+
lines.push('Gateway (web) routes:');
|
|
204
|
+
lines.push('');
|
|
205
|
+
lines.push('| Route | Handler file |');
|
|
206
|
+
lines.push('|---|---|');
|
|
207
|
+
for (const r of s.routes) {
|
|
208
|
+
lines.push('| `aim:req,on:' + r.on + ',' + patstr('', r.raw.path).slice(5) +
|
|
209
|
+
'` | `src/srv/' + s.name + '/' + r.file + '.ts` |');
|
|
210
|
+
}
|
|
211
|
+
lines.push('');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return lines.join('\n');
|
|
215
|
+
}
|
|
216
|
+
function systemMapMd(model) {
|
|
217
|
+
const srvs = services(model);
|
|
218
|
+
const ents = entities(model);
|
|
219
|
+
const envs = model.main.env || {};
|
|
220
|
+
const active = Object.keys(envs).filter((k) => envs[k] && envs[k].active);
|
|
221
|
+
const webactive = !!(envs.web && envs.web.active);
|
|
222
|
+
const zones = {};
|
|
223
|
+
for (const e of ents) {
|
|
224
|
+
(zones[e.zone] = zones[e.zone] || []).push(e);
|
|
225
|
+
}
|
|
226
|
+
const lines = [];
|
|
227
|
+
lines.push('# Reference: system map (generated)');
|
|
228
|
+
lines.push('');
|
|
229
|
+
lines.push(GENMARK('doc_gen'));
|
|
230
|
+
lines.push('');
|
|
231
|
+
lines.push('*Diátaxis: reference — the system structure and its dependencies,');
|
|
232
|
+
lines.push('derived from the model.*');
|
|
233
|
+
lines.push('');
|
|
234
|
+
lines.push('## Architecture');
|
|
235
|
+
lines.push('');
|
|
236
|
+
lines.push('```mermaid');
|
|
237
|
+
lines.push('flowchart TB');
|
|
238
|
+
if (webactive) {
|
|
239
|
+
lines.push(' subgraph spa[Web SPA]');
|
|
240
|
+
lines.push(' shell[cmp/shell]');
|
|
241
|
+
lines.push(' admin[cmp/admin]');
|
|
242
|
+
lines.push(' publiccmp[cmp/public + cmp/auth]');
|
|
243
|
+
lines.push(' settings[cmp/settings]');
|
|
244
|
+
for (const e of ents.filter((x) => x.custom)) {
|
|
245
|
+
lines.push(' view_' + mid(e.canon) + '[cmp/view/' + e.zone + '_' + e.name + ']');
|
|
246
|
+
}
|
|
247
|
+
lines.push(' bus[(Seneca bus)]');
|
|
248
|
+
lines.push(' shell --> bus');
|
|
249
|
+
lines.push(' admin --> bus');
|
|
250
|
+
lines.push(' publiccmp --> bus');
|
|
251
|
+
lines.push(' settings --> bus');
|
|
252
|
+
for (const e of ents.filter((x) => x.custom)) {
|
|
253
|
+
lines.push(' view_' + mid(e.canon) + ' --> bus');
|
|
254
|
+
}
|
|
255
|
+
lines.push(' end');
|
|
256
|
+
lines.push(' bus -->|aim:* over browser transport| gateway{{gateway}}');
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
lines.push(' client([Clients]) -->|aim:*| gateway{{gateway}}');
|
|
260
|
+
}
|
|
261
|
+
lines.push(' subgraph services[Services]');
|
|
262
|
+
for (const s of srvs) {
|
|
263
|
+
lines.push(' srv_' + mid(s.name) + '[' + s.name + ']');
|
|
264
|
+
}
|
|
265
|
+
lines.push(' end');
|
|
266
|
+
for (const s of srvs) {
|
|
267
|
+
lines.push(' gateway --> srv_' + mid(s.name));
|
|
268
|
+
}
|
|
269
|
+
// Service deps from the model (srv.deps), if declared.
|
|
270
|
+
for (const s of srvs) {
|
|
271
|
+
for (const d of Object.keys(s.srv.deps || {})) {
|
|
272
|
+
lines.push(' srv_' + mid(s.name) + ' -.depends.-> srv_' + mid(d));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
lines.push(' subgraph data[Entities]');
|
|
276
|
+
for (const zone of Object.keys(zones).sort()) {
|
|
277
|
+
lines.push(' subgraph zone_' + mid(zone) + '[zone ' + zone + ']');
|
|
278
|
+
for (const e of zones[zone]) {
|
|
279
|
+
lines.push(' ' + mid(e.canon) + '[' + e.name + ']');
|
|
280
|
+
}
|
|
281
|
+
lines.push(' end');
|
|
282
|
+
}
|
|
283
|
+
lines.push(' end');
|
|
284
|
+
for (const s of srvs) {
|
|
285
|
+
lines.push(' srv_' + mid(s.name) + ' --> data');
|
|
286
|
+
}
|
|
287
|
+
lines.push('```');
|
|
288
|
+
lines.push('');
|
|
289
|
+
lines.push('## Target environments');
|
|
290
|
+
lines.push('');
|
|
291
|
+
lines.push('```mermaid');
|
|
292
|
+
lines.push('flowchart LR');
|
|
293
|
+
lines.push(' model[(model.json)]');
|
|
294
|
+
for (const name of active) {
|
|
295
|
+
lines.push(' model --> env_' + mid(name) + '[env ' + name + ']');
|
|
296
|
+
}
|
|
297
|
+
lines.push('```');
|
|
298
|
+
lines.push('');
|
|
299
|
+
lines.push('Active environments: ' + (active.map((a) => '`' + a + '`').join(', ') || 'none') + '.');
|
|
300
|
+
lines.push('');
|
|
301
|
+
lines.push('See also: [entities](entities.md) · [messages](messages.md).');
|
|
302
|
+
lines.push('');
|
|
303
|
+
return lines.join('\n');
|
|
304
|
+
}
|
|
305
|
+
function srvReadmeMd(model, s) {
|
|
306
|
+
const lines = [];
|
|
307
|
+
lines.push('# Service: ' + s.name + ' (generated)');
|
|
308
|
+
lines.push('');
|
|
309
|
+
lines.push(GENMARK('doc_gen'));
|
|
310
|
+
lines.push('');
|
|
311
|
+
lines.push('Answers `aim:' +
|
|
312
|
+
Object.keys((s.srv.in && s.srv.in.aim) || {}).filter((k) => 'req' !== k).join('`, `aim:') +
|
|
313
|
+
'` messages, loaded by convention (`@voxgig/system` MakeSrv): each');
|
|
314
|
+
lines.push('message maps to the action file named after its last pattern pair.');
|
|
315
|
+
lines.push('');
|
|
316
|
+
if (s.srv.user && s.srv.user.required) {
|
|
317
|
+
lines.push('Requires a signed-in user (`user.required: true`).');
|
|
318
|
+
lines.push('');
|
|
319
|
+
}
|
|
320
|
+
lines.push('## Messages');
|
|
321
|
+
lines.push('');
|
|
322
|
+
lines.push('| Message | Action file |');
|
|
323
|
+
lines.push('|---|---|');
|
|
324
|
+
for (const m of s.msgs) {
|
|
325
|
+
lines.push('| `' + m.pattern + '` | `' + m.file + '.ts` |');
|
|
326
|
+
}
|
|
327
|
+
for (const r of s.routes) {
|
|
328
|
+
lines.push('| `aim:req,on:' + r.on + ',' + patstr('', r.raw.path).slice(5) +
|
|
329
|
+
'` (gateway) | `' + r.file + '.ts` |');
|
|
330
|
+
}
|
|
331
|
+
lines.push('');
|
|
332
|
+
lines.push('## Flow');
|
|
333
|
+
lines.push('');
|
|
334
|
+
lines.push('```mermaid');
|
|
335
|
+
lines.push('flowchart LR');
|
|
336
|
+
lines.push(' gateway{{gateway}} -->|validated msg| srv[srv ' + s.name + ']');
|
|
337
|
+
const seen = new Set();
|
|
338
|
+
for (const m of s.msgs) {
|
|
339
|
+
if (!seen.has(m.file)) {
|
|
340
|
+
seen.add(m.file);
|
|
341
|
+
lines.push(' srv --> ' + mid(m.file) + '["' + m.pattern + '<br>' + m.file + '.ts"]');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const r of s.routes) {
|
|
345
|
+
const rid = mid(r.file);
|
|
346
|
+
if (!seen.has(r.file)) {
|
|
347
|
+
seen.add(r.file);
|
|
348
|
+
lines.push(' gateway -.route.-> ' + rid + '["' + r.file + '.ts"]');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
lines.push('```');
|
|
352
|
+
lines.push('');
|
|
353
|
+
lines.push('Message params are validated from the model (gubu); see');
|
|
354
|
+
lines.push('`../../../model/` and `docs/reference/messages.md` at the project root.');
|
|
355
|
+
lines.push('');
|
|
356
|
+
return lines.join('\n');
|
|
357
|
+
}
|
|
358
|
+
// Write content if changed; return true when written.
|
|
359
|
+
function writeIfChanged(dest, content) {
|
|
360
|
+
const existing = fs_1.default.existsSync(dest) ? fs_1.default.readFileSync(dest, 'utf8') : null;
|
|
361
|
+
if (existing === content) {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
|
|
365
|
+
fs_1.default.writeFileSync(dest, content);
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
// doc_gen(model, spec): generate the model-driven docs. Returns
|
|
369
|
+
// { created } - the (project-relative) paths actually (re)written.
|
|
370
|
+
const doc_gen = async (model, spec) => {
|
|
371
|
+
const root = spec.root;
|
|
372
|
+
const srvfolder = spec.srvfolder || path_1.default.join(root, 'backend', 'src', 'srv');
|
|
373
|
+
const created = [];
|
|
374
|
+
const refdir = path_1.default.join(root, 'docs', 'reference');
|
|
375
|
+
if (writeIfChanged(path_1.default.join(refdir, 'entities.md'), entitiesMd(model))) {
|
|
376
|
+
created.push('docs/reference/entities.md');
|
|
377
|
+
}
|
|
378
|
+
if (writeIfChanged(path_1.default.join(refdir, 'messages.md'), messagesMd(model))) {
|
|
379
|
+
created.push('docs/reference/messages.md');
|
|
380
|
+
}
|
|
381
|
+
if (writeIfChanged(path_1.default.join(refdir, 'system-map.md'), systemMapMd(model))) {
|
|
382
|
+
created.push('docs/reference/system-map.md');
|
|
383
|
+
}
|
|
384
|
+
// Per-service READMEs - only for services whose folder exists (a
|
|
385
|
+
// declared-but-unimplemented service has nowhere sensible to document).
|
|
386
|
+
for (const s of services(model)) {
|
|
387
|
+
const dir = path_1.default.join(srvfolder, s.name);
|
|
388
|
+
if (fs_1.default.existsSync(dir)) {
|
|
389
|
+
if (writeIfChanged(path_1.default.join(dir, 'README.md'), srvReadmeMd(model, s))) {
|
|
390
|
+
created.push('backend/src/srv/' + s.name + '/README.md');
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return { created: created.sort() };
|
|
395
|
+
};
|
|
396
|
+
exports.doc_gen = doc_gen;
|
|
397
|
+
//# sourceMappingURL=doc_gen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc_gen.js","sourceRoot":"","sources":["../../doc/doc_gen.ts"],"names":[],"mappings":"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"}
|
package/dist/env/web/web_gen.js
CHANGED
|
@@ -26,20 +26,52 @@ const generate_1 = require("../lambda/generate");
|
|
|
26
26
|
// the project root, which holds backend/ and web/). $$slots$$ in the
|
|
27
27
|
// output path are rendered too.
|
|
28
28
|
const WEB_FILES = [
|
|
29
|
-
// Frontend SPA (web/)
|
|
29
|
+
// Frontend SPA (web/) — a model-driven enterprise app: public site + login,
|
|
30
|
+
// an app shell (nav / project selector / user menu / collapsible entity
|
|
31
|
+
// menu), and generic CRUD with relationship navigation. All runtime
|
|
32
|
+
// model-driven (model.js reads /model.json), so it fits any entity graph.
|
|
30
33
|
{ frag: 'web/package.json.frag', out: 'web/package.json' },
|
|
31
34
|
{ frag: 'web/index.html.frag', out: 'web/index.html' },
|
|
32
35
|
{ frag: 'web/playwright.config.js.frag', out: 'web/playwright.config.js' },
|
|
33
36
|
{ frag: 'web/src/main.js.frag', out: 'web/src/main.js' },
|
|
34
37
|
{ frag: 'web/src/bus.js.frag', out: 'web/src/bus.js' },
|
|
38
|
+
{ frag: 'web/src/model.js.frag', out: 'web/src/model.js' },
|
|
39
|
+
{ frag: 'web/src/api.js.frag', out: 'web/src/api.js' },
|
|
40
|
+
{ frag: 'web/src/hooks.js.frag', out: 'web/src/hooks.js' },
|
|
41
|
+
{ frag: 'web/src/theme.js.frag', out: 'web/src/theme.js' },
|
|
42
|
+
{ frag: 'web/src/customise.js.frag', out: 'web/src/customise.js' },
|
|
43
|
+
{ frag: 'web/src/custom.css.frag', out: 'web/src/custom.css' },
|
|
35
44
|
{ frag: 'web/src/style.css.frag', out: 'web/src/style.css' },
|
|
36
45
|
{ frag: 'web/src/cmp/app.js.frag', out: 'web/src/cmp/app.js' },
|
|
46
|
+
{ frag: 'web/src/cmp/public.js.frag', out: 'web/src/cmp/public.js' },
|
|
37
47
|
{ frag: 'web/src/cmp/auth.js.frag', out: 'web/src/cmp/auth.js' },
|
|
48
|
+
{ frag: 'web/src/cmp/shell.js.frag', out: 'web/src/cmp/shell.js' },
|
|
38
49
|
{ frag: 'web/src/cmp/admin.js.frag', out: 'web/src/cmp/admin.js' },
|
|
50
|
+
{ frag: 'web/src/cmp/settings.js.frag', out: 'web/src/cmp/settings.js' },
|
|
51
|
+
// Per-component doc sidecars (mermaid structure + message diagrams),
|
|
52
|
+
// create-once next to each component.
|
|
53
|
+
{ frag: 'web/src/cmp/app.md.frag', out: 'web/src/cmp/app.md' },
|
|
54
|
+
{ frag: 'web/src/cmp/public.md.frag', out: 'web/src/cmp/public.md' },
|
|
55
|
+
{ frag: 'web/src/cmp/auth.md.frag', out: 'web/src/cmp/auth.md' },
|
|
56
|
+
{ frag: 'web/src/cmp/shell.md.frag', out: 'web/src/cmp/shell.md' },
|
|
57
|
+
{ frag: 'web/src/cmp/admin.md.frag', out: 'web/src/cmp/admin.md' },
|
|
58
|
+
{ frag: 'web/src/cmp/settings.md.frag', out: 'web/src/cmp/settings.md' },
|
|
39
59
|
{ frag: 'web/e2e/smoke.spec.js.frag', out: 'web/e2e/smoke.spec.js' },
|
|
60
|
+
{ frag: 'web/AGENTS.md.frag', out: 'web/AGENTS.md' },
|
|
61
|
+
// Project docs (Diátaxis) for the web app, alongside the scaffold's
|
|
62
|
+
// backend docs (@voxgig/create-system DocsPart). Create-once like all
|
|
63
|
+
// WEB_FILES: the project owns its docs after generation.
|
|
64
|
+
{ frag: 'docs/how-to/customise-the-web-app.md.frag', out: 'docs/how-to/customise-the-web-app.md' },
|
|
65
|
+
{ frag: 'docs/how-to/use-the-api.md.frag', out: 'docs/how-to/use-the-api.md' },
|
|
66
|
+
{ frag: 'docs/how-to/change-the-theme.md.frag', out: 'docs/how-to/change-the-theme.md' },
|
|
67
|
+
{ frag: 'docs/how-to/add-a-custom-entity-view.md.frag', out: 'docs/how-to/add-a-custom-entity-view.md' },
|
|
68
|
+
{ frag: 'docs/reference/web-app.md.frag', out: 'docs/reference/web-app.md' },
|
|
69
|
+
{ frag: 'docs/explanation/web-architecture.md.frag', out: 'docs/explanation/web-architecture.md' },
|
|
40
70
|
// Backend web runner + auth service (backend/src/...)
|
|
41
71
|
{ frag: 'backend/env/web/web.ts.frag', out: 'backend/src/env/web/web.ts' },
|
|
72
|
+
{ frag: 'backend/env/shared/seed.ts.frag', out: 'backend/src/env/shared/seed.ts' },
|
|
42
73
|
{ frag: 'backend/srv/auth/auth-srv.ts.frag', out: 'backend/src/srv/auth/auth-srv.ts' },
|
|
74
|
+
{ frag: 'backend/srv/auth/user_util.ts.frag', out: 'backend/src/srv/auth/user_util.ts' },
|
|
43
75
|
{ frag: 'backend/srv/auth/get_info.ts.frag', out: 'backend/src/srv/auth/get_info.ts' },
|
|
44
76
|
{ frag: 'backend/srv/auth/signin_user.ts.frag', out: 'backend/src/srv/auth/signin_user.ts' },
|
|
45
77
|
{ frag: 'backend/srv/auth/signout_user.ts.frag', out: 'backend/src/srv/auth/signout_user.ts' },
|
|
@@ -47,6 +79,56 @@ const WEB_FILES = [
|
|
|
47
79
|
{ frag: 'backend/srv/auth/web_signin_user.ts.frag', out: 'backend/src/srv/auth/web_signin_user.ts' },
|
|
48
80
|
{ frag: 'backend/srv/auth/web_signout_user.ts.frag', out: 'backend/src/srv/auth/web_signout_user.ts' },
|
|
49
81
|
{ frag: 'backend/srv/auth/web_load_auth.ts.frag', out: 'backend/src/srv/auth/web_load_auth.ts' },
|
|
82
|
+
// Auth settings/security extensions.
|
|
83
|
+
{ frag: 'backend/srv/auth/change_pass.ts.frag', out: 'backend/src/srv/auth/change_pass.ts' },
|
|
84
|
+
{ frag: 'backend/srv/auth/web_change_pass.ts.frag', out: 'backend/src/srv/auth/web_change_pass.ts' },
|
|
85
|
+
{ frag: 'backend/srv/auth/update_user.ts.frag', out: 'backend/src/srv/auth/update_user.ts' },
|
|
86
|
+
{ frag: 'backend/srv/auth/web_update_user.ts.frag', out: 'backend/src/srv/auth/web_update_user.ts' },
|
|
87
|
+
{ frag: 'backend/srv/auth/remind_pass.ts.frag', out: 'backend/src/srv/auth/remind_pass.ts' },
|
|
88
|
+
{ frag: 'backend/srv/auth/web_remind_pass.ts.frag', out: 'backend/src/srv/auth/web_remind_pass.ts' },
|
|
89
|
+
// API access keys (Settings & security + REST API auth).
|
|
90
|
+
{ frag: 'backend/srv/auth/apikey_util.ts.frag', out: 'backend/src/srv/auth/apikey_util.ts' },
|
|
91
|
+
{ frag: 'backend/srv/auth/create_apikey.ts.frag', out: 'backend/src/srv/auth/create_apikey.ts' },
|
|
92
|
+
{ frag: 'backend/srv/auth/list_apikey.ts.frag', out: 'backend/src/srv/auth/list_apikey.ts' },
|
|
93
|
+
{ frag: 'backend/srv/auth/revoke_apikey.ts.frag', out: 'backend/src/srv/auth/revoke_apikey.ts' },
|
|
94
|
+
{ frag: 'backend/srv/auth/web_create_apikey.ts.frag', out: 'backend/src/srv/auth/web_create_apikey.ts' },
|
|
95
|
+
{ frag: 'backend/srv/auth/web_list_apikey.ts.frag', out: 'backend/src/srv/auth/web_list_apikey.ts' },
|
|
96
|
+
{ frag: 'backend/srv/auth/web_revoke_apikey.ts.frag', out: 'backend/src/srv/auth/web_revoke_apikey.ts' },
|
|
97
|
+
// The strict-JSON REST API (model main.api): api service + HTTP mapping.
|
|
98
|
+
// valid_gen.ts (request validation shapes) is REGENERATED by api_gen.
|
|
99
|
+
{ frag: 'backend/srv/api/api-srv.ts.frag', out: 'backend/src/srv/api/api-srv.ts' },
|
|
100
|
+
{ frag: 'backend/srv/api/get_info.ts.frag', out: 'backend/src/srv/api/get_info.ts' },
|
|
101
|
+
{ frag: 'backend/srv/api/on_ent.ts.frag', out: 'backend/src/srv/api/on_ent.ts' },
|
|
102
|
+
{ frag: 'backend/srv/api/expose.ts.frag', out: 'backend/src/srv/api/expose.ts' },
|
|
103
|
+
{ frag: 'backend/env/web/api.ts.frag', out: 'backend/src/env/web/api.ts' },
|
|
104
|
+
// Unit tests for what EnvWeb generates (node:test). They are
|
|
105
|
+
// MODEL-DRIVEN - the entity under test and its payloads come from the
|
|
106
|
+
// model - so they hold for any entity graph, and they stand in for
|
|
107
|
+
// other services with mock messages rather than booting them.
|
|
108
|
+
{ frag: 'backend/test/unit/srv/api/api.setup.ts.frag', out: 'backend/test/unit/srv/api/api.setup.ts' },
|
|
109
|
+
{ frag: 'backend/test/unit/srv/api/api.test.ts.frag', out: 'backend/test/unit/srv/api/api.test.ts' },
|
|
110
|
+
{ frag: 'backend/test/unit/srv/auth/auth.setup.ts.frag', out: 'backend/test/unit/srv/auth/auth.setup.ts' },
|
|
111
|
+
{ frag: 'backend/test/unit/srv/auth/apikey.test.ts.frag', out: 'backend/test/unit/srv/auth/apikey.test.ts' },
|
|
112
|
+
{ frag: 'backend/test/unit/srv/auth/session.test.ts.frag', out: 'backend/test/unit/srv/auth/session.test.ts' },
|
|
113
|
+
{ frag: 'backend/test/unit/env/web/api-router.test.ts.frag', out: 'backend/test/unit/env/web/api-router.test.ts' },
|
|
114
|
+
{ frag: 'backend/test/unit/env/web/surface.test.ts.frag', out: 'backend/test/unit/env/web/surface.test.ts' },
|
|
115
|
+
{ frag: 'backend/test/unit/srv/ent/ent.setup.ts.frag', out: 'backend/test/unit/srv/ent/ent.setup.ts' },
|
|
116
|
+
{ frag: 'backend/test/unit/srv/ent/proxy.test.ts.frag', out: 'backend/test/unit/srv/ent/proxy.test.ts' },
|
|
117
|
+
// Generic entity service (parameterised CRUD, membership-scoped).
|
|
118
|
+
{ frag: 'backend/srv/ent/ent-srv.ts.frag', out: 'backend/src/srv/ent/ent-srv.ts' },
|
|
119
|
+
{ frag: 'backend/srv/ent/access.ts.frag', out: 'backend/src/srv/ent/access.ts' },
|
|
120
|
+
{ frag: 'backend/srv/ent/get_info.ts.frag', out: 'backend/src/srv/ent/get_info.ts' },
|
|
121
|
+
{ frag: 'backend/srv/ent/cmd_list.ts.frag', out: 'backend/src/srv/ent/cmd_list.ts' },
|
|
122
|
+
{ frag: 'backend/srv/ent/cmd_load.ts.frag', out: 'backend/src/srv/ent/cmd_load.ts' },
|
|
123
|
+
{ frag: 'backend/srv/ent/cmd_save.ts.frag', out: 'backend/src/srv/ent/cmd_save.ts' },
|
|
124
|
+
{ frag: 'backend/srv/ent/cmd_remove.ts.frag', out: 'backend/src/srv/ent/cmd_remove.ts' },
|
|
125
|
+
// Browser proxies: aim:web,on:ent,cmd:* -> aim:ent,cmd:* (the gateway
|
|
126
|
+
// accepts only aim:web, so the entity service is never posted directly
|
|
127
|
+
// from a browser).
|
|
128
|
+
{ frag: 'backend/srv/ent/web_cmd_list.ts.frag', out: 'backend/src/srv/ent/web_cmd_list.ts' },
|
|
129
|
+
{ frag: 'backend/srv/ent/web_cmd_load.ts.frag', out: 'backend/src/srv/ent/web_cmd_load.ts' },
|
|
130
|
+
{ frag: 'backend/srv/ent/web_cmd_save.ts.frag', out: 'backend/src/srv/ent/web_cmd_save.ts' },
|
|
131
|
+
{ frag: 'backend/srv/ent/web_cmd_remove.ts.frag', out: 'backend/src/srv/ent/web_cmd_remove.ts' },
|
|
50
132
|
];
|
|
51
133
|
exports.WEB_FILES = WEB_FILES;
|
|
52
134
|
// Default seeded users (local testing only), rendered into the runner.
|
|
@@ -87,6 +169,82 @@ const web_gen = async (model, spec) => {
|
|
|
87
169
|
fs_1.default.writeFileSync(dest, content);
|
|
88
170
|
created.push(outrel);
|
|
89
171
|
}
|
|
172
|
+
// Custom entity views: for every entity declaring `ux:{ view: 'custom' }`,
|
|
173
|
+
// generate a hand-coded view component (CREATE-ONCE, so edits survive) plus
|
|
174
|
+
// an always-regenerated index (views.js) that imports them.
|
|
175
|
+
const entzones = model.main.ent || {};
|
|
176
|
+
const customs = [];
|
|
177
|
+
for (const zone of Object.keys(entzones)) {
|
|
178
|
+
for (const name of Object.keys(entzones[zone])) {
|
|
179
|
+
const def = entzones[zone][name];
|
|
180
|
+
if (def && def.ux && 'custom' === def.ux.view) {
|
|
181
|
+
customs.push({ zone, name, canon: zone + '/' + name });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
for (const c of customs) {
|
|
186
|
+
const vslots = Object.assign({}, slots, {
|
|
187
|
+
canon: c.canon,
|
|
188
|
+
zone: c.zone,
|
|
189
|
+
name: c.name,
|
|
190
|
+
tag: 'vg-view-' + c.zone + '-' + c.name,
|
|
191
|
+
className: 'VgView' + (0, util_1.camelify)(c.zone) + (0, util_1.camelify)(c.name),
|
|
192
|
+
Label: (0, util_1.camelify)(c.name),
|
|
193
|
+
});
|
|
194
|
+
// The component starter plus its doc sidecar, both create-once.
|
|
195
|
+
for (const ext of ['js', 'md']) {
|
|
196
|
+
const outrel = 'web/src/cmp/view/' + c.zone + '_' + c.name + '.' + ext;
|
|
197
|
+
const dest = path_1.default.join(spec.root, outrel);
|
|
198
|
+
if (fs_1.default.existsSync(dest) && !spec.force) {
|
|
199
|
+
skipped.push(outrel);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const content = (0, generate_1.renderFragment)((0, generate_1.loadFragment)('web/src/cmp/view/custom-view.' + ext + '.frag', spec, 'web'), vslots);
|
|
203
|
+
fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
|
|
204
|
+
fs_1.default.writeFileSync(dest, content);
|
|
205
|
+
created.push(outrel);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// views.js — the generated index of custom views. Regenerated whenever the
|
|
209
|
+
// set of custom views changes; a no-op run leaves it (and `created`) alone.
|
|
210
|
+
const viewsRel = 'web/src/views.js';
|
|
211
|
+
const viewsPath = path_1.default.join(spec.root, viewsRel);
|
|
212
|
+
const viewsBody = '// AUTO-GENERATED: imports every custom entity view (model entities declaring\n' +
|
|
213
|
+
'// ux:{view:\'custom\'}). Regenerated from the model — do not edit. The view\n' +
|
|
214
|
+
'// files it imports are hand-coded and create-once.\n\n' +
|
|
215
|
+
customs.map((c) => `import './cmp/view/${c.zone}_${c.name}.js'`).join('\n') + '\n';
|
|
216
|
+
const existingViews = fs_1.default.existsSync(viewsPath) ? fs_1.default.readFileSync(viewsPath, 'utf8') : null;
|
|
217
|
+
if (existingViews !== viewsBody) {
|
|
218
|
+
fs_1.default.mkdirSync(path_1.default.join(spec.root, 'web/src'), { recursive: true });
|
|
219
|
+
fs_1.default.writeFileSync(viewsPath, viewsBody);
|
|
220
|
+
created.push(viewsRel);
|
|
221
|
+
}
|
|
222
|
+
// theme.css — the design theme from the model (main.theme). Each mode's
|
|
223
|
+
// tokens become CSS variables under :root[data-theme-mode="<mode>"], with the
|
|
224
|
+
// default mode also on plain :root. Regenerated from the model; override
|
|
225
|
+
// tokens in custom.css and add modes via the theme:modes hook.
|
|
226
|
+
const theme = model.main && model.main.theme;
|
|
227
|
+
if (theme && theme.modes && Object.keys(theme.modes).length) {
|
|
228
|
+
const defMode = theme.mode || Object.keys(theme.modes)[0];
|
|
229
|
+
const tokensCss = (tokens) => Object.keys(tokens).sort()
|
|
230
|
+
.map((k) => ` --vg-${k}: ${tokens[k]};`).join('\n');
|
|
231
|
+
const blocks = [':root,\n:root[data-theme-mode="' + defMode + '"] {\n' + tokensCss(theme.modes[defMode]) + '\n}'];
|
|
232
|
+
for (const mode of Object.keys(theme.modes)) {
|
|
233
|
+
if (mode !== defMode) {
|
|
234
|
+
blocks.push(':root[data-theme-mode="' + mode + '"] {\n' + tokensCss(theme.modes[mode]) + '\n}');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const themeBody = '/* AUTO-GENERATED from the model theme (main.theme) — do not edit.\n' +
|
|
238
|
+
' Override tokens in custom.css; add modes via the theme:modes hook. */\n\n' +
|
|
239
|
+
blocks.join('\n\n') + '\n';
|
|
240
|
+
const themePath = path_1.default.join(spec.root, 'web/src/theme.css');
|
|
241
|
+
const existingTheme = fs_1.default.existsSync(themePath) ? fs_1.default.readFileSync(themePath, 'utf8') : null;
|
|
242
|
+
if (existingTheme !== themeBody) {
|
|
243
|
+
fs_1.default.mkdirSync(path_1.default.join(spec.root, 'web/src'), { recursive: true });
|
|
244
|
+
fs_1.default.writeFileSync(themePath, themeBody);
|
|
245
|
+
created.push('web/src/theme.css');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
90
248
|
return { created: created.sort(), skipped: skipped.sort() };
|
|
91
249
|
};
|
|
92
250
|
exports.web_gen = web_gen;
|