@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 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.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
+ }
@@ -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
  }
@@ -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
  }