@nlabs/metropolisjs 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# MetropolisJS: Seamless Frontend-Backend Integration Framework
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="docs/assets/metropolisjs-logo.png" alt="MetropolisJS logo" width="432">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-logo.png" alt="MetropolisJS logo" width="432">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
> **The Ultimate Frontend Integration Library for Modern Web Applications**
|
|
@@ -465,28 +465,11 @@ const ChatComponent = () => {
|
|
|
465
465
|
|
|
466
466
|
## Architecture
|
|
467
467
|
|
|
468
|
-
MetropolisJS
|
|
468
|
+
MetropolisJS sits between React and your services. The provider supplies configuration and action access, adapters keep data typed, Rip-Hunter handles transport, and ArkhamJS keeps application state reactive.
|
|
469
469
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
│ │◄──►│ │◄──►│ Backend │
|
|
474
|
-
│ UI Layer │ │ Integration │ │ Services │
|
|
475
|
-
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
476
|
-
│ │
|
|
477
|
-
│ ▼
|
|
478
|
-
│ ┌─────────────────┐
|
|
479
|
-
│ │ ArkhamJS │
|
|
480
|
-
│ │ Data Store │
|
|
481
|
-
│ │ │
|
|
482
|
-
│ └─────────────────┘
|
|
483
|
-
│
|
|
484
|
-
▼
|
|
485
|
-
┌─────────────────┐
|
|
486
|
-
│ React Context │
|
|
487
|
-
│ (Config/Flux) │
|
|
488
|
-
└─────────────────┘
|
|
489
|
-
```
|
|
470
|
+
<p align="center">
|
|
471
|
+
<img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-architecture.png" alt="MetropolisJS architecture showing React components, the provider and hooks, actions, adapters, ArkhamJS state, Rip-Hunter transport, Reaktor services, and real-time updates" width="960">
|
|
472
|
+
</p>
|
|
490
473
|
|
|
491
474
|
### Core Components
|
|
492
475
|
|
|
@@ -497,6 +480,14 @@ MetropolisJS is built on a powerful three-layer architecture with modern React p
|
|
|
497
480
|
- **Configuration**: Context-based configuration (React best practices)
|
|
498
481
|
- **Hooks**: Specialized hooks for accessing actions and configuration
|
|
499
482
|
|
|
483
|
+
### Request and State Lifecycle
|
|
484
|
+
|
|
485
|
+
Actions hide the full request lifecycle behind a typed method. They validate inputs, use the request cache when configured, communicate through Rip-Hunter, update the ArkhamJS store, and then dispatch Flux events to subscribers. WebSocket and SSE messages enter the same reactive state flow.
|
|
486
|
+
|
|
487
|
+
<p align="center">
|
|
488
|
+
<img src="https://raw.githubusercontent.com/nitrogenlabs/metropolisjs/main/docs/assets/metropolisjs-request-lifecycle.svg" alt="Sequence diagram showing a React component calling a MetropolisJS action, input validation and caching, Rip-Hunter transport, a Reaktor response, and an ArkhamJS state update followed by a Flux event" width="960">
|
|
489
|
+
</p>
|
|
490
|
+
|
|
500
491
|
### Modern Architecture Features
|
|
501
492
|
|
|
502
493
|
MetropolisJS follows React best practices:
|
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="760" viewBox="0 0 1440 760" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">How MetropolisJS connects a React application to state and backend services</title>
|
|
3
|
+
<desc id="desc">React components, hooks, configuration, and live experiences connect to MetropolisJS. MetropolisJS provides configuration, typed actions, adapters, caching, and real-time coordination, then integrates with ArkhamJS, Rip-Hunter, Reaktor, WebSocket, and server-sent event services.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<style>
|
|
6
|
+
.canvas{fill:#fff}.heading{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:21px;font-weight:700}.brand{fill:#2563eb;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:32px;font-weight:700}.label{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:20px;font-weight:600}.detail{fill:#64748b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:16px;font-weight:400}.feature{fill:#1f2937;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:18px;font-weight:500}.ink{fill:none;stroke:#374151;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}.blue{fill:none;stroke:#2563eb;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal{fill:none;stroke:#14b8a6;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal-fill{fill:#14b8a6}
|
|
7
|
+
</style>
|
|
8
|
+
<symbol id="window" viewBox="0 0 64 64"><rect class="ink" x="7" y="9" width="50" height="46" rx="5"/><path class="ink" d="M7 20h50M14 15h2m6 0h2M15 28h15v15H15zm22 0h12m-12 8h12m-12 8h8"/></symbol>
|
|
9
|
+
<symbol id="hook" viewBox="0 0 64 64"><path class="ink" d="M19 12v18c0 9 5 15 13 15s13-6 13-15V12M14 12h10m16 0h10M32 45v8"/><circle class="ink" cx="32" cy="55" r="3"/></symbol>
|
|
10
|
+
<symbol id="sliders" viewBox="0 0 64 64"><path class="ink" d="M11 17h42M11 32h42M11 47h42"/><circle class="canvas" cx="25" cy="17" r="6"/><circle class="ink" cx="25" cy="17" r="6"/><circle class="canvas" cx="42" cy="32" r="6"/><circle class="ink" cx="42" cy="32" r="6"/><circle class="canvas" cx="20" cy="47" r="6"/><circle class="ink" cx="20" cy="47" r="6"/></symbol>
|
|
11
|
+
<symbol id="pulse" viewBox="0 0 64 64"><path class="ink" d="M7 35h11l6-15 11 29 7-18 5 4h10"/></symbol>
|
|
12
|
+
<symbol id="database" viewBox="0 0 64 64"><ellipse class="ink" cx="32" cy="14" rx="20" ry="8"/><path class="ink" d="M12 14v34c0 4 9 8 20 8s20-4 20-8V14M12 31c0 4 9 8 20 8s20-4 20-8M12 47c0 4 9 8 20 8s20-4 20-8"/></symbol>
|
|
13
|
+
<symbol id="network" viewBox="0 0 64 64"><circle class="ink" cx="32" cy="13" r="6"/><circle class="ink" cx="13" cy="48" r="6"/><circle class="ink" cx="51" cy="48" r="6"/><path class="ink" d="M29 18 16 43m19-25 13 25M19 48h26"/></symbol>
|
|
14
|
+
<symbol id="server" viewBox="0 0 64 64"><rect class="ink" x="10" y="10" width="44" height="18" rx="4"/><rect class="ink" x="10" y="36" width="44" height="18" rx="4"/><circle class="ink" cx="18" cy="19" r="2"/><circle class="ink" cx="18" cy="45" r="2"/><path class="ink" d="M26 19h20M26 45h20"/></symbol>
|
|
15
|
+
<symbol id="broadcast" viewBox="0 0 64 64"><circle class="ink" cx="32" cy="32" r="4"/><path class="ink" d="M24 24a11 11 0 0 0 0 16m16-16a11 11 0 0 1 0 16M17 17a21 21 0 0 0 0 30m30-30a21 21 0 0 1 0 30"/></symbol>
|
|
16
|
+
</defs>
|
|
17
|
+
|
|
18
|
+
<rect class="canvas" width="1440" height="760"/>
|
|
19
|
+
<text class="heading" x="72" y="68">Your React application</text>
|
|
20
|
+
<text class="heading" x="525" y="68">MetropolisJS integration layer</text>
|
|
21
|
+
<text class="heading" x="1045" y="68">State + services</text>
|
|
22
|
+
|
|
23
|
+
<use href="#window" x="70" y="125" width="64" height="64"/>
|
|
24
|
+
<text class="label" x="160" y="153">React components</text><text class="detail" x="160" y="178">UI, routes, and views</text>
|
|
25
|
+
<use href="#hook" x="70" y="260" width="64" height="64"/>
|
|
26
|
+
<text class="label" x="160" y="288">Specialized hooks</text><text class="detail" x="160" y="313">Typed action access</text>
|
|
27
|
+
<use href="#sliders" x="70" y="395" width="64" height="64"/>
|
|
28
|
+
<text class="label" x="160" y="423">App configuration</text><text class="detail" x="160" y="448">Endpoints, auth, adapters</text>
|
|
29
|
+
<use href="#pulse" x="70" y="530" width="64" height="64"/>
|
|
30
|
+
<text class="label" x="160" y="558">Live experiences</text><text class="detail" x="160" y="583">Messages and notifications</text>
|
|
31
|
+
|
|
32
|
+
<path class="blue" d="M375 157h20q20 0 20 20v365q0 20-20 20h-20M375 292h40M375 427h40"/>
|
|
33
|
+
<path class="blue" d="M415 480h85m-13-13 13 13-13 13"/>
|
|
34
|
+
|
|
35
|
+
<circle cx="660" cy="174" r="82" fill="none" stroke="#2563eb" stroke-width="3"/>
|
|
36
|
+
<path fill="#2563eb" d="m630 169 30 18 30-18v38l-30 19-30-19z"/>
|
|
37
|
+
<path fill="#0f172a" d="m610 128 50 30 50-30v74l-20 12v-44l-30 18-30-18v44l-20-12z"/>
|
|
38
|
+
<text class="brand" x="660" y="300" text-anchor="middle">MetropolisJS</text>
|
|
39
|
+
<text class="detail" x="660" y="330" text-anchor="middle">Frontend API integration and reactive data</text>
|
|
40
|
+
|
|
41
|
+
<circle class="teal" cx="533" cy="360" r="12"/><path class="teal" d="m527 360 4 4 8-9"/><text class="feature" x="563" y="367">Provider + configuration context</text>
|
|
42
|
+
<circle class="teal" cx="533" cy="420" r="12"/><path class="teal" d="M527 420h12m-6-6v12"/><text class="feature" x="563" y="427">Typed actions + domain hooks</text>
|
|
43
|
+
<circle class="teal" cx="533" cy="480" r="12"/><path class="teal" d="m527 480 4 4 8-9"/><text class="feature" x="563" y="487">Adapters + validation</text>
|
|
44
|
+
<circle class="teal" cx="533" cy="540" r="12"/><path class="teal" d="M527 540h12"/><text class="feature" x="563" y="547">Request cache + Flux events</text>
|
|
45
|
+
<circle class="teal" cx="533" cy="600" r="12"/><path class="teal" d="M527 600h12m-6-6v12"/><text class="feature" x="563" y="607">Real-time coordination</text>
|
|
46
|
+
|
|
47
|
+
<path class="blue" d="M805 480h140m-13-13 13 13-13 13"/>
|
|
48
|
+
<path class="teal" d="M1011 157h-22q-20 0-20 20v365q0 20 20 20h22M969 292h42M969 427h42"/>
|
|
49
|
+
|
|
50
|
+
<circle class="teal-fill" cx="1011" cy="157" r="6"/><use href="#database" x="1040" y="125" width="64" height="64"/>
|
|
51
|
+
<text class="label" x="1130" y="153">ArkhamJS</text><text class="detail" x="1130" y="178">Reactive Flux state</text>
|
|
52
|
+
<circle class="teal-fill" cx="1011" cy="292" r="6"/><use href="#network" x="1040" y="260" width="64" height="64"/>
|
|
53
|
+
<text class="label" x="1130" y="288">Rip-Hunter</text><text class="detail" x="1130" y="313">GraphQL + REST transport</text>
|
|
54
|
+
<circle class="teal-fill" cx="1011" cy="427" r="6"/><use href="#server" x="1040" y="395" width="64" height="64"/>
|
|
55
|
+
<text class="label" x="1130" y="423">Reaktor</text><text class="detail" x="1130" y="448">Backend actions + APIs</text>
|
|
56
|
+
<circle class="teal-fill" cx="1011" cy="562" r="6"/><use href="#broadcast" x="1040" y="530" width="64" height="64"/>
|
|
57
|
+
<text class="label" x="1130" y="558">WebSocket + SSE</text><text class="detail" x="1130" y="583">Real-time event streams</text>
|
|
58
|
+
</svg>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="520" viewBox="0 0 1440 520" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">MetropolisJS request and state lifecycle</title>
|
|
3
|
+
<desc id="desc">A React component invokes a typed action, an adapter validates the input, the request cache or Rip-Hunter resolves the data, Reaktor responds, ArkhamJS updates state, and a Flux event renders the latest state.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<style>
|
|
6
|
+
.canvas{fill:#fff}.heading{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:22px;font-weight:700}.step{fill:#111827;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:17px;font-weight:650}.detail{fill:#64748b;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:14px;font-weight:400}.number{fill:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;font-size:13px;font-weight:700}.ink{fill:none;stroke:#374151;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}.blue{fill:none;stroke:#2563eb;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.teal{fill:none;stroke:#14b8a6;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round}.blue-fill{fill:#2563eb}.teal-fill{fill:#14b8a6}
|
|
7
|
+
</style>
|
|
8
|
+
<symbol id="cursor" viewBox="0 0 64 64"><path class="ink" d="m16 9 33 31-17 1-9 14z"/><path class="ink" d="m34 41 11 13"/></symbol>
|
|
9
|
+
<symbol id="check" viewBox="0 0 64 64"><path class="ink" d="M18 8h28v48H18zM25 8V4h14v4M25 25l5 5 10-12M25 40h14"/></symbol>
|
|
10
|
+
<symbol id="route" viewBox="0 0 64 64"><circle class="ink" cx="14" cy="17" r="6"/><circle class="ink" cx="50" cy="47" r="6"/><path class="ink" d="M20 17h12q10 0 10 10v10q0 10 8 10M32 17l-6-6m6 6-6 6"/></symbol>
|
|
11
|
+
<symbol id="server" viewBox="0 0 64 64"><rect class="ink" x="10" y="10" width="44" height="18" rx="4"/><rect class="ink" x="10" y="36" width="44" height="18" rx="4"/><circle class="ink" cx="18" cy="19" r="2"/><circle class="ink" cx="18" cy="45" r="2"/><path class="ink" d="M26 19h20M26 45h20"/></symbol>
|
|
12
|
+
<symbol id="database" viewBox="0 0 64 64"><ellipse class="ink" cx="32" cy="14" rx="20" ry="8"/><path class="ink" d="M12 14v34c0 4 9 8 20 8s20-4 20-8V14M12 31c0 4 9 8 20 8s20-4 20-8M12 47c0 4 9 8 20 8s20-4 20-8"/></symbol>
|
|
13
|
+
<symbol id="refresh" viewBox="0 0 64 64"><path class="ink" d="M49 24A19 19 0 0 0 17 17l-5 6m3-13 2 7 7-2M15 40a19 19 0 0 0 32 7l5-6m-3 13-2-7-7 2"/></symbol>
|
|
14
|
+
</defs>
|
|
15
|
+
|
|
16
|
+
<rect class="canvas" width="1440" height="520"/>
|
|
17
|
+
<text class="heading" x="70" y="62">From UI intent to reactive state</text>
|
|
18
|
+
<path class="blue" d="M150 229h505m-14-12 14 12-14 12"/>
|
|
19
|
+
<path class="teal" d="M655 229h635m-14-12 14 12-14 12"/>
|
|
20
|
+
|
|
21
|
+
<g transform="translate(105 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#cursor" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">1</text></g>
|
|
22
|
+
<text class="step" x="150" y="304" text-anchor="middle">Invoke an action</text><text class="detail" x="150" y="329" text-anchor="middle">Component + typed hook</text>
|
|
23
|
+
|
|
24
|
+
<g transform="translate(330 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#check" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">2</text></g>
|
|
25
|
+
<text class="step" x="375" y="304" text-anchor="middle">Validate input</text><text class="detail" x="375" y="329" text-anchor="middle">Adapter produces typed data</text>
|
|
26
|
+
|
|
27
|
+
<g transform="translate(555 160)"><circle class="blue-fill" cx="45" cy="69" r="8"/><use href="#route" x="13" y="-3" width="64" height="64"/><circle class="blue-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">3</text></g>
|
|
28
|
+
<text class="step" x="600" y="304" text-anchor="middle">Resolve request</text><text class="detail" x="600" y="329" text-anchor="middle">Cache hit or Rip-Hunter</text>
|
|
29
|
+
|
|
30
|
+
<g transform="translate(780 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#server" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">4</text></g>
|
|
31
|
+
<text class="step" x="825" y="304" text-anchor="middle">Receive response</text><text class="detail" x="825" y="329" text-anchor="middle">Reaktor returns result</text>
|
|
32
|
+
|
|
33
|
+
<g transform="translate(1005 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#database" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">5</text></g>
|
|
34
|
+
<text class="step" x="1050" y="304" text-anchor="middle">Update state</text><text class="detail" x="1050" y="329" text-anchor="middle">ArkhamJS stores the result</text>
|
|
35
|
+
|
|
36
|
+
<g transform="translate(1230 160)"><circle class="teal-fill" cx="45" cy="69" r="8"/><use href="#refresh" x="13" y="-3" width="64" height="64"/><circle class="teal-fill" cx="16" cy="-3" r="13"/><text class="number" x="16" y="2" text-anchor="middle">6</text></g>
|
|
37
|
+
<text class="step" x="1275" y="304" text-anchor="middle">Render latest data</text><text class="detail" x="1275" y="329" text-anchor="middle">Flux event notifies listeners</text>
|
|
38
|
+
|
|
39
|
+
<path class="teal" d="M1275 365v35q0 24-24 24H174q-24 0-24-24v-35"/>
|
|
40
|
+
<path class="teal" d="m138 379 12-14 12 14"/>
|
|
41
|
+
<text class="detail" x="712" y="456" text-anchor="middle">WebSocket and SSE events join the same state-update loop</text>
|
|
42
|
+
</svg>
|