jsgui3-server 0.0.151 → 0.0.155

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 (109) hide show
  1. package/README.md +21 -0
  2. package/admin-ui/v1/controls/admin_shell.js +33 -0
  3. package/admin-ui/v1/server.js +14 -1
  4. package/docs/agi/skills/README.md +23 -0
  5. package/docs/agi/skills/agent-output-control/SKILL.md +56 -0
  6. package/docs/agi/skills/ai-deep-research/SKILL.md +52 -0
  7. package/docs/agi/skills/autonomous-ui-inspection/SKILL.md +102 -0
  8. package/docs/agi/skills/deep-research/SKILL.md +156 -0
  9. package/docs/agi/skills/endurance/SKILL.md +53 -0
  10. package/docs/agi/skills/exploring-other-codebases/SKILL.md +56 -0
  11. package/docs/agi/skills/instruction-adherence/SKILL.md +73 -0
  12. package/docs/agi/skills/jsgui3-activation-debug/SKILL.md +94 -0
  13. package/docs/agi/skills/jsgui3-context-menu-patterns/SKILL.md +94 -0
  14. package/docs/agi/skills/puppeteer-efficient-ui-verification/SKILL.md +65 -0
  15. package/docs/agi/skills/runaway-process-guard/SKILL.md +49 -0
  16. package/docs/agi/skills/session-discipline/SKILL.md +40 -0
  17. package/docs/agi/skills/skill-writing/SKILL.md +211 -0
  18. package/docs/agi/skills/static-analysis/SKILL.md +58 -0
  19. package/docs/agi/skills/targeted-testing/SKILL.md +63 -0
  20. package/docs/agi/skills/understanding-jsgui3/SKILL.md +85 -0
  21. package/docs/api-reference.md +120 -2
  22. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +1 -0
  23. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +33 -0
  24. package/docs/books/website-design/01-introduction.md +73 -0
  25. package/docs/books/website-design/02-current-state.md +195 -0
  26. package/docs/books/website-design/03-base-class.md +181 -0
  27. package/docs/books/website-design/04-webpage.md +307 -0
  28. package/docs/books/website-design/05-website.md +456 -0
  29. package/docs/books/website-design/06-pages-storage.md +170 -0
  30. package/docs/books/website-design/07-api-layer.md +285 -0
  31. package/docs/books/website-design/08-server-integration.md +271 -0
  32. package/docs/books/website-design/09-cross-agent-review.md +190 -0
  33. package/docs/books/website-design/10-open-questions.md +196 -0
  34. package/docs/books/website-design/11-converged-recommendation.md +205 -0
  35. package/docs/books/website-design/12-content-model.md +395 -0
  36. package/docs/books/website-design/13-webpage-module-spec.md +404 -0
  37. package/docs/books/website-design/14-website-module-spec.md +541 -0
  38. package/docs/books/website-design/15-multi-repo-plan.md +275 -0
  39. package/docs/books/website-design/16-minimal-first.md +203 -0
  40. package/docs/books/website-design/17-implementation-report-codex.md +81 -0
  41. package/docs/books/website-design/README.md +43 -0
  42. package/docs/bundling-system-deep-dive.md +112 -3
  43. package/docs/configuration-reference.md +84 -0
  44. package/docs/proposals/jsgui3-website-and-webpage-design-jsgui3-server-support.md +257 -0
  45. package/docs/proposals/jsgui3-website-and-webpage-design-review.md +73 -0
  46. package/docs/proposals/jsgui3-website-and-webpage-design.md +732 -0
  47. package/docs/swagger.md +316 -0
  48. package/examples/controls/1) window/server.js +6 -1
  49. package/examples/controls/21) mvvm and declarative api/check.js +94 -0
  50. package/examples/controls/21) mvvm and declarative api/check_output.txt +25 -0
  51. package/examples/controls/21) mvvm and declarative api/check_output_2.txt +27 -0
  52. package/examples/controls/21) mvvm and declarative api/client.js +241 -0
  53. declarative api/e2e-screenshot-1-name-change.png +0 -0
  54. declarative api/e2e-screenshot-2-toggled.png +0 -0
  55. declarative api/e2e-screenshot-3-final.png +0 -0
  56. declarative api/e2e-screenshot-final.png +0 -0
  57. package/examples/controls/21) mvvm and declarative api/e2e-test.js +175 -0
  58. package/examples/controls/21) mvvm and declarative api/out.html +1 -0
  59. package/examples/controls/21) mvvm and declarative api/page_out.html +1 -0
  60. package/examples/controls/21) mvvm and declarative api/server.js +18 -0
  61. package/examples/data-views/01) query-endpoint/server.js +61 -0
  62. package/labs/website-design/001-base-class-overhead/check.js +162 -0
  63. package/labs/website-design/002-pages-storage/check.js +244 -0
  64. package/labs/website-design/002-pages-storage/results.txt +0 -0
  65. package/labs/website-design/003-type-detection/check.js +193 -0
  66. package/labs/website-design/003-type-detection/results.txt +0 -0
  67. package/labs/website-design/004-two-stage-validation/check.js +314 -0
  68. package/labs/website-design/004-two-stage-validation/results.txt +0 -0
  69. package/labs/website-design/005-normalize-input/check.js +303 -0
  70. package/labs/website-design/006-serve-website-spike/check.js +290 -0
  71. package/labs/website-design/README.md +34 -0
  72. package/labs/website-design/manifest.json +68 -0
  73. package/labs/website-design/run-all.js +60 -0
  74. package/middleware/json-body.js +126 -0
  75. package/openapi.js +474 -0
  76. package/package.json +13 -7
  77. package/publishers/Publishers.js +6 -5
  78. package/publishers/http-function-publisher.js +135 -126
  79. package/publishers/http-webpage-publisher.js +89 -11
  80. package/publishers/query-publisher.js +116 -0
  81. package/publishers/swagger-publisher.js +203 -0
  82. package/publishers/swagger-ui.js +578 -0
  83. package/resources/adapters/array-adapter.js +143 -0
  84. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +90 -22
  85. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +50 -14
  86. package/resources/processors/bundlers/js/esbuild/Core_JS_Single_File_Minifying_Bundler_Using_ESBuild.js +48 -14
  87. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +396 -44
  88. package/resources/query-resource.js +131 -0
  89. package/serve-factory.js +677 -18
  90. package/server.js +585 -167
  91. package/tests/README.md +86 -2
  92. package/tests/admin-ui-jsgui-controls.test.js +16 -1
  93. package/tests/bundling-default-control-elimination.puppeteer.test.js +32 -1
  94. package/tests/control-elimination-root-feature-pruning.test.js +440 -0
  95. package/tests/control-elimination-static-bracket-access.test.js +245 -0
  96. package/tests/control-scan-manifest-regression.test.js +2 -0
  97. package/tests/end-to-end.test.js +22 -21
  98. package/tests/fixtures/control_scan_manifest_expectations.json +4 -2
  99. package/tests/helpers/playwright-e2e-harness.js +326 -0
  100. package/tests/helpers/puppeteer-e2e-harness.js +62 -1
  101. package/tests/openapi.test.js +319 -0
  102. package/tests/playwright-smoke.test.js +134 -0
  103. package/tests/project-local-controls-bundling.puppeteer.test.js +462 -0
  104. package/tests/publish-enhancements.test.js +673 -0
  105. package/tests/query-publisher.test.js +430 -0
  106. package/tests/quick-json-body-test.js +169 -0
  107. package/tests/serve.test.js +425 -122
  108. package/tests/swagger-publisher.test.js +1076 -0
  109. package/tests/test-runner.js +4 -0
@@ -0,0 +1,395 @@
1
+ # Chapter 12: The Content Model
2
+
3
+ The previous chapters treated `content` as a reference to a Control constructor — a UI component that renders the page. This chapter expands the model: a **Webpage holds actual content**, not just a pointer to a renderer.
4
+
5
+ ---
6
+
7
+ ## 12.1 Why Content Belongs on the Page
8
+
9
+ A Webpage that only holds routing metadata (`path`, `title`) and a Control reference leaves a fundamental question unanswered: **where does the text live?**
10
+
11
+ In the current jsgui3 ecosystem, page text is typically hardcoded inside the Control's `compose()` method:
12
+
13
+ ```js
14
+ class AboutPage extends Control {
15
+ compose() {
16
+ const h1 = new Control({ context: this.context, tagName: 'h1' });
17
+ h1.text = 'About Our Company'; // ← text is embedded in the UI
18
+ this.add(h1);
19
+ }
20
+ }
21
+ ```
22
+
23
+ This works for single-language sites with stable copy, but it breaks down when:
24
+
25
+ - The same page needs to render in multiple languages
26
+ - Content is authored by non-developers (CMS, markdown files, API)
27
+ - The same Control layout needs different text for different sites
28
+ - A/B testing requires swapping copy without changing the component
29
+
30
+ **The fix**: the Webpage holds the content, and the Control receives it at render time.
31
+
32
+ ---
33
+
34
+ ## 12.2 Content as Named Strings
35
+
36
+ The simplest content model is a flat map of named strings:
37
+
38
+ ```js
39
+ const page = new Webpage({
40
+ path: '/about',
41
+ title: 'About Us',
42
+ ctrl: AboutCtrl,
43
+ strings: {
44
+ heading: 'About Our Company',
45
+ body: 'We build tools for developers...',
46
+ cta: 'Get Started'
47
+ }
48
+ });
49
+ ```
50
+
51
+ ### Why named strings?
52
+
53
+ | Property | Benefit |
54
+ |----------|---------|
55
+ | Explicit keys | Controls reference content by name, not position |
56
+ | Serializable | `JSON.stringify(page.strings)` just works |
57
+ | Inspectable | Admin UIs can list what content a page expects |
58
+ | Diffable | Content changes show up as key-level diffs |
59
+ | Translatable | Each key maps independently to translations |
60
+
61
+ ### What counts as a "string"?
62
+
63
+ Content strings may contain:
64
+
65
+ - **Plain text**: `'About Our Company'`
66
+ - **Markdown**: `'# Welcome\n\nWe build **tools** for developers.'`
67
+ - **HTML fragments**: `'<em>Welcome</em> to our site'`
68
+
69
+ The Webpage does not interpret the content — it stores it. The Control decides how to render it (text node, innerHTML, markdown parser). This separation means the content model is format-agnostic.
70
+
71
+ ---
72
+
73
+ ## 12.3 Structured Content
74
+
75
+ Named strings are flat. Some pages need structured content — a blog post has a title, author, date, body, and tags. A product page has a name, price, features list, and images.
76
+
77
+ Two approaches:
78
+
79
+ ### Approach A: Nested strings map
80
+
81
+ ```js
82
+ strings: {
83
+ 'hero.heading': 'Build Faster',
84
+ 'hero.subheading': 'Tools for modern developers',
85
+ 'features.0.title': 'Speed',
86
+ 'features.0.body': 'Compile in seconds',
87
+ 'features.1.title': 'Safety',
88
+ 'features.1.body': 'Type-checked by default'
89
+ }
90
+ ```
91
+
92
+ Flat but uses dot-notation keys. Easy to serialize and translate, but awkward to iterate over feature lists.
93
+
94
+ ### Approach B: Content object
95
+
96
+ ```js
97
+ content: {
98
+ hero: {
99
+ heading: 'Build Faster',
100
+ subheading: 'Tools for modern developers'
101
+ },
102
+ features: [
103
+ { title: 'Speed', body: 'Compile in seconds' },
104
+ { title: 'Safety', body: 'Type-checked by default' }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ Richer structure, maps naturally to component trees. But translation tools need to walk nested objects.
110
+
111
+ ### Recommendation
112
+
113
+ Support **both**. The Webpage stores a `content` object that can be arbitrarily nested. A convenience `get_string(dotted.path)` method provides flat access when needed:
114
+
115
+ ```js
116
+ page.content = { hero: { heading: 'Build Faster' } };
117
+ page.get_string('hero.heading'); // → 'Build Faster'
118
+ ```
119
+
120
+ This gives structured data for complex pages while keeping simple pages simple.
121
+
122
+ ---
123
+
124
+ ## 12.4 Internationalization (i18n)
125
+
126
+ ### The core model
127
+
128
+ Each content value can have translations keyed by locale:
129
+
130
+ ```js
131
+ const page = new Webpage({
132
+ path: '/about',
133
+ title: { en: 'About Us', fr: 'À propos', de: 'Über uns' },
134
+ ctrl: AboutCtrl,
135
+ content: {
136
+ heading: { en: 'About Our Company', fr: 'À propos de notre entreprise' },
137
+ body: { en: 'We build tools...', fr: 'Nous construisons des outils...' }
138
+ }
139
+ });
140
+ ```
141
+
142
+ ### Locale detection
143
+
144
+ How does the Webpage know which locale to use? It doesn't — that's a server/runtime concern. The Webpage stores all translations; the caller picks one:
145
+
146
+ ```js
147
+ // At render time, the server resolves the locale
148
+ const locale = req.acceptsLanguages(['en', 'fr']) || 'en';
149
+ const heading = page.get_string('heading', locale);
150
+ ```
151
+
152
+ ### Fallback chains
153
+
154
+ Locales fall back through a chain:
155
+
156
+ ```
157
+ 'en-GB' → 'en' → default (first available)
158
+ ```
159
+
160
+ The method `get_string(key, locale)` implements this:
161
+
162
+ ```js
163
+ get_string(key, locale) {
164
+ const value = this._resolve_dotted(this.content, key);
165
+ if (value == null) return undefined;
166
+
167
+ // Plain string (no translations)
168
+ if (typeof value === 'string') return value;
169
+
170
+ // Translated object
171
+ if (typeof value === 'object') {
172
+ // Try exact match
173
+ if (value[locale]) return value[locale];
174
+
175
+ // Try language-only (en-GB → en)
176
+ const lang = locale && locale.split('-')[0];
177
+ if (lang && value[lang]) return value[lang];
178
+
179
+ // Fallback to first available
180
+ const keys = Object.keys(value);
181
+ return keys.length > 0 ? value[keys[0]] : undefined;
182
+ }
183
+
184
+ return String(value);
185
+ }
186
+ ```
187
+
188
+ ### Mixed content (some translated, some not)
189
+
190
+ A page can mix translated and untranslated strings:
191
+
192
+ ```js
193
+ content: {
194
+ heading: { en: 'About', fr: 'À propos' }, // translated
195
+ copyright: '© 2026 JSGUI' // not translated
196
+ }
197
+ ```
198
+
199
+ `get_string('copyright', 'fr')` returns `'© 2026 JSGUI'` — if the value is a plain string, locale is ignored.
200
+
201
+ ### Available locales
202
+
203
+ ```js
204
+ get locales() {
205
+ // Walk content tree, collect all locale keys from translated values
206
+ }
207
+ ```
208
+
209
+ This lets admin UIs show which languages a page supports and identify missing translations.
210
+
211
+ ---
212
+
213
+ ## 12.5 Content vs. Ctrl — Separation of Concerns
214
+
215
+ The Webpage now has two distinct properties:
216
+
217
+ | Property | Type | Role |
218
+ |----------|------|------|
219
+ | `ctrl` | Function (constructor) | **How** the page looks (layout, styling, interaction) |
220
+ | `content` | Object | **What** the page says (text, data, translations) |
221
+
222
+ The Control receives content at render time:
223
+
224
+ ```js
225
+ // Server-side rendering
226
+ const instance = new page.ctrl({
227
+ context,
228
+ content: page.resolve_content(locale)
229
+ });
230
+ ```
231
+
232
+ The Control accesses strings through whatever mechanism it prefers:
233
+
234
+ ```js
235
+ class AboutCtrl extends Control {
236
+ compose() {
237
+ const spec = this.spec || {};
238
+ const content = spec.content || {};
239
+
240
+ const h1 = new Control({ context: this.context, tagName: 'h1' });
241
+ h1.text = content.heading || 'About';
242
+ this.add(h1);
243
+ }
244
+ }
245
+ ```
246
+
247
+ ### What `resolve_content(locale)` returns
248
+
249
+ A flat or nested object with all strings resolved to the requested locale:
250
+
251
+ ```js
252
+ page.resolve_content('fr');
253
+ // → { heading: 'À propos de notre entreprise', body: 'Nous construisons...' }
254
+ ```
255
+
256
+ This means the Control never sees locale keys — it gets a plain content object. The translation layer is transparent.
257
+
258
+ ---
259
+
260
+ ## 12.6 Title as Content
261
+
262
+ `title` is a special string — it appears in the `<title>` tag, in navigation, in admin UIs. Under the content model, it should support translation:
263
+
264
+ ```js
265
+ const page = new Webpage({
266
+ path: '/about',
267
+ title: { en: 'About Us', fr: 'À propos' }
268
+ });
269
+
270
+ // get_title(locale) resolves like any other string
271
+ page.get_title('en'); // → 'About Us'
272
+ page.get_title('fr'); // → 'À propos'
273
+ page.get_title(); // → first available
274
+ ```
275
+
276
+ For backward compatibility, `title` can also be a plain string:
277
+
278
+ ```js
279
+ const page = new Webpage({ path: '/', title: 'Home' });
280
+ page.get_title('fr'); // → 'Home' (no translation, returns as-is)
281
+ ```
282
+
283
+ ---
284
+
285
+ ## 12.7 Content Sources
286
+
287
+ Where does content come from? The Webpage doesn't care — it stores whatever it's given. But common patterns include:
288
+
289
+ ### Inline (hardcoded)
290
+
291
+ ```js
292
+ new Webpage({
293
+ path: '/',
294
+ content: { heading: 'Welcome' }
295
+ });
296
+ ```
297
+
298
+ ### From JSON files
299
+
300
+ ```js
301
+ const content = require('./content/about.json');
302
+ new Webpage({ path: '/about', content });
303
+ ```
304
+
305
+ ### From a CMS or API (at build time)
306
+
307
+ ```js
308
+ const content = await cms.getPage('about');
309
+ new Webpage({ path: '/about', content });
310
+ ```
311
+
312
+ ### From a CMS or API (at serve time)
313
+
314
+ The server can populate content lazily:
315
+
316
+ ```js
317
+ const page = new Webpage({ path: '/about', ctrl: AboutCtrl });
318
+ // Content loaded on demand per request
319
+ server.on('before-render', async (page, req) => {
320
+ page.content = await cms.getPage('about', req.locale);
321
+ });
322
+ ```
323
+
324
+ The Webpage supports all of these because it just holds data. The loading strategy is the application's concern.
325
+
326
+ ---
327
+
328
+ ## 12.8 Content and Validation
329
+
330
+ How does two-stage validation (Chapter 11) apply to content?
331
+
332
+ ### Construction time
333
+
334
+ - `content` can be `undefined` (set later)
335
+ - If present, must be an object (not a string, number, or array)
336
+ - No requirement for specific keys
337
+
338
+ ### Finalize time
339
+
340
+ - Content is **not required** — a page can be purely dynamic (Control renders everything itself)
341
+ - If content includes translation objects, warn about inconsistent locale coverage (some keys have `fr`, others don't)
342
+ - Title follows the same rules: can be string or translation object
343
+
344
+ This means content is always optional. Some pages are data-driven (need content), others are interactive (Control-only). Both are valid.
345
+
346
+ ---
347
+
348
+ ## 12.9 Serialization
349
+
350
+ `toJSON()` includes content for admin introspection:
351
+
352
+ ```js
353
+ page.toJSON();
354
+ // {
355
+ // path: '/about',
356
+ // title: { en: 'About Us', fr: 'À propos' },
357
+ // has_content: true,
358
+ // content_keys: ['heading', 'body', 'cta'],
359
+ // locales: ['en', 'fr'],
360
+ // is_dynamic: true,
361
+ // ...
362
+ // }
363
+ ```
364
+
365
+ Note: `toJSON()` includes `content_keys` (what strings exist) and `locales` (what languages), but **not the content values themselves**. Full content is available via `page.content`. This keeps admin summaries lightweight while preserving access to the full data when needed.
366
+
367
+ ---
368
+
369
+ ## 12.10 Design Decisions Summary
370
+
371
+ | Decision | Choice | Rationale |
372
+ |----------|--------|-----------|
373
+ | Content storage | Arbitrarily nested object | Supports both flat strings and structured data |
374
+ | Translation format | Values are either strings or `{locale: string}` objects | Minimal overhead, no wrapper classes |
375
+ | Locale resolution | Fallback chain with `get_string(key, locale)` | Predictable, simple to implement |
376
+ | Content required? | No — optional at both stages | Some pages are purely interactive |
377
+ | Ctrl receives content how? | Via `spec.content` in constructor | Standard jsgui3 pattern, no new mechanism |
378
+ | Title | Supports same translation format | Consistent with content model |
379
+ | Format interpretation | None — Webpage stores, Control interprets | Format-agnostic, no coupling to markdown/HTML |
380
+
381
+ ---
382
+
383
+ ## 12.11 Open Questions for Review
384
+
385
+ These are intentionally left open for other agents to comment on:
386
+
387
+ 1. **Should content support non-string values?** (numbers, booleans, dates) — or should everything be stringified?
388
+ 2. **Should there be a content schema?** — a way to declare what keys a page expects, so missing translations or typos can be caught
389
+ 3. **Should `get_string` throw or return undefined for missing keys?** — silent fallback vs. fail-fast
390
+ 4. **Should the Webpage support content inheritance?** — a base page that provides defaults, overridden by child pages
391
+ 5. **How should images and media be handled?** — as content values (URLs) or as a separate `assets` property?
392
+
393
+ ---
394
+
395
+ *Next: [Chapter 13](13-webpage-module-spec.md) defines the complete `jsgui3-webpage` module specification.*