@arkeytyp/valu-api 1.1.1 → 1.1.3
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 +184 -0
- package/package.json +1 -1
- package/src/ValuApi.js +48 -2
- package/src/ValuApplication.js +11 -0
- package/types/valu-api.d.ts +32 -0
package/README.md
CHANGED
|
@@ -225,6 +225,190 @@ const reply = await valuApi.runConsoleCommand(
|
|
|
225
225
|
console.log(reply);
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
+
# Routing in Valu iFrame Applications
|
|
229
|
+
|
|
230
|
+
Valu Social allows third‑party developers to register **mini‑applications** that are rendered inside the platform using **iframes**.
|
|
231
|
+
|
|
232
|
+
Each mini‑app:
|
|
233
|
+
|
|
234
|
+
* Has a unique **application ID / slug**
|
|
235
|
+
* Is mounted under a dedicated URL namespace in Valu Social
|
|
236
|
+
* Can manage its own internal routing (for example using React Router)
|
|
237
|
+
|
|
238
|
+
The host application (Valu Social) and the iframe application must stay **route‑synchronized** to ensure:
|
|
239
|
+
|
|
240
|
+
* Correct deep‑linking
|
|
241
|
+
* Proper browser navigation (back / forward)
|
|
242
|
+
* Shareable URLs
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Application Configuration
|
|
247
|
+
|
|
248
|
+
A mini‑application is registered with a configuration similar to the following:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"id": "demo-app",
|
|
253
|
+
"slug": "demo-app",
|
|
254
|
+
"iframe": {
|
|
255
|
+
"url": "https://sample.texpo.io"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Key fields:
|
|
261
|
+
|
|
262
|
+
* **id / slug** – determines the public URL under valu-social.com
|
|
263
|
+
* **iframe.url** – the base URL loaded inside the iframe
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Host → Iframe Route Mapping
|
|
268
|
+
|
|
269
|
+
Valu Social automatically maps routes from the host URL to the iframe URL.
|
|
270
|
+
|
|
271
|
+
### Base Route
|
|
272
|
+
|
|
273
|
+
When a user opens:
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
https://valu-social.com/demo-app
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Valu Social loads the iframe at:
|
|
280
|
+
|
|
281
|
+
```
|
|
282
|
+
https://sample.texpo.io/
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
### Nested Routes
|
|
288
|
+
|
|
289
|
+
When a user opens:
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
https://valu-social.com/demo-app/page/1
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Valu Social loads the iframe at:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
https://sample.texpo.io/page/1
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
📌 **Rule:**
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
/valu-social/<app-slug>/<path>
|
|
305
|
+
→
|
|
306
|
+
<iframe-base-url>/<path>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
No additional configuration is required for this behavior.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Iframe → Host Route Synchronization
|
|
314
|
+
|
|
315
|
+
If your mini‑application uses **client‑side routing** (for example React Router), route changes **inside the iframe** are not automatically reflected in the Valu Social URL.
|
|
316
|
+
|
|
317
|
+
To keep the host URL in sync, your app must explicitly report route changes using:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
valuApi.pushRoute(pathname);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
This ensures:
|
|
324
|
+
|
|
325
|
+
* The browser URL updates correctly
|
|
326
|
+
* Deep links work as expected
|
|
327
|
+
* Page refresh restores the correct internal state
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## React Router Integration (Recommended)
|
|
332
|
+
|
|
333
|
+
### Valu Router Bridge Component
|
|
334
|
+
|
|
335
|
+
Below is a small helper component that listens for route changes and reports them to Valu Social.
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
import { useEffect } from "react";
|
|
339
|
+
import { useLocation } from "react-router-dom";
|
|
340
|
+
import { useValuAPI } from "@/Hooks/useValuApi";
|
|
341
|
+
|
|
342
|
+
export function ValuRouterBridge() {
|
|
343
|
+
const valuApi = useValuAPI();
|
|
344
|
+
const { pathname } = useLocation();
|
|
345
|
+
|
|
346
|
+
// Iframe → Host
|
|
347
|
+
useEffect(() => {
|
|
348
|
+
if (!valuApi) return;
|
|
349
|
+
valuApi.pushRoute(pathname);
|
|
350
|
+
}, [valuApi, pathname]);
|
|
351
|
+
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
What this does:
|
|
357
|
+
|
|
358
|
+
* Listens to internal route changes via `useLocation()`
|
|
359
|
+
* Pushes the current pathname to Valu Social
|
|
360
|
+
* Keeps host and iframe URLs aligned
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## Example Application Setup
|
|
365
|
+
|
|
366
|
+
Below is an example of how the bridge is used inside a React application with React Router:
|
|
367
|
+
|
|
368
|
+
```tsx
|
|
369
|
+
export default function Home() {
|
|
370
|
+
return (
|
|
371
|
+
<BrowserRouter>
|
|
372
|
+
<ValuRouterBridge />
|
|
373
|
+
|
|
374
|
+
<div className="flex flex-col min-h-screen">
|
|
375
|
+
<TopBar isIFrame={false} />
|
|
376
|
+
|
|
377
|
+
<main className="flex-grow w-full px-4 py-8">
|
|
378
|
+
<div className="max-w-[1400px] mx-auto">
|
|
379
|
+
<Routes>
|
|
380
|
+
<Route path="/" element={<Navigate to="/console" replace />} />
|
|
381
|
+
|
|
382
|
+
<Route
|
|
383
|
+
path="/console"
|
|
384
|
+
element={
|
|
385
|
+
<>
|
|
386
|
+
<Console />
|
|
387
|
+
<SampleApiCalls />
|
|
388
|
+
</>
|
|
389
|
+
}
|
|
390
|
+
/>
|
|
391
|
+
|
|
392
|
+
<Route path="/storage" element={<ApplicationStorage />} />
|
|
393
|
+
<Route path="/documentation" element={<Documentation />} />
|
|
394
|
+
</Routes>
|
|
395
|
+
</div>
|
|
396
|
+
</main>
|
|
397
|
+
|
|
398
|
+
<Footer />
|
|
399
|
+
</div>
|
|
400
|
+
</BrowserRouter>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Best Practices
|
|
407
|
+
|
|
408
|
+
* Always report route changes using `valuApi.pushRoute`
|
|
409
|
+
* Use relative paths (e.g. `/console`, `/page/1`)
|
|
410
|
+
* Ensure your app can handle being opened directly on any route
|
|
411
|
+
|
|
228
412
|
## Sample Project
|
|
229
413
|
|
|
230
414
|
We've created a sample application integrated with Valu API.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkeytyp/valu-api",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "A package for developing iframe applications for Valu Social. Allows invoking functions of registered Valu applications and subscribing to events, as well as other features that enable developers creating own ifram apps for the value social",
|
|
5
5
|
"main": "src/ValuApi.js",
|
|
6
6
|
"scripts": {
|
package/src/ValuApi.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {EventEmitter} from "./EventEmitter.js";
|
|
2
2
|
import {APIPointer} from "./APIPointer.js";
|
|
3
3
|
import {guid4, nextId} from "./Utils.js";
|
|
4
|
-
import {Intent} from "./Intent";
|
|
4
|
+
import {Intent} from "./Intent.js";
|
|
5
5
|
|
|
6
6
|
export { ValuApplication } from "./ValuApplication.js";
|
|
7
7
|
export { Intent } from "./Intent.js";
|
|
@@ -17,6 +17,7 @@ export { APIPointer } from "./APIPointer.js";
|
|
|
17
17
|
export class ValuApi {
|
|
18
18
|
|
|
19
19
|
static API_READY = 'api:ready'
|
|
20
|
+
static ON_ROUTE = `on_route`;
|
|
20
21
|
|
|
21
22
|
#eventEmitter;
|
|
22
23
|
#valuApplication = {};
|
|
@@ -184,6 +185,40 @@ export class ValuApi {
|
|
|
184
185
|
return deferredPromise.promise;
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
|
|
189
|
+
#runCommand(name, data) {
|
|
190
|
+
this.#postToValuApp('api:run-command', {
|
|
191
|
+
command: name,
|
|
192
|
+
data: data,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Pushes a new route onto the navigation stack.
|
|
198
|
+
*
|
|
199
|
+
* Use this when:
|
|
200
|
+
* navigating forward
|
|
201
|
+
* opening a new view
|
|
202
|
+
* preserving back-navigation history
|
|
203
|
+
* @param path
|
|
204
|
+
*/
|
|
205
|
+
pushRoute = (path) => {
|
|
206
|
+
this.#runCommand('pushRoute', path);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Replaces the current route without adding a new history entry.
|
|
211
|
+
* Use this when:
|
|
212
|
+
* redirecting
|
|
213
|
+
* normalizing URLs
|
|
214
|
+
* preventing back-navigation to the previous route
|
|
215
|
+
* @param {string }path
|
|
216
|
+
*/
|
|
217
|
+
replaceRoute = (path) => {
|
|
218
|
+
this.#runCommand('replaceRoute', path);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
187
222
|
#createDeferred() {
|
|
188
223
|
let resolve, reject;
|
|
189
224
|
const promise = new Promise((res, rej) => {
|
|
@@ -219,9 +254,20 @@ export class ValuApi {
|
|
|
219
254
|
break;
|
|
220
255
|
}
|
|
221
256
|
|
|
257
|
+
case 'api:trigger': {
|
|
258
|
+
switch (message.action) {
|
|
259
|
+
case ValuApi.ON_ROUTE: {
|
|
260
|
+
this.#eventEmitter.emit(ValuApi.ON_ROUTE, message.data);
|
|
261
|
+
this.#applicationInstance?.onUpdateRouterContext(message.data);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
222
267
|
case 'api:new-intent': {
|
|
223
268
|
const intent = new Intent(message.applicationId, message.action, message.params);
|
|
224
269
|
this.#applicationInstance?.onNewIntent(intent);
|
|
270
|
+
break;
|
|
225
271
|
}
|
|
226
272
|
|
|
227
273
|
case 'api:run-console-completed': {
|
|
@@ -265,4 +311,4 @@ export class ValuApi {
|
|
|
265
311
|
}
|
|
266
312
|
}
|
|
267
313
|
}
|
|
268
|
-
}
|
|
314
|
+
}
|
package/src/ValuApplication.js
CHANGED
|
@@ -28,4 +28,15 @@ export class ValuApplication {
|
|
|
28
28
|
* Called when the app is about to be destroyed.
|
|
29
29
|
*/
|
|
30
30
|
async onDestroy() {}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Called when the application’s router context changes.
|
|
35
|
+
*
|
|
36
|
+
* This typically happens when:
|
|
37
|
+
* the app moves between main / side / modal containers
|
|
38
|
+
* the host updates routing or layout state
|
|
39
|
+
* @param {string} context
|
|
40
|
+
*/
|
|
41
|
+
onUpdateRouterContext(context) {};
|
|
31
42
|
}
|
package/types/valu-api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
declare module '@arkeytyp/valu-api' {
|
|
2
2
|
export class ValuApi {
|
|
3
3
|
static API_READY: string;
|
|
4
|
+
static ON_ROUTE : string;
|
|
4
5
|
|
|
5
6
|
get connected(): boolean;
|
|
6
7
|
|
|
@@ -22,6 +23,27 @@ declare module '@arkeytyp/valu-api' {
|
|
|
22
23
|
removeEventListener(event: string, callback: (data: any) => void): void;
|
|
23
24
|
getApi(apiName: string, version?: number): Promise<APIPointer>;
|
|
24
25
|
runConsoleCommand(command: string): Promise<any | string>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pushes a new route onto the navigation stack.
|
|
29
|
+
*
|
|
30
|
+
* Use this when:
|
|
31
|
+
* navigating forward
|
|
32
|
+
* opening a new view
|
|
33
|
+
* preserving back-navigation history
|
|
34
|
+
* @param path
|
|
35
|
+
*/
|
|
36
|
+
pushRoute(path: string): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Replaces the current route without adding a new history entry.
|
|
40
|
+
* Use this when:
|
|
41
|
+
* redirecting
|
|
42
|
+
* normalizing URLs
|
|
43
|
+
* preventing back-navigation to the previous route
|
|
44
|
+
* @param {string }path
|
|
45
|
+
*/
|
|
46
|
+
replaceRoute(path: string): void;
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
export class APIPointer {
|
|
@@ -107,5 +129,15 @@ declare module '@arkeytyp/valu-api' {
|
|
|
107
129
|
* Use this to clean up resources (e.g., closing connections, clearing timers).
|
|
108
130
|
*/
|
|
109
131
|
onDestroy(): void;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Called when the application’s router context changes.
|
|
135
|
+
*
|
|
136
|
+
* This typically happens when:
|
|
137
|
+
* the app moves between main / side / modal containers
|
|
138
|
+
* the host updates routing or layout state
|
|
139
|
+
* @param context - updated application route
|
|
140
|
+
*/
|
|
141
|
+
onUpdateRouterContext(context: string): void;
|
|
110
142
|
}
|
|
111
143
|
}
|