jsgui3-server 0.0.148 → 0.0.149

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 (134) hide show
  1. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  2. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
  3. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
  4. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
  5. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
  6. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
  7. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
  8. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
  9. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
  10. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
  11. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
  12. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
  13. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
  14. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
  15. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
  16. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
  17. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
  18. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
  19. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
  20. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
  21. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
  22. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
  23. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
  24. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
  25. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
  26. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
  27. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
  28. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
  29. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
  30. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
  31. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
  32. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
  33. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
  34. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
  35. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
  36. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
  37. package/README.md +85 -3
  38. package/admin-ui/client.js +8 -8
  39. package/dev-status.svg +139 -0
  40. package/docs/api-reference.md +301 -43
  41. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  42. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  43. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  44. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  45. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  46. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  47. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  48. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  49. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  50. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  51. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  52. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  53. package/docs/bundling-system-deep-dive.md +9 -4
  54. package/docs/comprehensive-documentation.md +49 -18
  55. package/docs/configuration-reference.md +152 -27
  56. package/docs/core/README.md +19 -0
  57. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  58. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  59. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  60. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  61. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  62. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  63. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  64. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  65. package/docs/publishers-guide.md +59 -4
  66. package/docs/resources-guide.md +184 -35
  67. package/docs/simple-server-api-design.md +72 -17
  68. package/docs/system-architecture.md +18 -14
  69. package/examples/controls/15) window, observable SSE/server.js +6 -1
  70. package/examples/controls/19) window, auto observable ui/server.js +9 -0
  71. package/examples/controls/20) window, task manager app/README.md +133 -0
  72. package/examples/controls/20) window, task manager app/client.js +797 -0
  73. package/examples/controls/20) window, task manager app/server.js +178 -0
  74. package/examples/controls/6) window, color_palette/client.js +165 -68
  75. package/examples/controls/9) window, date picker/client.js +362 -76
  76. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  77. package/examples/jsgui3-html/06) theming/client.js +22 -1
  78. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  79. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  80. package/lab/experiments/capture-color-controls.js +196 -0
  81. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  82. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  83. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  84. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  85. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  86. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  87. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  88. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  89. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  90. package/lab/screenshot-utils.js +248 -0
  91. package/module.js +11 -4
  92. package/package.json +12 -2
  93. package/publishers/Publishers.js +4 -3
  94. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  95. package/publishers/http-sse-publisher.js +341 -0
  96. package/resources/process-resource.js +950 -0
  97. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
  98. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  99. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  100. package/resources/remote-process-resource.js +355 -0
  101. package/resources/server-resource-pool.js +354 -41
  102. package/serve-factory.js +441 -259
  103. package/server.js +89 -13
  104. package/tests/README.md +66 -4
  105. package/tests/admin-ui-render.test.js +24 -0
  106. package/tests/assigners.test.js +56 -40
  107. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  108. package/tests/configuration-validation.test.js +21 -18
  109. package/tests/content-analysis.test.js +7 -6
  110. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  111. package/tests/control-scan-manifest-regression.test.js +144 -0
  112. package/tests/end-to-end.test.js +15 -14
  113. package/tests/error-handling.test.js +222 -179
  114. package/tests/fixtures/bundling-default-button-client.js +37 -0
  115. package/tests/fixtures/bundling-default-window-client.js +34 -0
  116. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  117. package/tests/fixtures/resource-monitor-client.js +319 -0
  118. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  119. package/tests/http-sse-publisher.test.js +136 -0
  120. package/tests/performance.test.js +69 -65
  121. package/tests/process-resource.test.js +138 -0
  122. package/tests/publishers.test.js +7 -7
  123. package/tests/remote-process-resource.test.js +160 -0
  124. package/tests/sass-controls.e2e.test.js +7 -1
  125. package/tests/serve-resources.test.js +270 -0
  126. package/tests/serve.test.js +120 -50
  127. package/tests/server-resource-pool.test.js +106 -0
  128. package/tests/small-controls-bundle-size.test.js +252 -0
  129. package/tests/test-runner.js +13 -1
  130. package/tests/window-examples.puppeteer.test.js +204 -1
  131. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  132. package/tests/temp_invalid.js +0 -7
  133. package/tests/temp_invalid_utf8.js +0 -1
  134. package/tests/temp_malformed.js +0 -10
@@ -0,0 +1,40 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+ Active_HTML_Document: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/1-standard/5-ui/Active_HTML_Document'),
36
+ Button: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/0-core/0-basic/0-native-compositional/button')
37
+ });
38
+ Object.assign(jsgui, jsgui.controls);
39
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
40
+ module.exports = jsgui;
@@ -0,0 +1,40 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+ Active_HTML_Document: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/1-standard/5-ui/Active_HTML_Document'),
36
+ Window: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/1-standard/6-layout/window')
37
+ });
38
+ Object.assign(jsgui, jsgui.controls);
39
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
40
+ module.exports = jsgui;
@@ -0,0 +1,39 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+ Window: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/1-standard/6-layout/window')
36
+ });
37
+ Object.assign(jsgui, jsgui.controls);
38
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
39
+ module.exports = jsgui;
@@ -0,0 +1,39 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+
36
+ });
37
+ Object.assign(jsgui, jsgui.controls);
38
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
39
+ module.exports = jsgui;
@@ -0,0 +1,39 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+
36
+ });
37
+ Object.assign(jsgui, jsgui.controls);
38
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
39
+ module.exports = jsgui;
@@ -0,0 +1,39 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+
36
+ });
37
+ Object.assign(jsgui, jsgui.controls);
38
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
39
+ module.exports = jsgui;
@@ -0,0 +1,39 @@
1
+ const jsgui = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/html-core/html-core.js');
2
+ jsgui.Router = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/router/router.js');
3
+ jsgui.Resource = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/resource.js');
4
+ jsgui.Resource_Pool = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/pool.js');
5
+ jsgui.Resource.Data_KV = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-kv-resource.js');
6
+ jsgui.Resource.Data_Transform = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/data-transform-resource.js');
7
+ jsgui.Resource.Compilation = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compilation-resource.js');
8
+ jsgui.Resource.Compiler = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/resource/compiler-resource.js');
9
+ jsgui.gfx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/node_modules/jsgui3-gfx-core/core/gfx-core.js');
10
+ jsgui.Resource.load_compiler = (name, jsfn, options) => {
11
+ const compiler_name = name;
12
+ const compiler_fn = jsfn;
13
+ const compiler_options = options || {};
14
+ if (typeof compiler_name !== 'string' || compiler_name.length === 0) {
15
+ throw new Error('Resource.load_compiler(name, fn, options) requires a non-empty string name');
16
+ }
17
+ if (typeof compiler_fn !== 'function') {
18
+ throw new Error('Resource.load_compiler(name, fn, options) requires a function compiler implementation');
19
+ }
20
+ const compiler_resource = new jsgui.Resource.Compiler({ name: compiler_name });
21
+ compiler_resource.transform = (input, transform_options = {}) => {
22
+ const merged_options = Object.assign({}, compiler_options, transform_options);
23
+ return compiler_fn(input, merged_options);
24
+ };
25
+ jsgui.Resource.compilers = jsgui.Resource.compilers || {};
26
+ jsgui.Resource.compilers[compiler_name] = compiler_resource;
27
+ const pool = compiler_options.pool || compiler_options.resource_pool;
28
+ if (pool && typeof pool.add === 'function') {
29
+ pool.add(compiler_resource);
30
+ }
31
+ return compiler_resource;
32
+ };
33
+ jsgui.controls = jsgui.controls || {};
34
+ Object.assign(jsgui.controls, {
35
+ Window: require('/mnt/c/Users/james/Documents/repos/jsgui3-html/controls/organised/1-standard/6-layout/window')
36
+ });
37
+ Object.assign(jsgui, jsgui.controls);
38
+ jsgui.mixins = jsgui.mx = require('/mnt/c/Users/james/Documents/repos/jsgui3-html/control_mixins/mx.js');
39
+ module.exports = jsgui;
package/README.md CHANGED
@@ -8,7 +8,7 @@ This is the primary entry point for understanding the JSGUI3 Server project. Rea
8
8
  - You're looking for quick start guides and basic usage examples
9
9
  - You need to understand the project's capabilities and limitations
10
10
 
11
- **Note:** For detailed API documentation, see [docs/comprehensive-documentation.md](docs/comprehensive-documentation.md). For advanced server API design, see [docs/simple-server-api-design.md](docs/simple-server-api-design.md).
11
+ **Note:** For detailed API documentation, see [docs/comprehensive-documentation.md](docs/comprehensive-documentation.md). For advanced server API design, see [docs/simple-server-api-design.md](docs/simple-server-api-design.md). For dense internals focused on startup, resources, SSE, and E2E verification, see [docs/core/jsgui3-server-core-book/00-table-of-contents.md](docs/core/jsgui3-server-core-book/00-table-of-contents.md). For deep bundling research and lightweight-bundle strategy, see [docs/books/jsgui3-bundling-research-book/README.md](docs/books/jsgui3-bundling-research-book/README.md).
12
12
 
13
13
  # Jsgui3 Server
14
14
 
@@ -72,6 +72,66 @@ Server.serve({
72
72
  });
73
73
  ```
74
74
 
75
+ ### Managed Resources (In-Process, Direct Process, Remote Process)
76
+
77
+ ```javascript
78
+ const Server = require('jsgui3-server');
79
+ const { Resource } = require('jsgui3-server');
80
+
81
+ class In_Process_Cache_Resource extends Resource {
82
+ constructor(spec = {}) {
83
+ super(spec);
84
+ this.cache = new Map();
85
+ }
86
+ start(callback) { callback(null, true); }
87
+ stop(callback) { this.cache.clear(); callback(null, true); }
88
+ }
89
+
90
+ const server = await Server.serve({
91
+ port: 3000,
92
+ api: {
93
+ 'resources/summary': () => server.resource_pool.summary
94
+ },
95
+ resources: {
96
+ // Existing in-process resource instance
97
+ cache: new In_Process_Cache_Resource({ name: 'cache' }),
98
+
99
+ // Local child process (default process_manager is direct)
100
+ worker_direct: {
101
+ type: 'process',
102
+ command: process.execPath,
103
+ args: ['worker.js'],
104
+ autoRestart: true
105
+ },
106
+
107
+ // Local process managed by PM2 (pm2Path is optional)
108
+ worker_pm2: {
109
+ type: 'process',
110
+ processManager: {
111
+ type: 'pm2'
112
+ },
113
+ command: process.execPath,
114
+ args: ['worker.js']
115
+ },
116
+
117
+ // Remote process controlled over HTTP
118
+ remote_worker: {
119
+ type: 'remote',
120
+ host: '127.0.0.1',
121
+ port: 3400
122
+ }
123
+ },
124
+ events: true // creates /events SSE endpoint and forwards pool resource lifecycle events
125
+ });
126
+ ```
127
+
128
+ All process-style resources expose a consistent API:
129
+ - `start()`
130
+ - `stop()`
131
+ - `restart()`
132
+ - `status` (includes `state`, `pid`, `uptime`, `restartCount`, `lastHealthCheck`, `memoryUsage`, `processManager`)
133
+ - `get_abstract()`
134
+
75
135
  ### Real-time SSE Streams
76
136
 
77
137
  ```javascript
@@ -83,11 +143,29 @@ const obs = observable(next => {
83
143
 
84
144
  // Start server and publish stream
85
145
  const server = await Server.serve(MyControl);
86
- server.publish_observable('/api/stream', obs);
87
- // Server publishes SSE stream at http://localhost:3000/api/stream
146
+
147
+ // Simple name - auto-prefixes /api/
148
+ server.publish_observable('stream', obs);
149
+ // Server publishes SSE stream at http://localhost:8080/api/stream
150
+
151
+ // Or use full route path for custom routes
152
+ server.publish_observable('/custom/events', obs);
153
+ // Server publishes SSE stream at http://localhost:8080/custom/events
154
+
88
155
  // Client connects via EventSource or Remote_Observable
89
156
  ```
90
157
 
158
+ ### General SSE Publisher
159
+
160
+ ```javascript
161
+ const HTTP_SSE_Publisher = require('jsgui3-server/publishers/http-sse-publisher');
162
+
163
+ const sse_publisher = new HTTP_SSE_Publisher({ name: 'events' });
164
+ server.server_router.set_route('/events', sse_publisher, sse_publisher.handle_http);
165
+
166
+ sse_publisher.broadcast('resource_update', { running: 3, total: 5 });
167
+ ```
168
+
91
169
  > **Note:** The new `Server.serve()` API is the recommended approach for most use cases. See [Simple Server API Design](docs/simple-server-api-design.md) for complete documentation and advanced features.
92
170
 
93
171
  ## Architecture Overview
@@ -909,11 +987,15 @@ Minimal example:
909
987
  ```javascript
910
988
  // Inside your server bootstrap (after constructing Server)
911
989
  server.on('ready', () => {
990
+ // Simple name - auto-prefixes /api/
912
991
  // Returns text/plain at GET/POST /api/hello
913
992
  server.publish('hello', name => `Hello ${name || 'world'}`);
914
993
 
915
994
  // Returns application/json at GET/POST /api/sum
916
995
  server.publish('sum', ({ a, b }) => ({ sum: a + b }));
996
+
997
+ // Full route path for custom routes (no auto-prefix)
998
+ server.publish('/custom/endpoint', () => ({ custom: true }));
917
999
  });
918
1000
  ```
919
1001
 
@@ -50,9 +50,9 @@ class Admin_Page extends Active_HTML_Document {
50
50
  top_bar.add(this.page_title);
51
51
 
52
52
  // Content Panel
53
- const content = new controls.div({ context, class: 'admin-content' });
54
- main.add(content);
55
- this.content = content;
53
+ const content_panel = new controls.div({ context, class: 'admin-content' });
54
+ main.add(content_panel);
55
+ this.content_panel = content_panel;
56
56
 
57
57
  // Default content (Overview)
58
58
  this._render_overview();
@@ -75,15 +75,15 @@ class Admin_Page extends Active_HTML_Document {
75
75
  }
76
76
 
77
77
  _render_overview() {
78
- // Clear main content area (this.content is the admin-content div)
79
- if (this.content && this.content.content && typeof this.content.content.clear === 'function') {
80
- this.content.content.clear();
81
- }
78
+ // Clear main content area (content_panel is the admin-content div)
79
+ if (this.content_panel && this.content_panel.content && typeof this.content_panel.content.clear === 'function') {
80
+ this.content_panel.content.clear();
81
+ }
82
82
 
83
83
  const welcome = new controls.div({ context: this.context, class: 'welcome-card' });
84
84
  welcome.add(new controls.h3({ context: this.context }).add('Welcome directly to jsgui3-server Admin'));
85
85
  welcome.add(new controls.p({ context: this.context }).add('Select a resource from the sidebar to inspect.'));
86
- this.content.add(welcome);
86
+ this.content_panel.add(welcome);
87
87
  }
88
88
 
89
89
  activate() {
package/dev-status.svg ADDED
@@ -0,0 +1,139 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 850" style="background:#1e1e2e; font-family: 'Segoe UI', sans-serif; color: #cdd6f4;">
2
+ <!-- Definitions for gradients and shadows -->
3
+ <defs>
4
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
5
+ <feDropShadow dx="3" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.5"/>
6
+ </filter>
7
+ <linearGradient id="grad-server" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" style="stop-color:#313244;stop-opacity:1" />
9
+ <stop offset="100%" style="stop-color:#45475a;stop-opacity:1" />
10
+ </linearGradient>
11
+ <linearGradient id="grad-client" x1="0%" y1="0%" x2="100%" y2="100%">
12
+ <stop offset="0%" style="stop-color:#181825;stop-opacity:1" />
13
+ <stop offset="100%" style="stop-color:#1e1e2e;stop-opacity:1" />
14
+ </linearGradient>
15
+ <linearGradient id="grad-success" x1="0%" y1="0%" x2="100%" y2="100%">
16
+ <stop offset="0%" style="stop-color:#a6e3a1;stop-opacity:0.2" />
17
+ <stop offset="100%" style="stop-color:#a6e3a1;stop-opacity:0.1" />
18
+ </linearGradient>
19
+ <marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
20
+ <path d="M0,0 L0,6 L9,3 z" fill="#89b4fa" />
21
+ </marker>
22
+ </defs>
23
+
24
+ <!-- Title -->
25
+ <text x="50" y="50" fill="#cdd6f4" font-size="32" font-weight="bold">jsgui3-server: Dev Status & Architecture</text>
26
+ <text x="50" y="80" fill="#a6adc8" font-size="16">Admin UI • Auto-Observables • Defensive Programming • Server Lifecycle</text>
27
+
28
+ <!-- Areas -->
29
+ <rect x="50" y="110" width="500" height="700" rx="15" fill="url(#grad-server)" stroke="#585b70" stroke-width="2" />
30
+ <text x="70" y="140" fill="#fab387" font-size="24" font-weight="bold">Server Side (Node.js)</text>
31
+
32
+ <rect x="650" y="110" width="500" height="700" rx="15" fill="url(#grad-client)" stroke="#585b70" stroke-width="2" />
33
+ <text x="670" y="140" fill="#89b4fa" font-size="24" font-weight="bold">Client Side (Browser)</text>
34
+
35
+ <!-- Server Components -->
36
+
37
+ <!-- Server Lifecycle Box -->
38
+ <g transform="translate(80, 180)">
39
+ <rect width="440" height="140" rx="8" fill="#313244" stroke="#f38ba8" stroke-width="2" stroke-dasharray="5,5" filter="url(#shadow)" />
40
+ <text x="15" y="30" fill="#f38ba8" font-size="18" font-weight="bold">Lifecycle Fixes</text>
41
+
42
+ <g transform="translate(20, 50)">
43
+ <circle cx="10" cy="10" r="5" fill="#f38ba8" />
44
+ <text x="25" y="15" fill="#cdd6f4" font-size="14">1. _started Guard (Prevents double start)</text>
45
+ </g>
46
+ <g transform="translate(20, 80)">
47
+ <circle cx="10" cy="10" r="5" fill="#f38ba8" />
48
+ <text x="25" y="15" fill="#cdd6f4" font-size="14">2. Split Events: 'ready' vs 'listening'</text>
49
+ </g>
50
+ <g transform="translate(20, 110)">
51
+ <text x="25" y="15" fill="#a6adc8" font-size="12" font-style="italic">Fixed EADDRINUSE crash root cause</text>
52
+ </g>
53
+ </g>
54
+
55
+ <!-- Admin Module -->
56
+ <g transform="translate(80, 350)">
57
+ <rect width="200" height="120" rx="8" fill="#45475a" stroke="#89b4fa" stroke-width="2" filter="url(#shadow)" />
58
+ <text x="50" y="30" fill="#89b4fa" font-size="18" font-weight="bold">Admin_Module</text>
59
+ <line x1="10" y1="40" x2="190" y2="40" stroke="#585b70" />
60
+ <text x="15" y="65" fill="#cdd6f4" font-size="14">GET /api/admin/resources</text>
61
+ <text x="15" y="90" fill="#cdd6f4" font-size="14">GET /api/admin/observables</text>
62
+ </g>
63
+
64
+ <!-- Publishers -->
65
+ <g transform="translate(320, 350)">
66
+ <rect width="200" height="120" rx="8" fill="#45475a" stroke="#a6e3a1" stroke-width="2" filter="url(#shadow)" />
67
+ <text x="30" y="30" fill="#a6e3a1" font-size="18" font-weight="bold">Publishers</text>
68
+ <line x1="10" y1="40" x2="190" y2="40" stroke="#585b70" />
69
+ <text x="15" y="65" fill="#cdd6f4" font-size="14">HTTP_Webpage_Publisher</text>
70
+ <text x="15" y="90" fill="#cdd6f4" font-size="14">HTTP_Observable_Publisher</text>
71
+ </g>
72
+
73
+ <!-- Defensive Layers -->
74
+ <g transform="translate(80, 500)">
75
+ <rect width="440" height="100" rx="8" fill="url(#grad-success)" stroke="#a6e3a1" stroke-width="1" />
76
+ <text x="15" y="30" fill="#a6e3a1" font-size="18" font-weight="bold">Defensive Coding Layers</text>
77
+ <text x="20" y="60" fill="#cdd6f4" font-size="14">• Resource_Pool.add(undefined) → Warn & Return</text>
78
+ <text x="20" y="80" fill="#cdd6f4" font-size="14">• Bundler Failures → Fallback Empty Bundle</text>
79
+ </g>
80
+
81
+ <!-- Bundler -->
82
+ <g transform="translate(80, 630)">
83
+ <rect width="440" height="60" rx="8" fill="#313244" stroke="#fab387" stroke-width="2" />
84
+ <text x="15" y="35" fill="#fab387" font-size="18" font-weight="bold">Bundler (esbuild)</text>
85
+ <text x="180" y="35" fill="#cdd6f4" font-size="14">Fixed: npm link jsgui3-html</text>
86
+ </g>
87
+
88
+
89
+ <!-- Client Components -->
90
+
91
+ <!-- Admin Page -->
92
+ <g transform="translate(680, 250)">
93
+ <rect width="440" height="180" rx="8" fill="#313244" stroke="#89b4fa" stroke-width="2" filter="url(#shadow)" />
94
+ <text x="15" y="30" fill="#89b4fa" font-size="18" font-weight="bold">Admin_Page (Client)</text>
95
+ <line x1="10" y1="40" x2="430" y2="40" stroke="#585b70" />
96
+
97
+ <!-- Child Controls -->
98
+ <g transform="translate(20, 60)">
99
+ <rect width="180" height="40" rx="4" fill="#45475a" stroke="#cba6f7" stroke-width="1" />
100
+ <text x="35" y="25" fill="#cba6f7" font-size="14">Resource_List</text>
101
+ </g>
102
+
103
+ <g transform="translate(220, 60)">
104
+ <rect width="180" height="40" rx="4" fill="#45475a" stroke="#cba6f7" stroke-width="1" />
105
+ <text x="25" y="25" fill="#cba6f7" font-size="14">Observables_List</text>
106
+ </g>
107
+
108
+ <g transform="translate(120, 120)">
109
+ <rect width="200" height="40" rx="4" fill="#45475a" stroke="#f9e2af" stroke-width="1" />
110
+ <text x="30" y="25" fill="#f9e2af" font-size="14">Auto_Observable_UI</text>
111
+ </g>
112
+ </g>
113
+
114
+ <!-- Data Flow Arrows -->
115
+
116
+ <!-- Route Info -->
117
+ <path d="M 280 410 L 680 300" stroke="#89b4fa" stroke-width="2" stroke-dasharray="5,5" fill="none" marker-end="url(#arrow)" />
118
+ <rect x="420" y="330" width="120" height="25" rx="4" fill="#1e1e2e" stroke="#89b4fa" />
119
+ <text x="430" y="347" fill="#89b4fa" font-size="12">JSON Data</text>
120
+
121
+ <!-- Observable Stream -->
122
+ <path d="M 430 470 L 800 390" stroke="#a6e3a1" stroke-width="2" fill="none" marker-end="url(#arrow)" />
123
+ <rect x="560" y="440" width="100" height="25" rx="4" fill="#1e1e2e" stroke="#a6e3a1" />
124
+ <text x="575" y="457" fill="#a6e3a1" font-size="12">SSE Steam</text>
125
+
126
+ <!-- Legend -->
127
+ <g transform="translate(900, 750)">
128
+ <text x="0" y="0" fill="#a6adc8" font-size="14" font-weight="bold">Legend</text>
129
+ <rect x="0" y="10" width="15" height="15" fill="#313244" stroke="#f38ba8" />
130
+ <text x="25" y="23" fill="#cdd6f4" font-size="12">Lifecycle Code</text>
131
+
132
+ <rect x="0" y="35" width="15" height="15" fill="#313244" stroke="#89b4fa" />
133
+ <text x="25" y="48" fill="#cdd6f4" font-size="12">UI / Route Component</text>
134
+
135
+ <rect x="0" y="60" width="15" height="15" fill="#313244" stroke="#a6e3a1" />
136
+ <text x="25" y="73" fill="#cdd6f4" font-size="12">Observable / Defensive</text>
137
+ </g>
138
+
139
+ </svg>