@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.
Files changed (100) hide show
  1. package/README.md +51 -2
  2. package/build.ts +20 -0
  3. package/dist/api/api_gen.d.ts +7 -0
  4. package/dist/api/api_gen.js +370 -0
  5. package/dist/api/api_gen.js.map +1 -0
  6. package/dist/build.d.ts +16 -1
  7. package/dist/build.js +17 -1
  8. package/dist/build.js.map +1 -1
  9. package/dist/doc/doc_gen.d.ts +8 -0
  10. package/dist/doc/doc_gen.js +397 -0
  11. package/dist/doc/doc_gen.js.map +1 -0
  12. package/dist/env/web/web_gen.js +159 -1
  13. package/dist/env/web/web_gen.js.map +1 -1
  14. package/dist/shape/ent.js +4 -2
  15. package/dist/shape/ent.js.map +1 -1
  16. package/env/web/web_gen.ts +174 -1
  17. package/package.json +6 -9
  18. package/tm/web/backend/env/shared/seed.ts.frag +23 -0
  19. package/tm/web/backend/env/web/api.ts.frag +157 -0
  20. package/tm/web/backend/env/web/web.ts.frag +37 -4
  21. package/tm/web/backend/srv/api/api-srv.ts.frag +4 -0
  22. package/tm/web/backend/srv/api/expose.ts.frag +33 -0
  23. package/tm/web/backend/srv/api/get_info.ts.frag +6 -0
  24. package/tm/web/backend/srv/api/on_ent.ts.frag +124 -0
  25. package/tm/web/backend/srv/auth/apikey_util.ts.frag +10 -0
  26. package/tm/web/backend/srv/auth/auth-srv.ts.frag +1 -0
  27. package/tm/web/backend/srv/auth/change_pass.ts.frag +12 -0
  28. package/tm/web/backend/srv/auth/create_apikey.ts.frag +33 -0
  29. package/tm/web/backend/srv/auth/get_info.ts.frag +1 -0
  30. package/tm/web/backend/srv/auth/list_apikey.ts.frag +15 -0
  31. package/tm/web/backend/srv/auth/load_auth.ts.frag +10 -1
  32. package/tm/web/backend/srv/auth/remind_pass.ts.frag +35 -0
  33. package/tm/web/backend/srv/auth/revoke_apikey.ts.frag +23 -0
  34. package/tm/web/backend/srv/auth/signin_user.ts.frag +12 -2
  35. package/tm/web/backend/srv/auth/signout_user.ts.frag +11 -1
  36. package/tm/web/backend/srv/auth/update_user.ts.frag +15 -0
  37. package/tm/web/backend/srv/auth/user_util.ts.frag +21 -0
  38. package/tm/web/backend/srv/auth/web_change_pass.ts.frag +16 -0
  39. package/tm/web/backend/srv/auth/web_create_apikey.ts.frag +15 -0
  40. package/tm/web/backend/srv/auth/web_list_apikey.ts.frag +11 -0
  41. package/tm/web/backend/srv/auth/web_load_auth.ts.frag +1 -0
  42. package/tm/web/backend/srv/auth/web_remind_pass.ts.frag +7 -0
  43. package/tm/web/backend/srv/auth/web_revoke_apikey.ts.frag +14 -0
  44. package/tm/web/backend/srv/auth/web_signin_user.ts.frag +1 -0
  45. package/tm/web/backend/srv/auth/web_signout_user.ts.frag +11 -3
  46. package/tm/web/backend/srv/auth/web_update_user.ts.frag +11 -0
  47. package/tm/web/backend/srv/ent/access.ts.frag +179 -0
  48. package/tm/web/backend/srv/ent/cmd_list.ts.frag +86 -0
  49. package/tm/web/backend/srv/ent/cmd_load.ts.frag +56 -0
  50. package/tm/web/backend/srv/ent/cmd_remove.ts.frag +60 -0
  51. package/tm/web/backend/srv/ent/cmd_save.ts.frag +141 -0
  52. package/tm/web/backend/srv/ent/ent-srv.ts.frag +4 -0
  53. package/tm/web/backend/srv/ent/get_info.ts.frag +6 -0
  54. package/tm/web/backend/srv/ent/web_cmd_list.ts.frag +17 -0
  55. package/tm/web/backend/srv/ent/web_cmd_load.ts.frag +17 -0
  56. package/tm/web/backend/srv/ent/web_cmd_remove.ts.frag +17 -0
  57. package/tm/web/backend/srv/ent/web_cmd_save.ts.frag +17 -0
  58. package/tm/web/backend/test/unit/env/web/api-router.test.ts.frag +315 -0
  59. package/tm/web/backend/test/unit/env/web/surface.test.ts.frag +109 -0
  60. package/tm/web/backend/test/unit/srv/api/api.setup.ts.frag +162 -0
  61. package/tm/web/backend/test/unit/srv/api/api.test.ts.frag +225 -0
  62. package/tm/web/backend/test/unit/srv/auth/apikey.test.ts.frag +152 -0
  63. package/tm/web/backend/test/unit/srv/auth/auth.setup.ts.frag +40 -0
  64. package/tm/web/backend/test/unit/srv/auth/session.test.ts.frag +189 -0
  65. package/tm/web/backend/test/unit/srv/ent/ent.setup.ts.frag +53 -0
  66. package/tm/web/backend/test/unit/srv/ent/proxy.test.ts.frag +109 -0
  67. package/tm/web/docs/explanation/web-architecture.md.frag +50 -0
  68. package/tm/web/docs/how-to/add-a-custom-entity-view.md.frag +53 -0
  69. package/tm/web/docs/how-to/change-the-theme.md.frag +63 -0
  70. package/tm/web/docs/how-to/customise-the-web-app.md.frag +68 -0
  71. package/tm/web/docs/how-to/use-the-api.md.frag +53 -0
  72. package/tm/web/docs/reference/web-app.md.frag +98 -0
  73. package/tm/web/web/AGENTS.md.frag +52 -0
  74. package/tm/web/web/e2e/smoke.spec.js.frag +13 -11
  75. package/tm/web/web/index.html.frag +3 -0
  76. package/tm/web/web/package.json.frag +4 -1
  77. package/tm/web/web/playwright.config.js.frag +6 -1
  78. package/tm/web/web/src/api.js.frag +92 -0
  79. package/tm/web/web/src/bus.js.frag +46 -23
  80. package/tm/web/web/src/cmp/admin.js.frag +292 -193
  81. package/tm/web/web/src/cmp/admin.md.frag +51 -0
  82. package/tm/web/web/src/cmp/app.js.frag +12 -15
  83. package/tm/web/web/src/cmp/app.md.frag +28 -0
  84. package/tm/web/web/src/cmp/auth.js.frag +69 -41
  85. package/tm/web/web/src/cmp/auth.md.frag +36 -0
  86. package/tm/web/web/src/cmp/public.js.frag +58 -0
  87. package/tm/web/web/src/cmp/public.md.frag +26 -0
  88. package/tm/web/web/src/cmp/settings.js.frag +178 -0
  89. package/tm/web/web/src/cmp/settings.md.frag +30 -0
  90. package/tm/web/web/src/cmp/shell.js.frag +231 -0
  91. package/tm/web/web/src/cmp/shell.md.frag +49 -0
  92. package/tm/web/web/src/cmp/view/custom-view.js.frag +47 -0
  93. package/tm/web/web/src/cmp/view/custom-view.md.frag +31 -0
  94. package/tm/web/web/src/custom.css.frag +8 -0
  95. package/tm/web/web/src/customise.js.frag +30 -0
  96. package/tm/web/web/src/hooks.js.frag +79 -0
  97. package/tm/web/web/src/main.js.frag +17 -4
  98. package/tm/web/web/src/model.js.frag +162 -0
  99. package/tm/web/web/src/style.css.frag +135 -21
  100. 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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"}
@@ -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;