@oasisomniverse/web8-api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @oasisomniverse/web8-api
2
+
3
+ Isomorphic (Node 18+ and browser) JavaScript/TypeScript-friendly client for the
4
+ **WEB8 OASIS Galactic Mesh API** - fractal holonic mesh node registration,
5
+ link/route computation, self-healing message relay, and protocol-bridge
6
+ translation between external wire formats and the unified `MeshMessage`
7
+ envelope, built on the OASIS2 WEB8 WebAPI.
8
+
9
+ Zero dependencies. Wraps the global `fetch`. Works the same in Node and the
10
+ browser.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @oasisomniverse/web8-api
16
+ ```
17
+
18
+ ## Quick start
19
+
20
+ ```js
21
+ const { Web8Client } = require('@oasisomniverse/web8-api');
22
+ // or: import { Web8Client } from '@oasisomniverse/web8-api';
23
+
24
+ const web8 = new Web8Client({ baseUrl: 'https://api.web8.oasisomniverse.one' });
25
+
26
+ const { isError, message, result } = await web8.mesh.registerNode({ name: 'edge-node-1' });
27
+ if (isError) throw new Error(message);
28
+ console.log(result);
29
+ ```
30
+
31
+ ## Calling any endpoint
32
+
33
+ Every controller on the OASIS2 WEB8 WebAPI is reachable as a lowerCamel
34
+ property on the client (`web8.mesh`, `web8.protocolBridge`). Every generated
35
+ method takes a single args object:
36
+
37
+ - Any key matching a `{token}` in the route template is consumed and
38
+ substituted into the URL (case-insensitive match).
39
+ - Any remaining keys become the query string (GET/DELETE) or JSON body
40
+ (POST/PUT) - **matching the real `[FromQuery]`/`[FromBody]` binding of the
41
+ underlying C# action**, not just the HTTP verb. `endpoints.json` records
42
+ exactly which arg names are query-bound (and which single arg is the whole
43
+ body, for actions like `TranslateInbound` whose entire body is one
44
+ primitive `[FromBody] string`) per operation - see [`docs/`](./docs/README.md)
45
+ for the per-method breakdown.
46
+
47
+ ```js
48
+ // POST v1/mesh/nodes/{nodeId}/heartbeat -> nodeId consumed as a route token
49
+ await web8.mesh.heartbeat({ nodeId });
50
+
51
+ // GET v1/mesh/route -> all args become query params
52
+ const route = await web8.mesh.computeRoute({ sourceNodeId, destinationNodeId });
53
+
54
+ // POST v1/mesh/links -> nodeAId/nodeBId/latencyMs are all [FromQuery] even
55
+ // though this is a POST, so they're sent on the URL - no JSON body is sent
56
+ await web8.mesh.addLink({ nodeAId, nodeBId, latencyMs: 12 });
57
+
58
+ // POST v1/protocol-bridge/translate-inbound -> format/sourceNodeId/destinationNodeId
59
+ // are [FromQuery]; rawPayload is the single [FromBody] string, so it becomes
60
+ // the raw JSON body itself (not wrapped in an object)
61
+ const inbound = await web8.protocolBridge.translateInbound({
62
+ format: 'Mqtt', sourceNodeId, destinationNodeId, rawPayload: '{"temp":21.5}'
63
+ });
64
+ ```
65
+
66
+ Every response has the shape:
67
+
68
+ ```ts
69
+ interface OASISResponse<T = any> {
70
+ isError: boolean;
71
+ message: string | null;
72
+ result: T;
73
+ raw: any;
74
+ statusCode: number;
75
+ }
76
+ ```
77
+
78
+ ## Auth
79
+
80
+ WEB8 is an internal galactic-mesh/protocol-bridge layer that sits behind the
81
+ same OASIS avatar identity as WEB4/WEB5/WEB6/WEB7 - it has no avatar/login
82
+ endpoints of its own. Reuse a JWT you've already obtained elsewhere (e.g.
83
+ from `web4-oasis-api`'s `client.auth.login()`):
84
+
85
+ ```js
86
+ web8.setToken(jwtToken);
87
+ ```
88
+
89
+ ## Module examples
90
+
91
+ ### Mesh (`web8.mesh`)
92
+
93
+ ```js
94
+ const nodeA = await web8.mesh.registerNode({ name: 'edge-node-a' });
95
+ const nodeB = await web8.mesh.registerNode({ name: 'edge-node-b' });
96
+ await web8.mesh.addLink({ nodeAId: nodeA.result.id, nodeBId: nodeB.result.id, latencyMs: 12 });
97
+ await web8.mesh.heartbeat({ nodeId: nodeA.result.id });
98
+
99
+ const route = await web8.mesh.computeRoute({ sourceNodeId: nodeA.result.id, destinationNodeId: nodeB.result.id });
100
+ const relay = await web8.mesh.sendMessage({ sourceNodeId: nodeA.result.id, destinationNodeId: nodeB.result.id, payload: 'hello' });
101
+ ```
102
+
103
+ ### Protocol Bridge (`web8.protocolBridge`)
104
+
105
+ ```js
106
+ const inbound = await web8.protocolBridge.translateInbound({
107
+ format: 'Mqtt',
108
+ sourceNodeId,
109
+ destinationNodeId,
110
+ rawPayload: '{"temp":21.5}'
111
+ });
112
+
113
+ // targetFormat is [FromQuery]; the rest of the args become the MeshMessage
114
+ // JSON body directly (not nested under a "message" key)
115
+ const outbound = await web8.protocolBridge.translateOutbound({ targetFormat: 'Json', ...inbound.result });
116
+ ```
117
+
118
+ ## Module reference
119
+
120
+ 2 modules, 8 operations in total. Full per-method tables live in
121
+ [`docs/`](./docs/README.md).
122
+
123
+ | Client property | Route prefix | Operations |
124
+ | --- | --- | --- |
125
+ | `web8.mesh` | `v1/mesh` | 6 |
126
+ | `web8.protocolBridge` | `v1/protocol-bridge` | 2 |
127
+
128
+ See [`docs/README.md`](./docs/README.md) for the full generated reference,
129
+ or [`docs/modules/`](./docs/modules) for per-module method tables with
130
+ parameter and route details.
131
+
132
+ ## Regenerating
133
+
134
+ The generated modules, type declarations and docs are produced from
135
+ `endpoints.json` (extracted from the WEB8 WebAPI controller source):
136
+
137
+ ```bash
138
+ npm run generate # src/modules/*.js + src/modules/index.js
139
+ npm run types # src/modules/*.d.ts + index.d.ts + src/core/types.d.ts
140
+ npm run docs # docs/README.md + docs/modules/*.md
141
+ ```
142
+
143
+ ## Testing
144
+
145
+ ```bash
146
+ npm test
147
+ ```
148
+
149
+ ## License
150
+
151
+ MIT
package/docs/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # WEB8 Galactic Mesh & Protocol Bridge API — JavaScript SDK Reference
2
+
3
+ Generated from `endpoints.json` (extracted from the WebAPI controllers) by
4
+ `scripts/generate-full-docs.js`. Regenerate the full pipeline after the API
5
+ changes:
6
+
7
+ ```
8
+ node scripts/extract-endpoints.js
9
+ node scripts/generate-modules.js
10
+ node scripts/generate-full-docs.js
11
+ ```
12
+
13
+ - [Module Reference](#module-reference) (2 modules, 8 operations)
14
+
15
+ ## Module Reference
16
+
17
+ | Client property | Route prefix | Operations |
18
+ | --- | --- | --- |
19
+ | [`web8.mesh`](modules/Mesh.md) | `v1/mesh` | 6 |
20
+ | [`web8.protocolBridge`](modules/ProtocolBridge.md) | `v1/protocol-bridge` | 2 |
@@ -0,0 +1,312 @@
1
+ # Mesh — `web8.mesh`
2
+
3
+ Source controller: [`MeshController.cs`](https://github.com/NextGenSoftwareUK/OASIS2/blob/main/WEB8/NextGenSoftware.OASIS.Web8.WebAPI/Controllers/MeshController.cs)
4
+ Route prefix: `v1/mesh`
5
+ 6 operation(s).
6
+
7
+ Every method takes a single args object: any key matching a `{token}` in the route is substituted into the URL; everything else becomes the query string (GET/DELETE) or JSON body (POST/PUT). Every call resolves to the standard OASIS envelope:
8
+
9
+ ```ts
10
+ {
11
+ isError: boolean;
12
+ isWarning: boolean;
13
+ message: string;
14
+ errorCode?: string;
15
+ result: T; // see each endpoint's Response section below
16
+ }
17
+ ```
18
+
19
+ ## Operations
20
+
21
+ ### `addLink`
22
+
23
+ **POST** `v1/mesh/links`
24
+
25
+ **Request**
26
+
27
+ Body fields:
28
+
29
+ | Field | Type |
30
+ | --- | --- |
31
+ | `nodeAId` | `Guid` |
32
+ | `nodeBId` | `Guid` |
33
+ | `latencyMs` | `double (optional)` |
34
+
35
+ **Response**
36
+
37
+ Standard `OASISResult` envelope (see top of this page) with:
38
+
39
+ `result` type: `bool`
40
+
41
+ **Example**
42
+
43
+ ```js
44
+ const { isError, message, result } = await web8.mesh.addLink({
45
+ nodeAId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
46
+ nodeBId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
47
+ latencyMs: 1.0
48
+ });
49
+ if (isError) throw new Error(message);
50
+ console.log(result);
51
+ ```
52
+
53
+ Example response:
54
+
55
+ ```json
56
+ {
57
+ "isError": false,
58
+ "message": "",
59
+ "result": true
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ### `computeRoute`
66
+
67
+ **GET** `v1/mesh/route`
68
+
69
+ **Request**
70
+
71
+ Query parameters:
72
+
73
+ | Field | Type |
74
+ | --- | --- |
75
+ | `sourceNodeId` | `Guid` |
76
+ | `destinationNodeId` | `Guid` |
77
+
78
+ **Response**
79
+
80
+ Standard `OASISResult` envelope (see top of this page) with:
81
+
82
+ `result` type: `Guid` (array)
83
+
84
+ **Example**
85
+
86
+ ```js
87
+ const { isError, message, result } = await web8.mesh.computeRoute({
88
+ sourceNodeId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
89
+ destinationNodeId: '3fa85f64-5717-4562-b3fc-2c963f66afa6'
90
+ });
91
+ if (isError) throw new Error(message);
92
+ console.log(result);
93
+ ```
94
+
95
+ Example response:
96
+
97
+ ```json
98
+ {
99
+ "isError": false,
100
+ "message": "",
101
+ "result": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"]
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ### `getNodes`
108
+
109
+ **GET** `v1/mesh/nodes`
110
+
111
+ **Request**
112
+
113
+ No request body.
114
+
115
+ **Response**
116
+
117
+ Standard `OASISResult` envelope (see top of this page) with:
118
+
119
+ `result` type: `GalacticNode` (array)
120
+
121
+ | Field | Type |
122
+ | --- | --- |
123
+ | `Id` | `Guid` |
124
+ | `Name` | `string` |
125
+ | `Type` | `NodeType` |
126
+ | `EndpointUrl` | `string` |
127
+ | `IsSovereign` | `bool` |
128
+ | `RegisteredUtc` | `DateTime` |
129
+ | `LastSeenUtc` | `DateTime` |
130
+
131
+ **Example**
132
+
133
+ ```js
134
+ const { isError, message, result } = await web8.mesh.getNodes({});
135
+ if (isError) throw new Error(message);
136
+ console.log(result);
137
+ ```
138
+
139
+ Example response:
140
+
141
+ ```json
142
+ {
143
+ "isError": false,
144
+ "message": "",
145
+ "result": [{ "Id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Name": "example string", "Type": { }, "EndpointUrl": "example string", "IsSovereign": true, "RegisteredUtc": "2026-01-01T00:00:00Z", "LastSeenUtc": "2026-01-01T00:00:00Z" }]
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ### `heartbeat`
152
+
153
+ **POST** `v1/mesh/nodes/{nodeId}/heartbeat`
154
+
155
+ Route parameters:
156
+
157
+ | Field | Type |
158
+ | --- | --- |
159
+ | `nodeId` | `Guid` |
160
+
161
+ **Request**
162
+
163
+ No request body.
164
+
165
+ **Response**
166
+
167
+ Standard `OASISResult` envelope (see top of this page) with:
168
+
169
+ `result` type: `bool`
170
+
171
+ **Example**
172
+
173
+ ```js
174
+ const { isError, message, result } = await web8.mesh.heartbeat({
175
+ nodeId: '<nodeId>'
176
+ });
177
+ if (isError) throw new Error(message);
178
+ console.log(result);
179
+ ```
180
+
181
+ Example response:
182
+
183
+ ```json
184
+ {
185
+ "isError": false,
186
+ "message": "",
187
+ "result": true
188
+ }
189
+ ```
190
+
191
+ ---
192
+
193
+ ### `registerNode`
194
+
195
+ The WEB8 fractal holonic mesh - register nodes, declare links, compute routes and relay messages with self-healing failover.
196
+
197
+ **POST** `v1/mesh/nodes`
198
+
199
+ **Request**
200
+
201
+ Body type: `GalacticNode`
202
+
203
+ | Field | Type |
204
+ | --- | --- |
205
+ | `Id` | `Guid` |
206
+ | `Name` | `string` |
207
+ | `Type` | `NodeType` |
208
+ | `EndpointUrl` | `string` |
209
+ | `IsSovereign` | `bool` |
210
+ | `RegisteredUtc` | `DateTime` |
211
+ | `LastSeenUtc` | `DateTime` |
212
+
213
+ **Response**
214
+
215
+ Standard `OASISResult` envelope (see top of this page) with:
216
+
217
+ `result` type: `GalacticNode`
218
+
219
+ | Field | Type |
220
+ | --- | --- |
221
+ | `Id` | `Guid` |
222
+ | `Name` | `string` |
223
+ | `Type` | `NodeType` |
224
+ | `EndpointUrl` | `string` |
225
+ | `IsSovereign` | `bool` |
226
+ | `RegisteredUtc` | `DateTime` |
227
+ | `LastSeenUtc` | `DateTime` |
228
+
229
+ **Example**
230
+
231
+ ```js
232
+ const { isError, message, result } = await web8.mesh.registerNode({
233
+ id: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
234
+ name: "example string",
235
+ type: { },
236
+ endpointUrl: "example string",
237
+ isSovereign: true,
238
+ registeredUtc: "2026-01-01T00:00:00Z",
239
+ lastSeenUtc: "2026-01-01T00:00:00Z"
240
+ });
241
+ if (isError) throw new Error(message);
242
+ console.log(result);
243
+ ```
244
+
245
+ Example response:
246
+
247
+ ```json
248
+ {
249
+ "isError": false,
250
+ "message": "",
251
+ "result": { "Id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Name": "example string", "Type": { }, "EndpointUrl": "example string", "IsSovereign": true, "RegisteredUtc": "2026-01-01T00:00:00Z", "LastSeenUtc": "2026-01-01T00:00:00Z" }
252
+ }
253
+ ```
254
+
255
+ ---
256
+
257
+ ### `sendMessage`
258
+
259
+ Routes and relays a message hop-by-hop to its destination, self-healing around any failed/stale node.
260
+
261
+ **POST** `v1/mesh/send`
262
+
263
+ **Request**
264
+
265
+ Body type: `MeshMessage`
266
+
267
+ | Field | Type |
268
+ | --- | --- |
269
+ | `Id` | `Guid` |
270
+ | `SourceNodeId` | `Guid` |
271
+ | `DestinationNodeId` | `Guid` |
272
+ | `Payload` | `string` |
273
+ | `Ttl` | `int` |
274
+
275
+ **Response**
276
+
277
+ Standard `OASISResult` envelope (see top of this page) with:
278
+
279
+ `result` type: `MeshRouteResult`
280
+
281
+ | Field | Type |
282
+ | --- | --- |
283
+ | `MessageId` | `Guid` |
284
+ | `Path` | `List<Guid>` |
285
+ | `TotalLatencyMs` | `double` |
286
+ | `Delivered` | `bool` |
287
+ | `RelayLog` | `List<string>` |
288
+
289
+ **Example**
290
+
291
+ ```js
292
+ const { isError, message, result } = await web8.mesh.sendMessage({
293
+ id: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
294
+ sourceNodeId: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
295
+ destinationNodeId: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
296
+ payload: "example string",
297
+ ttl: 1
298
+ });
299
+ if (isError) throw new Error(message);
300
+ console.log(result);
301
+ ```
302
+
303
+ Example response:
304
+
305
+ ```json
306
+ {
307
+ "isError": false,
308
+ "message": "",
309
+ "result": { "MessageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Path": ["3fa85f64-5717-4562-b3fc-2c963f66afa6"], "TotalLatencyMs": 1.0, "Delivered": true, "RelayLog": ["example string"] }
310
+ }
311
+ ```
312
+
@@ -0,0 +1,123 @@
1
+ # ProtocolBridge — `web8.protocolBridge`
2
+
3
+ Source controller: [`ProtocolBridgeController.cs`](https://github.com/NextGenSoftwareUK/OASIS2/blob/main/WEB8/NextGenSoftware.OASIS.Web8.WebAPI/Controllers/ProtocolBridgeController.cs)
4
+ Route prefix: `v1/protocol-bridge`
5
+ 2 operation(s).
6
+
7
+ Every method takes a single args object: any key matching a `{token}` in the route is substituted into the URL; everything else becomes the query string (GET/DELETE) or JSON body (POST/PUT). Every call resolves to the standard OASIS envelope:
8
+
9
+ ```ts
10
+ {
11
+ isError: boolean;
12
+ isWarning: boolean;
13
+ message: string;
14
+ errorCode?: string;
15
+ result: T; // see each endpoint's Response section below
16
+ }
17
+ ```
18
+
19
+ ## Operations
20
+
21
+ ### `translateInbound`
22
+
23
+ Translates any external system's wire format into the unified WEB8 MeshMessage envelope and back.
24
+
25
+ **POST** `v1/protocol-bridge/translate-inbound`
26
+
27
+ **Request**
28
+
29
+ Body fields:
30
+
31
+ | Field | Type |
32
+ | --- | --- |
33
+ | `format` | `BridgeFormat` |
34
+ | `sourceNodeId` | `Guid` |
35
+ | `destinationNodeId` | `Guid` |
36
+ | `rawPayload` | `string` |
37
+
38
+ **Response**
39
+
40
+ Standard `OASISResult` envelope (see top of this page) with:
41
+
42
+ `result` type: `MeshMessage`
43
+
44
+ | Field | Type |
45
+ | --- | --- |
46
+ | `Id` | `Guid` |
47
+ | `SourceNodeId` | `Guid` |
48
+ | `DestinationNodeId` | `Guid` |
49
+ | `Payload` | `string` |
50
+ | `Ttl` | `int` |
51
+
52
+ **Example**
53
+
54
+ ```js
55
+ const { isError, message, result } = await web8.protocolBridge.translateInbound({
56
+ format: '<format>',
57
+ sourceNodeId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
58
+ destinationNodeId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
59
+ rawPayload: 'example string'
60
+ });
61
+ if (isError) throw new Error(message);
62
+ console.log(result);
63
+ ```
64
+
65
+ Example response:
66
+
67
+ ```json
68
+ {
69
+ "isError": false,
70
+ "message": "",
71
+ "result": { "Id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "SourceNodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "DestinationNodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "Payload": "example string", "Ttl": 1 }
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ### `translateOutbound`
78
+
79
+ **POST** `v1/protocol-bridge/translate-outbound`
80
+
81
+ **Request**
82
+
83
+ Body type: `MeshMessage`
84
+
85
+ | Field | Type |
86
+ | --- | --- |
87
+ | `Id` | `Guid` |
88
+ | `SourceNodeId` | `Guid` |
89
+ | `DestinationNodeId` | `Guid` |
90
+ | `Payload` | `string` |
91
+ | `Ttl` | `int` |
92
+
93
+ **Response**
94
+
95
+ Standard `OASISResult` envelope (see top of this page) with:
96
+
97
+ `result` type: `string`
98
+
99
+ **Example**
100
+
101
+ ```js
102
+ const { isError, message, result } = await web8.protocolBridge.translateOutbound({
103
+ targetFormat: '<targetFormat>',
104
+ id: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
105
+ sourceNodeId: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
106
+ destinationNodeId: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
107
+ payload: "example string",
108
+ ttl: 1
109
+ });
110
+ if (isError) throw new Error(message);
111
+ console.log(result);
112
+ ```
113
+
114
+ Example response:
115
+
116
+ ```json
117
+ {
118
+ "isError": false,
119
+ "message": "",
120
+ "result": "example string"
121
+ }
122
+ ```
123
+
package/index.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ // AUTO-GENERATED by scripts/generate-types.js - do not hand-edit.
2
+ import type { OASISResponse, OASISSession } from './src/core/types';
3
+ import type { MeshModule } from './src/modules/Mesh';
4
+ import type { ProtocolBridgeModule } from './src/modules/ProtocolBridge';
5
+
6
+ export type { OASISResponse, OASISSession };
7
+
8
+ export interface Web8ClientOptions {
9
+ baseUrl?: string;
10
+ persistSession?: boolean;
11
+ fetchImpl?: typeof fetch;
12
+ }
13
+
14
+ export declare class HttpClient {
15
+ constructor(options?: { baseUrl?: string; tokenStore?: unknown; fetchImpl?: typeof fetch });
16
+ setBaseUrl(baseUrl: string): void;
17
+ request(verb: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, options?: Record<string, any>): Promise<OASISResponse>;
18
+ get(path: string, options?: Record<string, any>): Promise<OASISResponse>;
19
+ post(path: string, options?: Record<string, any>): Promise<OASISResponse>;
20
+ put(path: string, options?: Record<string, any>): Promise<OASISResponse>;
21
+ delete(path: string, options?: Record<string, any>): Promise<OASISResponse>;
22
+ }
23
+
24
+ export declare class TokenStore {
25
+ constructor(options?: { persist?: boolean });
26
+ getSession(): OASISSession | null;
27
+ getToken(): string | null;
28
+ setSession(session: OASISSession | null): void;
29
+ clear(): void;
30
+ }
31
+
32
+ export declare const DEFAULT_BASE_URL: string;
33
+
34
+ export declare class Web8Client {
35
+ constructor(options?: Web8ClientOptions);
36
+ readonly http: HttpClient;
37
+ readonly tokenStore: TokenStore;
38
+ readonly mesh: MeshModule;
39
+ readonly protocolBridge: ProtocolBridgeModule;
40
+ setBaseUrl(baseUrl: string): void;
41
+ setToken(jwtToken: string, sessionExtras?: Partial<OASISSession>): void;
42
+ }
43
+
44
+ export default Web8Client;
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('./src/index');
package/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import cjs from './index.js';
2
+
3
+ export const { Web8Client, HttpClient, TokenStore, DEFAULT_BASE_URL } = cjs;
4
+ export default Web8Client;
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@oasisomniverse/web8-api",
3
+ "version": "1.0.0",
4
+ "publishConfig": { "access": "public" },
5
+ "description": "Isomorphic (Node + browser) JavaScript/TypeScript-friendly client for the WEB8 OASIS Galactic Mesh API - fractal holonic mesh node registration, link/route computation, self-healing message relay, and protocol-bridge translation between external wire formats and the unified MeshMessage envelope, built on the OASIS2 WEB8 WebAPI.",
6
+ "main": "index.js",
7
+ "module": "index.mjs",
8
+ "types": "index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.d.ts",
12
+ "import": "./index.mjs",
13
+ "require": "./index.js"
14
+ }
15
+ },
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "files": [
20
+ "index.js",
21
+ "index.mjs",
22
+ "index.d.ts",
23
+ "src",
24
+ "docs"
25
+ ],
26
+ "scripts": {
27
+ "generate": "node scripts/generate-modules.js",
28
+ "docs": "node scripts/generate-docs.js",
29
+ "types": "node scripts/generate-types.js",
30
+ "test": "node --test test"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/NextGenSoftwareUK/OASIS-API-Javascript-Package-WEB8.git"
35
+ },
36
+ "author": "NextGen Software Ltd",
37
+ "license": "MIT",
38
+ "keywords": [
39
+ "WEB8 Galactic Mesh API",
40
+ "WEB8 WebAPI",
41
+ "OASIS Galactic Mesh",
42
+ "Holonic Mesh",
43
+ "Self-Healing Routing",
44
+ "Protocol Bridge",
45
+ "Mesh Networking",
46
+ "OASIS API",
47
+ "WEB 4",
48
+ "WEB 5",
49
+ "WEB 6",
50
+ "WEB 7",
51
+ "WEB 8",
52
+ "Blockchain",
53
+ "Interoperable",
54
+ "Metaverse",
55
+ "Aggregation",
56
+ "HOT-Swappable-Architecture",
57
+ "Decentralised",
58
+ "Distributed",
59
+ "Abstraction-Layer",
60
+ "Multi-Network",
61
+ "SSO",
62
+ "Agent-Centric",
63
+ "GOD-Protocol/API",
64
+ "More Coming..."
65
+ ]
66
+ }
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_BASE_URL = 'https://api.web8.oasisomniverse.one';
4
+
5
+ function buildQueryString(query) {
6
+ const entries = Object.entries(query || {}).filter(([, v]) => v !== undefined && v !== null);
7
+ if (!entries.length) return '';
8
+ const params = new URLSearchParams();
9
+ for (const [key, value] of entries) {
10
+ params.set(key, typeof value === 'object' ? JSON.stringify(value) : String(value));
11
+ }
12
+ return `?${params.toString()}`;
13
+ }
14
+
15
+ /**
16
+ * Thin isomorphic HTTP client around the global fetch API (Node 18+, all modern browsers).
17
+ * Every WEB8 Galactic Mesh & Protocol Bridge API call ultimately goes through `request()` below - there are no mocked
18
+ * or stubbed responses anywhere in this SDK.
19
+ */
20
+ class HttpClient {
21
+ constructor({ baseUrl = DEFAULT_BASE_URL, tokenStore, fetchImpl = globalThis.fetch } = {}) {
22
+ if (!fetchImpl) {
23
+ throw new Error(
24
+ 'No global fetch implementation found. Use Node 18+, a modern browser, or pass { fetchImpl } explicitly.'
25
+ );
26
+ }
27
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
28
+ this.tokenStore = tokenStore;
29
+ this.fetchImpl = fetchImpl;
30
+ }
31
+
32
+ setBaseUrl(baseUrl) {
33
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
34
+ }
35
+
36
+ /**
37
+ * @param {string} verb GET | POST | PUT | DELETE
38
+ * @param {string} path e.g. "v1/complete"
39
+ * @param {object} [options]
40
+ * @param {object} [options.query] query string params (GET/DELETE)
41
+ * @param {object} [options.body] JSON body (POST/PUT/DELETE)
42
+ * @param {boolean} [options.auth] attach Authorization: Bearer <token> (default true)
43
+ * @param {string} [options.token] override token for this single request
44
+ */
45
+ async request(verb, path, { query, body, auth = true, token } = {}) {
46
+ const url = `${this.baseUrl}/${path.replace(/^\/+/, '')}${buildQueryString(query)}`;
47
+ const headers = {
48
+ 'Content-Type': 'application/json',
49
+ Accept: 'application/json'
50
+ };
51
+
52
+ const bearer = token || (auth ? this.tokenStore?.getToken() : null);
53
+ if (bearer) headers.Authorization = `Bearer ${bearer}`;
54
+
55
+ const init = { method: verb, headers };
56
+ if (body !== undefined && verb !== 'GET') init.body = JSON.stringify(body);
57
+
58
+ let res;
59
+ try {
60
+ res = await this.fetchImpl(url, init);
61
+ } catch (err) {
62
+ return { isError: true, message: `Network error calling ${url}: ${err.message}`, exception: err };
63
+ }
64
+
65
+ const text = await res.text();
66
+ let json;
67
+ try {
68
+ json = text ? JSON.parse(text) : null;
69
+ } catch {
70
+ json = null;
71
+ }
72
+
73
+ if (!res.ok) {
74
+ const message =
75
+ json?.result?.message || json?.message || json?.title || `Request failed with status ${res.status}`;
76
+ return { isError: true, message, statusCode: res.status, raw: json };
77
+ }
78
+
79
+ // OASIS responses are typically { isError, message, result: { isError, message, result: <payload> } }.
80
+ // We surface the innermost payload as `.result` while keeping the full envelope available as `.raw`.
81
+ const inner = json?.result !== undefined ? json.result : json;
82
+ const payload = inner?.result !== undefined ? inner.result : inner;
83
+
84
+ return {
85
+ isError: Boolean(inner?.isError || json?.isError),
86
+ message: inner?.message || json?.message || null,
87
+ result: payload,
88
+ raw: json,
89
+ statusCode: res.status
90
+ };
91
+ }
92
+
93
+ get(path, options) {
94
+ return this.request('GET', path, options);
95
+ }
96
+
97
+ post(path, options) {
98
+ return this.request('POST', path, options);
99
+ }
100
+
101
+ put(path, options) {
102
+ return this.request('PUT', path, options);
103
+ }
104
+
105
+ delete(path, options) {
106
+ return this.request('DELETE', path, options);
107
+ }
108
+ }
109
+
110
+ module.exports = { HttpClient, DEFAULT_BASE_URL };
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const TOKEN_PATTERN = /\{(\w+)(?::\w+)?\}/g;
4
+
5
+ /**
6
+ * Resolves a route template like "get-by-id/{id}" against an args object,
7
+ * substituting path tokens and returning the resolved path plus whatever
8
+ * args were *not* consumed as path tokens (these become the query/body).
9
+ */
10
+ function resolveRoute(routeTemplate, args = {}) {
11
+ const consumed = new Set();
12
+ const path = routeTemplate.replace(TOKEN_PATTERN, (match, name) => {
13
+ const key = Object.keys(args).find((k) => k.toLowerCase() === name.toLowerCase());
14
+ consumed.add(key);
15
+ const value = key !== undefined ? args[key] : undefined;
16
+ if (value === undefined) {
17
+ throw new Error(`Missing required route parameter "${name}" for route "${routeTemplate}"`);
18
+ }
19
+ return encodeURIComponent(value);
20
+ });
21
+
22
+ const rest = {};
23
+ for (const [key, value] of Object.entries(args)) {
24
+ if (!consumed.has(key)) rest[key] = value;
25
+ }
26
+
27
+ return { path, rest };
28
+ }
29
+
30
+ function takeKey(obj, name) {
31
+ const matchKey = Object.keys(obj).find((k) => k.toLowerCase() === name.toLowerCase());
32
+ if (matchKey === undefined) return { found: false, value: undefined };
33
+ const value = obj[matchKey];
34
+ delete obj[matchKey];
35
+ return { found: true, value };
36
+ }
37
+
38
+ /**
39
+ * Builds a bound method for a single WEB8 endpoint operation.
40
+ * @param {import('./httpClient').HttpClient} http
41
+ * @param {string} routePrefix e.g. "api/avatar"
42
+ * @param {string} verb GET | POST | PUT | DELETE
43
+ * @param {string} route route template relative to routePrefix, e.g. "get-by-id/{id}"
44
+ * @param {object} [opts]
45
+ * @param {string[]} [opts.query] arg names that ASP.NET binds from the query
46
+ * string on this action regardless of HTTP verb (e.g. `[FromQuery]` flags
47
+ * mixed into an otherwise-body-bound POST/PUT action). Always sent as query.
48
+ * @param {string} [opts.bodyParam] when the action's entire request body is a
49
+ * single `[FromBody]` parameter (primitive or object), the JSON body is
50
+ * that arg's value directly rather than the leftover-args object wrapped
51
+ * around it.
52
+ */
53
+ function makeOperation(http, routePrefix, verb, route, opts = {}) {
54
+ const declaredQueryKeys = opts.query || [];
55
+ const bodyParam = opts.bodyParam;
56
+
57
+ return async function operation(args = {}) {
58
+ const { path, rest } = resolveRoute(route, args);
59
+ const fullPath = path ? `${routePrefix}/${path}` : routePrefix;
60
+
61
+ const query = {};
62
+ for (const key of declaredQueryKeys) {
63
+ const { found, value } = takeKey(rest, key);
64
+ if (found) query[key] = value;
65
+ }
66
+
67
+ let body;
68
+ if (bodyParam) {
69
+ const { found, value } = takeKey(rest, bodyParam);
70
+ if (found) body = value;
71
+ // Any args left over that we don't recognize still get sent (as query)
72
+ // rather than silently dropped.
73
+ Object.assign(query, rest);
74
+ } else if (verb === 'GET' || verb === 'DELETE') {
75
+ Object.assign(query, rest);
76
+ } else {
77
+ body = Object.keys(rest).length ? rest : undefined;
78
+ }
79
+
80
+ const hasQuery = Object.keys(query).length > 0;
81
+ return http.request(verb, fullPath, { query: hasQuery ? query : undefined, body });
82
+ };
83
+ }
84
+
85
+ module.exports = { resolveRoute, makeOperation };
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ const hasLocalStorage = typeof globalThis.localStorage !== 'undefined';
4
+ const STORAGE_KEY = 'oasis_session';
5
+
6
+ /**
7
+ * Holds the current JWT/avatar session for the SDK.
8
+ * In the browser it persists to localStorage by default; in Node (or when
9
+ * persistence is disabled) it simply lives in memory for the lifetime of
10
+ * the client instance. Callers can always set/get/clear explicitly.
11
+ */
12
+ class TokenStore {
13
+ constructor({ persist = hasLocalStorage } = {}) {
14
+ this.persist = persist;
15
+ this._session = null;
16
+
17
+ if (this.persist) {
18
+ try {
19
+ const raw = globalThis.localStorage.getItem(STORAGE_KEY);
20
+ if (raw) this._session = JSON.parse(raw);
21
+ } catch {
22
+ this._session = null;
23
+ }
24
+ }
25
+ }
26
+
27
+ getSession() {
28
+ return this._session;
29
+ }
30
+
31
+ getToken() {
32
+ return this._session?.jwtToken || this._session?.token || null;
33
+ }
34
+
35
+ setSession(session) {
36
+ this._session = session || null;
37
+ if (this.persist) {
38
+ try {
39
+ if (session) globalThis.localStorage.setItem(STORAGE_KEY, JSON.stringify(session));
40
+ else globalThis.localStorage.removeItem(STORAGE_KEY);
41
+ } catch {
42
+ // Storage unavailable (e.g. private browsing) - in-memory session still works.
43
+ }
44
+ }
45
+ }
46
+
47
+ clear() {
48
+ this.setSession(null);
49
+ }
50
+ }
51
+
52
+ module.exports = { TokenStore };
@@ -0,0 +1,18 @@
1
+ // AUTO-GENERATED by scripts/generate-types.js - do not hand-edit.
2
+ export interface OASISResponse<T = any> {
3
+ isError: boolean;
4
+ message: string | null;
5
+ result: T;
6
+ raw: any;
7
+ statusCode: number;
8
+ }
9
+
10
+ export interface OASISSession {
11
+ avatarId: string;
12
+ username: string;
13
+ email: string;
14
+ firstName?: string;
15
+ lastName?: string;
16
+ jwtToken: string;
17
+ refreshToken?: string;
18
+ }
package/src/index.js ADDED
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const { HttpClient, DEFAULT_BASE_URL } = require('./core/httpClient');
4
+ const { TokenStore } = require('./core/tokenStore');
5
+ const { attachGeneratedModules } = require('./modules/index');
6
+
7
+ /**
8
+ * Main SDK entry point. Works in Node 18+ and any modern browser.
9
+ *
10
+ * const { Web8Client } = require('@oasisomniverse/web8-api');
11
+ * const web8 = new Web8Client({ baseUrl: 'https://api.web8.oasisomniverse.one' });
12
+ * web8.setToken(jwtToken); // reuse a WEB4 OASIS JWT - WEB8 has no auth of its own
13
+ * const node = await web8.mesh.registerNode({ name: 'edge-node-1' });
14
+ *
15
+ * Every controller on the WEB8 Mesh WebAPI is reachable as a lowerCamel
16
+ * property (web8.mesh, web8.protocolBridge). Generated methods take a single
17
+ * args object; route template tokens (e.g. {nodeId}) are consumed from it
18
+ * automatically, remaining keys become the query string (GET/DELETE) or JSON
19
+ * body (POST/PUT).
20
+ */
21
+ class Web8Client {
22
+ constructor({ baseUrl = DEFAULT_BASE_URL, persistSession, fetchImpl } = {}) {
23
+ this.tokenStore = new TokenStore({ persist: persistSession });
24
+ this.http = new HttpClient({ baseUrl, tokenStore: this.tokenStore, fetchImpl });
25
+
26
+ attachGeneratedModules(this, this.http);
27
+ }
28
+
29
+ setBaseUrl(baseUrl) {
30
+ this.http.setBaseUrl(baseUrl);
31
+ }
32
+
33
+ /**
34
+ * WEB8 is an internal galactic-mesh/protocol-bridge layer sitting behind
35
+ * the same OASIS identity as WEB4/WEB5/WEB6/WEB7 - it has no avatar/auth
36
+ * endpoints of its own. Reuse a JWT you already obtained from the WEB4
37
+ * OASIS API (or your own backend) here.
38
+ */
39
+ setToken(jwtToken, sessionExtras = {}) {
40
+ this.tokenStore.setSession({ ...sessionExtras, jwtToken });
41
+ }
42
+ }
43
+
44
+ module.exports = { Web8Client, HttpClient, TokenStore, DEFAULT_BASE_URL };
45
+ module.exports.default = Web8Client;
@@ -0,0 +1,24 @@
1
+ // AUTO-GENERATED by scripts/generate-types.js - do not hand-edit.
2
+ import type { OASISResponse } from '../core/types';
3
+
4
+ export declare class MeshModule {
5
+ constructor(http: unknown);
6
+
7
+ /** POST v1/mesh/links (query: nodeAId, nodeBId, latencyMs) */
8
+ addLink(args?: Record<string, any>): Promise<OASISResponse>;
9
+
10
+ /** GET v1/mesh/route (query: sourceNodeId, destinationNodeId) */
11
+ computeRoute(args?: Record<string, any>): Promise<OASISResponse>;
12
+
13
+ /** GET v1/mesh/nodes */
14
+ getNodes(args?: Record<string, any>): Promise<OASISResponse>;
15
+
16
+ /** POST v1/mesh/nodes/{nodeId}/heartbeat */
17
+ heartbeat(args?: Record<string, any>): Promise<OASISResponse>;
18
+
19
+ /** POST v1/mesh/nodes */
20
+ registerNode(args?: Record<string, any>): Promise<OASISResponse>;
21
+
22
+ /** POST v1/mesh/send */
23
+ sendMessage(args?: Record<string, any>): Promise<OASISResponse>;
24
+ }
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ // AUTO-GENERATED by scripts/generate-modules.js from endpoints.json - do not hand-edit.
4
+ // Regenerate with: node scripts/generate-modules.js
5
+
6
+ const { makeOperation } = require('../core/routeHelper');
7
+
8
+ /**
9
+ * Generated wrapper for v1/mesh (source: WEB8 Mesh WebAPI MeshController.cs).
10
+ * Every method takes a single args object: path-template tokens (e.g. {id})
11
+ * are consumed from it automatically; any remaining keys are sent as the
12
+ * query string (GET/DELETE) or JSON body (POST/PUT).
13
+ */
14
+ class MeshModule {
15
+ constructor(http) {
16
+ this._http = http;
17
+
18
+ // POST v1/mesh/links (query: nodeAId, nodeBId, latencyMs)
19
+ this.addLink = makeOperation(http, "v1/mesh", "POST", "links", {"query":["nodeAId","nodeBId","latencyMs"]});
20
+ // GET v1/mesh/route (query: sourceNodeId, destinationNodeId)
21
+ this.computeRoute = makeOperation(http, "v1/mesh", "GET", "route", {"query":["sourceNodeId","destinationNodeId"]});
22
+ // GET v1/mesh/nodes
23
+ this.getNodes = makeOperation(http, "v1/mesh", "GET", "nodes");
24
+ // POST v1/mesh/nodes/{nodeId}/heartbeat
25
+ this.heartbeat = makeOperation(http, "v1/mesh", "POST", "nodes/{nodeId}/heartbeat");
26
+ // POST v1/mesh/nodes
27
+ this.registerNode = makeOperation(http, "v1/mesh", "POST", "nodes");
28
+ // POST v1/mesh/send
29
+ this.sendMessage = makeOperation(http, "v1/mesh", "POST", "send");
30
+ }
31
+ }
32
+
33
+ module.exports = { MeshModule };
@@ -0,0 +1,12 @@
1
+ // AUTO-GENERATED by scripts/generate-types.js - do not hand-edit.
2
+ import type { OASISResponse } from '../core/types';
3
+
4
+ export declare class ProtocolBridgeModule {
5
+ constructor(http: unknown);
6
+
7
+ /** POST v1/protocol-bridge/translate-inbound (query: format, sourceNodeId, destinationNodeId) (body: rawPayload) */
8
+ translateInbound(args?: Record<string, any>): Promise<OASISResponse>;
9
+
10
+ /** POST v1/protocol-bridge/translate-outbound (query: targetFormat) */
11
+ translateOutbound(args?: Record<string, any>): Promise<OASISResponse>;
12
+ }
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ // AUTO-GENERATED by scripts/generate-modules.js from endpoints.json - do not hand-edit.
4
+ // Regenerate with: node scripts/generate-modules.js
5
+
6
+ const { makeOperation } = require('../core/routeHelper');
7
+
8
+ /**
9
+ * Generated wrapper for v1/protocol-bridge (source: WEB8 Mesh WebAPI ProtocolBridgeController.cs).
10
+ * Every method takes a single args object: path-template tokens (e.g. {id})
11
+ * are consumed from it automatically; any remaining keys are sent as the
12
+ * query string (GET/DELETE) or JSON body (POST/PUT).
13
+ */
14
+ class ProtocolBridgeModule {
15
+ constructor(http) {
16
+ this._http = http;
17
+
18
+ // POST v1/protocol-bridge/translate-inbound (query: format, sourceNodeId, destinationNodeId) (body: rawPayload)
19
+ this.translateInbound = makeOperation(http, "v1/protocol-bridge", "POST", "translate-inbound", {"query":["format","sourceNodeId","destinationNodeId"],"bodyParam":"rawPayload"});
20
+ // POST v1/protocol-bridge/translate-outbound (query: targetFormat)
21
+ this.translateOutbound = makeOperation(http, "v1/protocol-bridge", "POST", "translate-outbound", {"query":["targetFormat"]});
22
+ }
23
+ }
24
+
25
+ module.exports = { ProtocolBridgeModule };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ // AUTO-GENERATED by scripts/generate-modules.js - do not hand-edit.
4
+ const { MeshModule } = require('./Mesh');
5
+ const { ProtocolBridgeModule } = require('./ProtocolBridge');
6
+
7
+ /**
8
+ * Attaches every generated module to the client under its lowerCamel controller
9
+ * name (e.g. client.completion, client.images, client.holonicMemory).
10
+ */
11
+ function attachGeneratedModules(client, http) {
12
+ client.mesh = client.mesh || new MeshModule(http);
13
+ client.protocolBridge = client.protocolBridge || new ProtocolBridgeModule(http);
14
+ return client;
15
+ }
16
+
17
+ module.exports = { attachGeneratedModules };