jsgui3-server 0.0.147 → 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.
- package/.github/workflows/control-scan-manifest-check.yml +31 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
- package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
- package/README.md +85 -3
- package/admin-ui/client.js +213 -0
- package/admin-ui/server.js +104 -0
- package/client/controls/auto-observable.js +207 -0
- package/dev-status.svg +139 -0
- package/docs/api-reference.md +301 -43
- package/docs/books/admin-ui/01-introduction.md +32 -0
- package/docs/books/admin-ui/02-architecture.md +92 -0
- package/docs/books/admin-ui/03-controls.md +194 -0
- package/docs/books/admin-ui/04-implementation-plan.md +62 -0
- package/docs/books/admin-ui/README.md +26 -0
- package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
- package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
- package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
- package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
- package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
- package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
- package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
- package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
- package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
- package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
- package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
- package/docs/bundling-system-deep-dive.md +9 -4
- package/docs/comprehensive-documentation.md +49 -18
- package/docs/configuration-reference.md +152 -27
- package/docs/core/README.md +19 -0
- package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
- package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
- package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
- package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
- package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
- package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
- package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
- package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
- package/docs/publishers-guide.md +59 -4
- package/docs/resources-guide.md +184 -35
- package/docs/simple-server-api-design.md +72 -17
- package/docs/system-architecture.md +18 -14
- package/examples/controls/15) window, observable SSE/server.js +6 -1
- package/examples/controls/19) window, auto observable ui/client.js +125 -0
- package/examples/controls/19) window, auto observable ui/server.js +73 -0
- package/examples/controls/20) window, task manager app/README.md +133 -0
- package/examples/controls/20) window, task manager app/client.js +797 -0
- package/examples/controls/20) window, task manager app/server.js +178 -0
- package/examples/controls/6) window, color_palette/client.js +165 -68
- package/examples/controls/9) window, date picker/client.js +362 -76
- package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
- package/examples/jsgui3-html/06) theming/client.js +22 -1
- package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
- package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
- package/lab/experiments/capture-color-controls.js +196 -0
- package/lab/results/screenshots/color-controls/full_page.png +0 -0
- package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
- package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
- package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
- package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
- package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
- package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
- package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
- package/lab/screenshot-utils.js +248 -0
- package/module.js +11 -4
- package/package.json +14 -4
- package/publishers/Publishers.js +4 -3
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
- package/publishers/http-observable-publisher.js +8 -0
- package/publishers/http-sse-publisher.js +341 -0
- package/publishers/http-webpage-publisher.js +13 -3
- package/publishers/http-webpageorsite-publisher.js +18 -0
- package/resources/process-resource.js +950 -0
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +164 -46
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
- package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
- package/resources/remote-process-resource.js +355 -0
- package/resources/server-resource-pool.js +354 -41
- package/serve-factory.js +441 -259
- package/server.js +161 -16
- package/tests/README.md +66 -4
- package/tests/admin-ui-render.test.js +24 -0
- package/tests/assigners.test.js +56 -40
- package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
- package/tests/configuration-validation.test.js +21 -18
- package/tests/content-analysis.test.js +7 -6
- package/tests/control-optimizer-cache-behavior.test.js +52 -0
- package/tests/control-scan-manifest-regression.test.js +144 -0
- package/tests/end-to-end.test.js +15 -14
- package/tests/error-handling.test.js +222 -179
- package/tests/fixtures/bundling-default-button-client.js +37 -0
- package/tests/fixtures/bundling-default-window-client.js +34 -0
- package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
- package/tests/fixtures/resource-monitor-client.js +319 -0
- package/tests/helpers/puppeteer-e2e-harness.js +317 -0
- package/tests/http-sse-publisher.test.js +136 -0
- package/tests/performance.test.js +69 -65
- package/tests/process-resource.test.js +138 -0
- package/tests/publishers.test.js +7 -7
- package/tests/remote-process-resource.test.js +160 -0
- package/tests/sass-controls.e2e.test.js +7 -1
- package/tests/serve-resources.test.js +270 -0
- package/tests/serve.test.js +120 -50
- package/tests/server-resource-pool.test.js +106 -0
- package/tests/small-controls-bundle-size.test.js +252 -0
- package/tests/test-runner.js +13 -1
- package/tests/window-examples.puppeteer.test.js +204 -1
- package/tests/window-resource-integration.puppeteer.test.js +585 -0
- package/tests/temp_invalid.js +0 -7
- package/tests/temp_invalid_utf8.js +0 -1
- package/tests/temp_malformed.js +0 -10
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 1. Startup Readiness State Machine
|
|
2
|
+
|
|
3
|
+
## Core Problem
|
|
4
|
+
|
|
5
|
+
A server that binds a socket before its route graph is installed can return transient `404` responses during cold start. This is a correctness defect, not a cosmetic delay.
|
|
6
|
+
|
|
7
|
+
## Current Model
|
|
8
|
+
|
|
9
|
+
`Server.serve(...)` now enforces a strict ordering:
|
|
10
|
+
|
|
11
|
+
1. construct `Server` instance
|
|
12
|
+
2. install API publishers and optional SSE publisher
|
|
13
|
+
3. wait for server `ready` event (emitted after webpage/website publisher route installation)
|
|
14
|
+
4. resolve additional page route preparations
|
|
15
|
+
5. bind sockets with `server.start(...)`
|
|
16
|
+
6. start configured resources
|
|
17
|
+
7. resolve `serve(...)` promise
|
|
18
|
+
|
|
19
|
+
## Removed Failure Pattern
|
|
20
|
+
|
|
21
|
+
Previous behavior had a fallback timer that could start listening before `ready`. This allowed requests to arrive before `/` and bundle routes were present.
|
|
22
|
+
|
|
23
|
+
That path is removed. Startup now rejects on readiness timeout instead of entering a half-ready listening state.
|
|
24
|
+
|
|
25
|
+
## Startup Timeout
|
|
26
|
+
|
|
27
|
+
`readyTimeoutMs` (default `120000`) bounds waiting for readiness. Timeout behavior:
|
|
28
|
+
|
|
29
|
+
- if readiness is not reached in time, `serve(...)` rejects
|
|
30
|
+
- no forced early listen occurs
|
|
31
|
+
|
|
32
|
+
This makes startup failure explicit and testable.
|
|
33
|
+
|
|
34
|
+
## Verification
|
|
35
|
+
|
|
36
|
+
`tests/serve.test.js` includes a delayed fake webpage publisher and asserts that:
|
|
37
|
+
|
|
38
|
+
- `serve(...)` does not resolve until delayed readiness arrives
|
|
39
|
+
- post-resolution `/` is immediately routable
|
|
40
|
+
|
|
41
|
+
This test prevents regression to pre-ready listening.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# 2. Resource Abstraction and Lifecycle
|
|
2
|
+
|
|
3
|
+
## Uniform Contract
|
|
4
|
+
|
|
5
|
+
`Process_Resource` and `Remote_Process_Resource` expose the same operational surface:
|
|
6
|
+
|
|
7
|
+
- `start()`
|
|
8
|
+
- `stop()`
|
|
9
|
+
- `restart()`
|
|
10
|
+
- `status` getter
|
|
11
|
+
- `get_abstract()`
|
|
12
|
+
|
|
13
|
+
The contract allows client/API code to remain management-backend agnostic.
|
|
14
|
+
|
|
15
|
+
## Process_Resource Modes
|
|
16
|
+
|
|
17
|
+
`Process_Resource` supports two process managers:
|
|
18
|
+
|
|
19
|
+
- `direct` (default): local child process (`spawn`)
|
|
20
|
+
- `pm2`: external manager integration (`pm2 start/stop/restart/jlist`)
|
|
21
|
+
|
|
22
|
+
PM2 path resolution is optional and automatic:
|
|
23
|
+
|
|
24
|
+
1. `processManager.pm2Path`
|
|
25
|
+
2. `PM2_PATH` env
|
|
26
|
+
3. local `node_modules/.bin/pm2(.cmd)`
|
|
27
|
+
4. `pm2` from `PATH`
|
|
28
|
+
|
|
29
|
+
Therefore explicit `pm2Path` is not required for default operation.
|
|
30
|
+
|
|
31
|
+
## State Machine
|
|
32
|
+
|
|
33
|
+
Nominal states:
|
|
34
|
+
|
|
35
|
+
- `stopped`
|
|
36
|
+
- `starting`
|
|
37
|
+
- `running`
|
|
38
|
+
- `stopping`
|
|
39
|
+
- `restarting`
|
|
40
|
+
- `crashed`
|
|
41
|
+
|
|
42
|
+
`Remote_Process_Resource` additionally uses `unreachable` for transport failure.
|
|
43
|
+
|
|
44
|
+
Each transition emits `state_change` with `{ from, to, timestamp }`.
|
|
45
|
+
|
|
46
|
+
## Operational Signals
|
|
47
|
+
|
|
48
|
+
`Process_Resource` emits:
|
|
49
|
+
|
|
50
|
+
- `stdout` / `stderr` (line-wise)
|
|
51
|
+
- `exit`
|
|
52
|
+
- `health_check`
|
|
53
|
+
- `unhealthy`
|
|
54
|
+
- `crashed`
|
|
55
|
+
|
|
56
|
+
`Remote_Process_Resource` emits:
|
|
57
|
+
|
|
58
|
+
- `state_change`
|
|
59
|
+
- `unreachable`
|
|
60
|
+
- `recovered`
|
|
61
|
+
|
|
62
|
+
## Failure Handling
|
|
63
|
+
|
|
64
|
+
### Local process
|
|
65
|
+
|
|
66
|
+
Unexpected non-zero exit with `autoRestart: true` enters exponential backoff restart.
|
|
67
|
+
|
|
68
|
+
Backoff progression:
|
|
69
|
+
|
|
70
|
+
- base `restartBackoffBaseMs` (default 1000)
|
|
71
|
+
- doubles per attempt
|
|
72
|
+
- capped at 60000ms
|
|
73
|
+
|
|
74
|
+
Exceeding `maxRestarts` transitions to `crashed` and halts auto-restart.
|
|
75
|
+
|
|
76
|
+
### Remote process
|
|
77
|
+
|
|
78
|
+
Transport failures do not map to `crashed`; they map to `unreachable`. Recovery is explicit via `recovered` event when polling succeeds again.
|
|
79
|
+
|
|
80
|
+
## Observability Payload
|
|
81
|
+
|
|
82
|
+
`status` normalizes operational metrics:
|
|
83
|
+
|
|
84
|
+
- state
|
|
85
|
+
- pid (if meaningful)
|
|
86
|
+
- uptime
|
|
87
|
+
- restartCount
|
|
88
|
+
- lastHealthCheck
|
|
89
|
+
- memoryUsage
|
|
90
|
+
- processManager metadata
|
|
91
|
+
|
|
92
|
+
The normalized shape is essential for stable API and UI projections.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 3. Resource Pool and Event Topology
|
|
2
|
+
|
|
3
|
+
## Role
|
|
4
|
+
|
|
5
|
+
`Server_Resource_Pool` is the system-wide resource coordinator. It extends `Resource_Pool` and adds lifecycle orchestration plus event fan-in.
|
|
6
|
+
|
|
7
|
+
## Lifecycle Surface
|
|
8
|
+
|
|
9
|
+
Implemented operations:
|
|
10
|
+
|
|
11
|
+
- `add(resource)` / `push(resource)`
|
|
12
|
+
- `remove(name)` with stop-on-remove semantics
|
|
13
|
+
- `start(callback)` start-all
|
|
14
|
+
- `stop(callback)` stop-all
|
|
15
|
+
- `get_resources_by_type(type)`
|
|
16
|
+
- `summary` getter
|
|
17
|
+
|
|
18
|
+
## Event Fan-In
|
|
19
|
+
|
|
20
|
+
Pool-level forwarded events:
|
|
21
|
+
|
|
22
|
+
- `resource_state_change`
|
|
23
|
+
- `crashed`
|
|
24
|
+
- `unhealthy`
|
|
25
|
+
- `unreachable`
|
|
26
|
+
- `recovered`
|
|
27
|
+
|
|
28
|
+
Forwarding payload includes `resourceName` plus original event fields.
|
|
29
|
+
|
|
30
|
+
This gives a single subscription point for system telemetry.
|
|
31
|
+
|
|
32
|
+
## Summary as Fleet Snapshot
|
|
33
|
+
|
|
34
|
+
`summary` aggregates:
|
|
35
|
+
|
|
36
|
+
- totals by state (`running`, `stopped`, `crashed`, `unreachable`, ...)
|
|
37
|
+
- `byType` entries with `get_abstract()` output per resource
|
|
38
|
+
|
|
39
|
+
`summary` is designed for status APIs and dashboard views.
|
|
40
|
+
|
|
41
|
+
## Correctness Invariants
|
|
42
|
+
|
|
43
|
+
1. removing a resource must detach forwarded listeners
|
|
44
|
+
2. stop-all must tolerate mixed callback/promise resource implementations
|
|
45
|
+
3. no event should lose resource identity when forwarded
|
|
46
|
+
|
|
47
|
+
`tests/server-resource-pool.test.js` and `tests/serve-resources.test.js` exercise these invariants.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# 4. SSE Publisher Semantics
|
|
2
|
+
|
|
3
|
+
## Component
|
|
4
|
+
|
|
5
|
+
`HTTP_SSE_Publisher` is a general-purpose server-sent events publisher, separate from observable-specific transport.
|
|
6
|
+
|
|
7
|
+
## Responsibilities
|
|
8
|
+
|
|
9
|
+
- maintain connection registry (`client_id -> response stream`)
|
|
10
|
+
- format protocol-correct SSE frames (`id`, `event`, `data`)
|
|
11
|
+
- provide `broadcast(event, data)`
|
|
12
|
+
- provide `send(client_id, event, data)`
|
|
13
|
+
- issue keepalive comments (`:keepalive`) to prevent idle timeout
|
|
14
|
+
- maintain bounded event history for replay
|
|
15
|
+
- support `Last-Event-ID` replay on reconnect
|
|
16
|
+
|
|
17
|
+
## Delivery Semantics
|
|
18
|
+
|
|
19
|
+
- event IDs are monotonic and server-local
|
|
20
|
+
- replay sends events with `id > last_event_id`
|
|
21
|
+
- delivery is best-effort per connected client
|
|
22
|
+
- failed writes trigger client disconnect cleanup
|
|
23
|
+
|
|
24
|
+
## Operational Constraints
|
|
25
|
+
|
|
26
|
+
- max client guard (`maxClients`)
|
|
27
|
+
- explicit `stop()` for deterministic shutdown
|
|
28
|
+
- keepalive timer lifecycle tied to active client count
|
|
29
|
+
|
|
30
|
+
## Integration Contract
|
|
31
|
+
|
|
32
|
+
When `Server.serve({ events: true })` is used, pool events are bridged into SSE broadcasts. Resource state transitions become browser-consumable without client polling loops.
|
|
33
|
+
|
|
34
|
+
## Verification
|
|
35
|
+
|
|
36
|
+
`tests/http-sse-publisher.test.js` validates:
|
|
37
|
+
|
|
38
|
+
- multi-client broadcast
|
|
39
|
+
- targeted delivery
|
|
40
|
+
- keepalive emission
|
|
41
|
+
- replay via `Last-Event-ID`
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# 5. Serve Factory Resource Wiring
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
`Server.serve(...)` must be a single-entry composition API that can instantiate and wire:
|
|
6
|
+
|
|
7
|
+
- pages / controls
|
|
8
|
+
- API handlers
|
|
9
|
+
- resources (process or remote)
|
|
10
|
+
- SSE endpoint for resource events
|
|
11
|
+
|
|
12
|
+
without requiring the caller to manually compose internals.
|
|
13
|
+
|
|
14
|
+
## Resource Configuration Forms
|
|
15
|
+
|
|
16
|
+
Accepted `resources` forms include:
|
|
17
|
+
|
|
18
|
+
- direct resource instance
|
|
19
|
+
- constructor function / class
|
|
20
|
+
- `{ class | Ctor | constructor_fn, ...spec }`
|
|
21
|
+
- `{ type: 'process' | 'remote', ...spec }`
|
|
22
|
+
|
|
23
|
+
Type inference fallbacks:
|
|
24
|
+
|
|
25
|
+
- `command` or `processManager` -> `Process_Resource`
|
|
26
|
+
- `host` or `endpoints` -> `Remote_Process_Resource`
|
|
27
|
+
|
|
28
|
+
## Event Bridge
|
|
29
|
+
|
|
30
|
+
With `events: true`:
|
|
31
|
+
|
|
32
|
+
- `HTTP_SSE_Publisher` mounted on `/events` (or configured route)
|
|
33
|
+
- pool events broadcast onto SSE stream
|
|
34
|
+
|
|
35
|
+
This creates a default telemetry channel with zero extra server code.
|
|
36
|
+
|
|
37
|
+
## Startup Ordering
|
|
38
|
+
|
|
39
|
+
1. configure APIs, events, and resource specs
|
|
40
|
+
2. wait for ready
|
|
41
|
+
3. start listening
|
|
42
|
+
4. start configured resources
|
|
43
|
+
|
|
44
|
+
## Shutdown Ordering
|
|
45
|
+
|
|
46
|
+
`server.close()` triggers resource pool stop and SSE stop before closing HTTP servers. This prevents orphan resources and dangling SSE clients.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# 6. E2E Testing Methodology
|
|
2
|
+
|
|
3
|
+
## Principle
|
|
4
|
+
|
|
5
|
+
A browser E2E test is valid only if it asserts both:
|
|
6
|
+
|
|
7
|
+
- user-visible state (DOM/control behavior)
|
|
8
|
+
- server truth (resource/process state, event emission)
|
|
9
|
+
|
|
10
|
+
UI-only assertions are necessary but insufficient for fullstack correctness.
|
|
11
|
+
|
|
12
|
+
## Harness
|
|
13
|
+
|
|
14
|
+
`tests/helpers/puppeteer-e2e-harness.js` provides:
|
|
15
|
+
|
|
16
|
+
- deterministic step runner (`run_interaction_story`)
|
|
17
|
+
- browser probe capture (`console`, `pageerror`, request failures)
|
|
18
|
+
- shared page/server boot helpers
|
|
19
|
+
- stable interaction primitives (input event dispatch, drag)
|
|
20
|
+
|
|
21
|
+
This converts long imperative test scripts into explicit state-transition stories.
|
|
22
|
+
|
|
23
|
+
## Story Pattern
|
|
24
|
+
|
|
25
|
+
Each step has:
|
|
26
|
+
|
|
27
|
+
- `name`
|
|
28
|
+
- `run(page)`
|
|
29
|
+
- optional `assert(page)`
|
|
30
|
+
|
|
31
|
+
Failure is annotated with `[story :: step]` to localize defects quickly.
|
|
32
|
+
|
|
33
|
+
## Current High-Value Scenarios
|
|
34
|
+
|
|
35
|
+
1. control interaction precision (`Date_Picker` + `Datetime_Picker`)
|
|
36
|
+
2. client-driven resource lifecycle control (`start/stop/restart`)
|
|
37
|
+
3. SSE propagation to browser and UI projection
|
|
38
|
+
4. parity check between browser-observed state and server-side resource state
|
|
39
|
+
|
|
40
|
+
Implemented in:
|
|
41
|
+
|
|
42
|
+
- `tests/window-resource-integration.puppeteer.test.js`
|
|
43
|
+
|
|
44
|
+
## Stability Guards
|
|
45
|
+
|
|
46
|
+
- route readiness gate before browser navigation in resource integration tests
|
|
47
|
+
- allowlist-based probe assertions for expected disconnect noise (`/events` abort on close)
|
|
48
|
+
- strict no-unexpected-console-error policy
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 7. Defect Detection and Hardening Loop
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Maximize bug discovery rate while minimizing false confidence.
|
|
6
|
+
|
|
7
|
+
## Loop
|
|
8
|
+
|
|
9
|
+
1. define invariant
|
|
10
|
+
2. encode invariant as automated test
|
|
11
|
+
3. reproduce failure deterministically
|
|
12
|
+
4. implement smallest correctness-preserving fix
|
|
13
|
+
5. re-run focused suite
|
|
14
|
+
6. run regression suites touching adjacent surfaces
|
|
15
|
+
7. document invariant and fix rationale
|
|
16
|
+
|
|
17
|
+
## Practical Invariants for Current Surface
|
|
18
|
+
|
|
19
|
+
### Startup / Routing
|
|
20
|
+
|
|
21
|
+
- `serve()` must not resolve before root route availability
|
|
22
|
+
- startup timeout must fail explicitly, not partially start
|
|
23
|
+
|
|
24
|
+
### Resource Management
|
|
25
|
+
|
|
26
|
+
- `stop()` semantics are idempotent for already-stopped resources
|
|
27
|
+
- crash vs unreachable classification must remain distinct
|
|
28
|
+
- process manager backend must not leak into consumer API shape
|
|
29
|
+
|
|
30
|
+
### Eventing
|
|
31
|
+
|
|
32
|
+
- every forwarded resource event must include `resourceName`
|
|
33
|
+
- SSE replay must preserve event ordering by event ID
|
|
34
|
+
|
|
35
|
+
### Browser Integration
|
|
36
|
+
|
|
37
|
+
- client action routes must be observable in captured API call log
|
|
38
|
+
- SSE state transitions must drive UI state within bounded time
|
|
39
|
+
|
|
40
|
+
## Recommended Additional Test Work
|
|
41
|
+
|
|
42
|
+
1. Add a chaos-style resource test with rapid start/stop/restart bursts and assert no deadlock.
|
|
43
|
+
2. Add remote resource failure flapping test (unreachable/recovered oscillation) with threshold validation.
|
|
44
|
+
3. Add SSE reconnection E2E test in browser with explicit `Last-Event-ID` continuity assertion.
|
|
45
|
+
4. Add matrix tests across process manager modes (`direct`, `pm2` if available) under the same API contract assertions.
|
|
46
|
+
|
|
47
|
+
These increase confidence in concurrency and degraded-network behavior, where latent bugs usually appear.
|
package/docs/publishers-guide.md
CHANGED
|
@@ -100,7 +100,7 @@ const publisher = new HTTP_Function_Publisher({
|
|
|
100
100
|
- Support for common image formats (JPEG, PNG, SVG, etc.)
|
|
101
101
|
- Efficient streaming for large files
|
|
102
102
|
|
|
103
|
-
### HTTP_Observable_Publisher
|
|
103
|
+
### HTTP_Observable_Publisher
|
|
104
104
|
|
|
105
105
|
**Purpose:** Streams observable data to clients using Server-Sent Events (SSE).
|
|
106
106
|
|
|
@@ -190,9 +190,64 @@ data: {"tick":1,"timestamp":1234567890,"message":"Server tick #1"}
|
|
|
190
190
|
data: {"tick":2,"timestamp":1234567891,"message":"Server tick #2"}
|
|
191
191
|
```
|
|
192
192
|
|
|
193
|
-
**See Also:** [Observable SSE Demo](../examples/controls/15)%20window,%20observable%20SSE/) for a complete working example.
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
**See Also:** [Observable SSE Demo](../examples/controls/15)%20window,%20observable%20SSE/) for a complete working example.
|
|
194
|
+
|
|
195
|
+
### HTTP_SSE_Publisher
|
|
196
|
+
|
|
197
|
+
**Purpose:** General-purpose Server-Sent Events publisher for manual event fan-out (not tied to an observable source).
|
|
198
|
+
|
|
199
|
+
**Key Features:**
|
|
200
|
+
- SSE client connection registry with `client_count`
|
|
201
|
+
- `broadcast(event_name, data)` for fan-out to all clients
|
|
202
|
+
- `send(client_id, event_name, data)` for targeted delivery
|
|
203
|
+
- Keepalive comments (`:keepalive`) to prevent idle proxy timeouts
|
|
204
|
+
- Last-Event-ID replay support for reconnecting clients
|
|
205
|
+
- Optional max-client limit
|
|
206
|
+
- Emits:
|
|
207
|
+
- `client_connected`
|
|
208
|
+
- `client_disconnected`
|
|
209
|
+
|
|
210
|
+
**Usage:**
|
|
211
|
+
```javascript
|
|
212
|
+
const HTTP_SSE_Publisher = require('jsgui3-server/publishers/http-sse-publisher');
|
|
213
|
+
|
|
214
|
+
const sse_publisher = new HTTP_SSE_Publisher({
|
|
215
|
+
name: 'events',
|
|
216
|
+
keepaliveIntervalMs: 15000,
|
|
217
|
+
maxClients: 100
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
server.server_router.set_route('/events', sse_publisher, sse_publisher.handle_http);
|
|
221
|
+
|
|
222
|
+
sse_publisher.broadcast('resource_state_change', {
|
|
223
|
+
resourceName: 'worker_1',
|
|
224
|
+
from: 'running',
|
|
225
|
+
to: 'crashed'
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**With `Server.serve()` Auto Wiring:**
|
|
230
|
+
```javascript
|
|
231
|
+
const server = await Server.serve({
|
|
232
|
+
events: true,
|
|
233
|
+
resources: {
|
|
234
|
+
worker_1: {
|
|
235
|
+
type: 'process',
|
|
236
|
+
command: process.execPath,
|
|
237
|
+
args: ['worker.js']
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// SSE endpoint is available at /events by default.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Choosing an SSE Publisher
|
|
246
|
+
|
|
247
|
+
- Use `HTTP_Observable_Publisher` when your source is an observable stream.
|
|
248
|
+
- Use `HTTP_SSE_Publisher` when you need explicit event push control (`broadcast`/`send`) from arbitrary server logic.
|
|
249
|
+
|
|
250
|
+
## Publisher Architecture
|
|
196
251
|
|
|
197
252
|
### Base Publisher Class
|
|
198
253
|
|
package/docs/resources-guide.md
CHANGED
|
@@ -17,19 +17,26 @@ Resources are JSGUI3 Server's abstraction layer for accessing data, functionalit
|
|
|
17
17
|
|
|
18
18
|
## Resource Types
|
|
19
19
|
|
|
20
|
-
### Server_Resource_Pool
|
|
21
|
-
|
|
22
|
-
**Purpose:** Manages collections of resources with access control and lifecycle management.
|
|
23
|
-
|
|
24
|
-
**Key Features:**
|
|
25
|
-
- Resource registration and discovery
|
|
26
|
-
- Access control through permission systems
|
|
27
|
-
- Lifecycle management (start
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
### Server_Resource_Pool
|
|
21
|
+
|
|
22
|
+
**Purpose:** Manages collections of resources with access control and lifecycle management.
|
|
23
|
+
|
|
24
|
+
**Key Features:**
|
|
25
|
+
- Resource registration and discovery
|
|
26
|
+
- Access control through permission systems
|
|
27
|
+
- Lifecycle management (`start()`, `stop()`, `remove(name)`)
|
|
28
|
+
- Type filtering with `get_resources_by_type(type)`
|
|
29
|
+
- Aggregated `summary` view of resource states
|
|
30
|
+
- Resource event forwarding:
|
|
31
|
+
- `resource_state_change`
|
|
32
|
+
- `crashed`
|
|
33
|
+
- `unhealthy`
|
|
34
|
+
- `unreachable`
|
|
35
|
+
- `recovered`
|
|
36
|
+
|
|
37
|
+
**Usage:**
|
|
38
|
+
```javascript
|
|
39
|
+
const pool = new Server_Resource_Pool({
|
|
33
40
|
access: {
|
|
34
41
|
full: ['admin'],
|
|
35
42
|
read: ['user']
|
|
@@ -37,14 +44,117 @@ const pool = new Server_Resource_Pool({
|
|
|
37
44
|
});
|
|
38
45
|
|
|
39
46
|
pool.add(myResource);
|
|
40
|
-
pool.start((err) => {
|
|
41
|
-
// Pool is ready
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
pool.start((err) => {
|
|
48
|
+
// Pool is ready
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const summary = pool.summary;
|
|
52
|
+
const process_resources = pool.get_resources_by_type('Process_Resource');
|
|
53
|
+
await pool.remove('legacy_worker');
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Process_Resource
|
|
57
|
+
|
|
58
|
+
**Purpose:** Represents a local long-running process as a resource.
|
|
59
|
+
|
|
60
|
+
**Key Features:**
|
|
61
|
+
- State machine lifecycle:
|
|
62
|
+
- `stopped`
|
|
63
|
+
- `starting`
|
|
64
|
+
- `running`
|
|
65
|
+
- `stopping`
|
|
66
|
+
- `restarting`
|
|
67
|
+
- `crashed`
|
|
68
|
+
- Emits:
|
|
69
|
+
- `state_change`
|
|
70
|
+
- `stdout`
|
|
71
|
+
- `stderr`
|
|
72
|
+
- `exit`
|
|
73
|
+
- `health_check`
|
|
74
|
+
- `unhealthy`
|
|
75
|
+
- `crashed`
|
|
76
|
+
- Auto-restart with exponential backoff (when `autoRestart: true`)
|
|
77
|
+
- Optional health checks (`http`, `tcp`, `custom`)
|
|
78
|
+
- Consistent status shape for direct and PM2-backed processes
|
|
79
|
+
|
|
80
|
+
**Process Manager Modes:**
|
|
81
|
+
- `direct` (default): Uses `child_process.spawn()`
|
|
82
|
+
- `pm2`: Uses PM2 CLI commands
|
|
83
|
+
|
|
84
|
+
**PM2 Path Resolution:**
|
|
85
|
+
- If `processManager.pm2Path` is set, it is used
|
|
86
|
+
- Else `PM2_PATH` env var is used (if set)
|
|
87
|
+
- Else local `node_modules/.bin/pm2` is used if present
|
|
88
|
+
- Else `pm2` is resolved from system `PATH`
|
|
89
|
+
|
|
90
|
+
**Usage:**
|
|
91
|
+
```javascript
|
|
92
|
+
const Process_Resource = require('jsgui3-server/resources/process-resource');
|
|
93
|
+
|
|
94
|
+
// Direct mode (default)
|
|
95
|
+
const worker_direct = new Process_Resource({
|
|
96
|
+
name: 'worker_direct',
|
|
97
|
+
command: process.execPath,
|
|
98
|
+
args: ['worker.js'],
|
|
99
|
+
autoRestart: true
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// PM2 mode (pm2Path optional)
|
|
103
|
+
const worker_pm2 = new Process_Resource({
|
|
104
|
+
name: 'worker_pm2',
|
|
105
|
+
processManager: { type: 'pm2' },
|
|
106
|
+
command: process.execPath,
|
|
107
|
+
args: ['worker.js']
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await worker_direct.start();
|
|
111
|
+
console.log(worker_direct.status);
|
|
112
|
+
await worker_direct.stop();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Remote_Process_Resource
|
|
116
|
+
|
|
117
|
+
**Purpose:** Represents a remote process controlled via HTTP API while keeping a resource-compatible interface.
|
|
118
|
+
|
|
119
|
+
**Key Features:**
|
|
120
|
+
- Same high-level lifecycle API as `Process_Resource`:
|
|
121
|
+
- `start()`
|
|
122
|
+
- `stop()`
|
|
123
|
+
- `restart()`
|
|
124
|
+
- `status`
|
|
125
|
+
- `get_abstract()`
|
|
126
|
+
- Periodic polling of remote status endpoint
|
|
127
|
+
- Emits:
|
|
128
|
+
- `state_change`
|
|
129
|
+
- `unreachable`
|
|
130
|
+
- `recovered`
|
|
131
|
+
- Maintains bounded history snapshots for diagnostics
|
|
132
|
+
|
|
133
|
+
**Usage:**
|
|
134
|
+
```javascript
|
|
135
|
+
const Remote_Process_Resource = require('jsgui3-server/resources/remote-process-resource');
|
|
136
|
+
|
|
137
|
+
const remote_worker = new Remote_Process_Resource({
|
|
138
|
+
name: 'remote_worker',
|
|
139
|
+
host: '192.168.1.100',
|
|
140
|
+
port: 3400,
|
|
141
|
+
pollIntervalMs: 30000,
|
|
142
|
+
httpTimeoutMs: 6000,
|
|
143
|
+
endpoints: {
|
|
144
|
+
status: '/',
|
|
145
|
+
start: '/api/start',
|
|
146
|
+
stop: '/api/stop',
|
|
147
|
+
health: '/api/health'
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await remote_worker.start();
|
|
152
|
+
console.log(remote_worker.status);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Website_Resource
|
|
156
|
+
|
|
157
|
+
**Purpose:** Wraps website objects for integration with the server's resource system.
|
|
48
158
|
|
|
49
159
|
**Key Features:**
|
|
50
160
|
- Website object encapsulation
|
|
@@ -110,16 +220,34 @@ class Custom_Resource extends Resource {
|
|
|
110
220
|
}
|
|
111
221
|
```
|
|
112
222
|
|
|
113
|
-
### Lifecycle Management
|
|
114
|
-
|
|
115
|
-
Resources follow a consistent lifecycle:
|
|
223
|
+
### Lifecycle Management
|
|
224
|
+
|
|
225
|
+
Resources follow a consistent lifecycle:
|
|
116
226
|
|
|
117
227
|
1. **Construction**: Resource is instantiated with configuration
|
|
118
228
|
2. **Registration**: Added to resource pool
|
|
119
229
|
3. **Start**: Resource becomes active and available
|
|
120
230
|
4. **Usage**: Resource handles requests and operations
|
|
121
231
|
5. **Stop**: Resource is deactivated
|
|
122
|
-
6. **Cleanup**: Resources are released
|
|
232
|
+
6. **Cleanup**: Resources are released
|
|
233
|
+
|
|
234
|
+
For process resources, consumers can rely on the same lifecycle API regardless of execution mode (`direct`, `pm2`, or `remote`).
|
|
235
|
+
|
|
236
|
+
### Unified Process Status
|
|
237
|
+
|
|
238
|
+
Both `Process_Resource` and `Remote_Process_Resource` expose a compatible `status` shape:
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
{
|
|
242
|
+
state: 'running',
|
|
243
|
+
pid: 12345,
|
|
244
|
+
uptime: 61520,
|
|
245
|
+
restartCount: 1,
|
|
246
|
+
lastHealthCheck: null,
|
|
247
|
+
memoryUsage: { rssBytes: 104857600 },
|
|
248
|
+
processManager: { type: 'direct' } // or 'pm2' / 'remote'
|
|
249
|
+
}
|
|
250
|
+
```
|
|
123
251
|
|
|
124
252
|
### Configuration Patterns
|
|
125
253
|
|
|
@@ -171,18 +299,39 @@ class Database_Resource extends Resource {
|
|
|
171
299
|
}
|
|
172
300
|
```
|
|
173
301
|
|
|
174
|
-
### Integration with Server
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
// Add to resource pool
|
|
178
|
-
server.resource_pool.add(databaseResource);
|
|
302
|
+
### Integration with Server
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
// Add to resource pool
|
|
306
|
+
server.resource_pool.add(databaseResource);
|
|
179
307
|
|
|
180
308
|
// Access from other components
|
|
181
309
|
const db = server.resource_pool.get_resource('Database_Resource');
|
|
182
|
-
db.query('SELECT * FROM users', [], (err, results) => {
|
|
183
|
-
// Handle results
|
|
184
|
-
});
|
|
185
|
-
```
|
|
310
|
+
db.query('SELECT * FROM users', [], (err, results) => {
|
|
311
|
+
// Handle results
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
With `Server.serve()` you can register resources declaratively:
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
const server = await Server.serve({
|
|
319
|
+
resources: {
|
|
320
|
+
worker_direct: {
|
|
321
|
+
type: 'process',
|
|
322
|
+
command: process.execPath,
|
|
323
|
+
args: ['worker.js']
|
|
324
|
+
},
|
|
325
|
+
remote_worker: {
|
|
326
|
+
type: 'remote',
|
|
327
|
+
host: '127.0.0.1',
|
|
328
|
+
port: 3400
|
|
329
|
+
},
|
|
330
|
+
local_cache: new In_Process_Cache_Resource({ name: 'local_cache' })
|
|
331
|
+
},
|
|
332
|
+
events: true
|
|
333
|
+
});
|
|
334
|
+
```
|
|
186
335
|
|
|
187
336
|
## Resource Communication Patterns
|
|
188
337
|
|
|
@@ -612,4 +761,4 @@ class Logged_Resource extends Resource {
|
|
|
612
761
|
|
|
613
762
|
---
|
|
614
763
|
|
|
615
|
-
This guide provides the foundation for understanding and extending the resource system. For specific resource implementations, refer to their individual source files in the `resources/` directory.
|
|
764
|
+
This guide provides the foundation for understanding and extending the resource system. For specific resource implementations, refer to their individual source files in the `resources/` directory.
|