@nominalso/vibe-host 0.2.0 → 0.3.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 +21 -7
- package/dist/index.cjs +49 -4
- package/dist/index.d.cts +36 -2
- package/dist/index.d.ts +36 -2
- package/dist/index.js +49 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,8 @@ Externalizes only `@hey-api/client-fetch`; TypeScript types are self-contained.
|
|
|
18
18
|
import { VibeAppHost } from '@nominalso/vibe-host'
|
|
19
19
|
|
|
20
20
|
const host = new VibeAppHost({
|
|
21
|
-
// Iframe origin(s) allowed to talk to this host
|
|
21
|
+
// Iframe origin(s) allowed to talk to this host. Exact origins, or glob
|
|
22
|
+
// patterns (e.g. 'https://*.vercel.app') — add patterns only in non-prod.
|
|
22
23
|
trustedOrigins: ['https://my-vibe-app.lovable.app'],
|
|
23
24
|
// This Vibe App's base path in nom-ui (used to sync the browser URL).
|
|
24
25
|
appBasePath: `/${tenant}/${subsidiary}/apps/${slug}`,
|
|
@@ -37,15 +38,28 @@ const unmount = host.mount()
|
|
|
37
38
|
|
|
38
39
|
### `new VibeAppHost(options)`
|
|
39
40
|
|
|
40
|
-
| Option | Type | Description
|
|
41
|
-
| ---------------- | ----------------------- |
|
|
42
|
-
| `trustedOrigins` | `string[]` | Iframe origins allowed to talk to this host. Messages from other origins are ignored.
|
|
43
|
-
| `getContext` | `() => HostContext` | Returns the current context to send to the iframe (`hostVersion` is injected automatically).
|
|
44
|
-
| `appBasePath` | `string` | Base URL path of this Vibe App in nom-ui, e.g. `/acme/1/apps/fixed-assets`.
|
|
45
|
-
| `clientConfig` | `VibeApiClientOptions?` | Points the built-in handler map at the Nominal API. Defaults to `{ baseUrl: '', stripApiPrefix: false }`.
|
|
41
|
+
| Option | Type | Description |
|
|
42
|
+
| ---------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `trustedOrigins` | `string[]` | Iframe origins allowed to talk to this host — exact origins or glob patterns (`https://*.vercel.app`, honoured unconditionally, so add only in non-prod). Messages from other origins are ignored. |
|
|
44
|
+
| `getContext` | `() => HostContext` | Returns the current context to send to the iframe (`hostVersion` is injected automatically). |
|
|
45
|
+
| `appBasePath` | `string` | Base URL path of this Vibe App in nom-ui, e.g. `/acme/1/apps/fixed-assets`. |
|
|
46
|
+
| `clientConfig` | `VibeApiClientOptions?` | Points the built-in handler map at the Nominal API. Defaults to `{ baseUrl: '', stripApiPrefix: false }`. |
|
|
46
47
|
|
|
47
48
|
`VibeApiClientOptions` is `{ baseUrl: string; stripApiPrefix?: boolean }`. Set `stripApiPrefix: true` when routing through a proxy whose convention expects paths without the `/api` prefix.
|
|
48
49
|
|
|
50
|
+
#### Testing preview deployments with `trustedOrigins` patterns
|
|
51
|
+
|
|
52
|
+
A pattern entry's `*` matches exactly one DNS label or port — anchored, scheme literal — so `https://*.vercel.app` matches `https://pr-7.vercel.app` but not `https://a.b.vercel.app` or `https://pr-7.vercel.app.evil.com`. Patterns are honoured unconditionally, so **gate the list on your own deploy environment** — keep production exact-only:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const trustedOrigins =
|
|
56
|
+
process.env.NOM_ENV === 'production'
|
|
57
|
+
? ['https://app.nominal.so']
|
|
58
|
+
: ['https://app.nominal.so', 'https://*.vercel.app', 'https://*.lovable.app']
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Because you cannot `postMessage` to a pattern, proactive context/subroute pushes target the exact entries plus any origin seen on a validated inbound message; a pattern-matched iframe still receives context via its `connect()` poll response.
|
|
62
|
+
|
|
49
63
|
### `mount(iframeWindow?): () => void`
|
|
50
64
|
|
|
51
65
|
Starts listening for bridge messages and sets up browser back/forward sync. Returns a cleanup function. Pass `iframeWindow` to immediately push context; if omitted, the host answers `GET_CONTEXT` polls but won't proactively push until you call `pushContextTo`.
|
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
|
|
29
29
|
// package.json
|
|
30
|
-
var version = "0.
|
|
30
|
+
var version = "0.3.0";
|
|
31
31
|
|
|
32
32
|
// ../protocol-types/dist/index.js
|
|
33
33
|
function normalizeSubroute(subroute) {
|
|
@@ -72,6 +72,33 @@ var HttpBridgeError = class extends BridgeError {
|
|
|
72
72
|
this.name = "HttpBridgeError";
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
|
+
function isOriginPattern(entry) {
|
|
76
|
+
return entry.includes("*");
|
|
77
|
+
}
|
|
78
|
+
var patternCache = /* @__PURE__ */ new Map();
|
|
79
|
+
function patternToRegExp(pattern) {
|
|
80
|
+
const cached = patternCache.get(pattern);
|
|
81
|
+
if (cached) return cached;
|
|
82
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
83
|
+
const body = escaped.replace(/\\\*/g, "[^./:@?#\\s]+");
|
|
84
|
+
const compiled = new RegExp(`^${body}$`);
|
|
85
|
+
patternCache.set(pattern, compiled);
|
|
86
|
+
return compiled;
|
|
87
|
+
}
|
|
88
|
+
function matchesOrigin(origin, entry) {
|
|
89
|
+
if (!isOriginPattern(entry)) return origin === entry;
|
|
90
|
+
return patternToRegExp(entry).test(origin);
|
|
91
|
+
}
|
|
92
|
+
function isOriginAllowed(origin, allowlist, { allowPatterns }) {
|
|
93
|
+
for (const entry of allowlist) {
|
|
94
|
+
if (isOriginPattern(entry)) {
|
|
95
|
+
if (allowPatterns && matchesOrigin(origin, entry)) return true;
|
|
96
|
+
} else if (origin === entry) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
75
102
|
|
|
76
103
|
// ../api-client/dist/index.js
|
|
77
104
|
var import_client_fetch = require("@hey-api/client-fetch");
|
|
@@ -629,6 +656,12 @@ function rejectUnwired(op) {
|
|
|
629
656
|
// src/VibeAppHost.ts
|
|
630
657
|
var VibeAppHost = class {
|
|
631
658
|
constructor(opts) {
|
|
659
|
+
/**
|
|
660
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
661
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
662
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
663
|
+
*/
|
|
664
|
+
this.connectedOrigins = /* @__PURE__ */ new Set();
|
|
632
665
|
this.iframeWindow = null;
|
|
633
666
|
/**
|
|
634
667
|
* Source windows we've already logged a successful handshake for. The bridge
|
|
@@ -642,6 +675,7 @@ var VibeAppHost = class {
|
|
|
642
675
|
};
|
|
643
676
|
const { trustedOrigins, getContext, appBasePath, clientConfig } = opts;
|
|
644
677
|
this.trustedOrigins = new Set(trustedOrigins);
|
|
678
|
+
this.exactOrigins = trustedOrigins.filter((entry) => !isOriginPattern(entry));
|
|
645
679
|
this.getContext = getContext;
|
|
646
680
|
this.appBasePath = appBasePath;
|
|
647
681
|
this.onRequestComplete = opts.onRequestComplete;
|
|
@@ -715,6 +749,7 @@ var VibeAppHost = class {
|
|
|
715
749
|
window.removeEventListener("message", this.boundHandleMessage);
|
|
716
750
|
window.removeEventListener("popstate", this.boundHandlePopState);
|
|
717
751
|
this.iframeWindow = null;
|
|
752
|
+
this.connectedOrigins.clear();
|
|
718
753
|
};
|
|
719
754
|
}
|
|
720
755
|
/**
|
|
@@ -734,7 +769,7 @@ var VibeAppHost = class {
|
|
|
734
769
|
type: "CONTEXT_PUSH",
|
|
735
770
|
payload: { ...this.getContext(), hostVersion: version }
|
|
736
771
|
};
|
|
737
|
-
for (const origin of this.
|
|
772
|
+
for (const origin of this.concreteTargets()) {
|
|
738
773
|
target.postMessage(message, origin);
|
|
739
774
|
}
|
|
740
775
|
}
|
|
@@ -754,15 +789,25 @@ var VibeAppHost = class {
|
|
|
754
789
|
type,
|
|
755
790
|
payload
|
|
756
791
|
};
|
|
757
|
-
for (const origin of this.
|
|
792
|
+
for (const origin of this.concreteTargets()) {
|
|
758
793
|
this.iframeWindow.postMessage(message, origin);
|
|
759
794
|
}
|
|
760
795
|
}
|
|
761
796
|
async handleMessage(event) {
|
|
762
|
-
if (!
|
|
797
|
+
if (!isOriginAllowed(event.origin, this.trustedOrigins, { allowPatterns: true })) return;
|
|
763
798
|
if (!isBridgeMessage(event.data)) return;
|
|
799
|
+
this.connectedOrigins.add(event.origin);
|
|
764
800
|
await this.kindHandlers[event.data.kind]?.(event.data, event.source, event.origin);
|
|
765
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
804
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
805
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
806
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
807
|
+
*/
|
|
808
|
+
concreteTargets() {
|
|
809
|
+
return /* @__PURE__ */ new Set([...this.exactOrigins, ...this.connectedOrigins]);
|
|
810
|
+
}
|
|
766
811
|
async handleRequest(msg, source, origin) {
|
|
767
812
|
const { requestId, type } = msg;
|
|
768
813
|
if (type === "GET_CONTEXT") {
|
package/dist/index.d.cts
CHANGED
|
@@ -3549,8 +3549,21 @@ interface RequestCompleteInfo {
|
|
|
3549
3549
|
interface VibeAppHostOptions {
|
|
3550
3550
|
/**
|
|
3551
3551
|
* Iframe origins allowed to talk to this host. Messages from any other origin
|
|
3552
|
-
* are ignored, and context/pushes are only sent to
|
|
3553
|
-
*
|
|
3552
|
+
* are ignored, and context/pushes are only sent to allowed origins.
|
|
3553
|
+
*
|
|
3554
|
+
* Each entry is either an exact origin (`'https://my-vibe-app.lovable.app'`)
|
|
3555
|
+
* or a **glob pattern** containing `*` (`'https://*.vercel.app'`), where `*`
|
|
3556
|
+
* matches exactly one DNS label or port — anchored, scheme literal — so
|
|
3557
|
+
* `https://*.vercel.app` matches `https://pr-7.vercel.app` but not
|
|
3558
|
+
* `https://a.b.vercel.app` or `https://pr-7.vercel.app.evil.com`.
|
|
3559
|
+
*
|
|
3560
|
+
* Patterns are honoured unconditionally, so **only add them for non-production
|
|
3561
|
+
* environments** — gate this list on your own deploy env (e.g. pass exact
|
|
3562
|
+
* origins only in production, exact + `*.vercel.app`/`*.lovable.app` in
|
|
3563
|
+
* staging/dev) to test against dynamic preview deployments. Because you cannot
|
|
3564
|
+
* `postMessage` to a pattern, proactive context/subroute pushes target the
|
|
3565
|
+
* exact entries plus any origin seen on a validated inbound message; pattern-
|
|
3566
|
+
* matched iframes still receive context via their `connect()` poll response.
|
|
3554
3567
|
*/
|
|
3555
3568
|
trustedOrigins: string[];
|
|
3556
3569
|
/** Returns the current context to send to the iframe on connect. */
|
|
@@ -3621,7 +3634,21 @@ interface VibeAppHostOptions {
|
|
|
3621
3634
|
* ```
|
|
3622
3635
|
*/
|
|
3623
3636
|
declare class VibeAppHost {
|
|
3637
|
+
/**
|
|
3638
|
+
* Configured allowlist. Entries may be exact origins or glob patterns
|
|
3639
|
+
* (`https://*.vercel.app`); inbound messages are validated against it with
|
|
3640
|
+
* {@link isOriginAllowed}. Pattern entries are honoured — nom-ui is expected
|
|
3641
|
+
* to supply patterns only in non-production environments.
|
|
3642
|
+
*/
|
|
3624
3643
|
private readonly trustedOrigins;
|
|
3644
|
+
/** The exact (non-pattern) subset of {@link trustedOrigins} — see {@link concreteTargets}. */
|
|
3645
|
+
private readonly exactOrigins;
|
|
3646
|
+
/**
|
|
3647
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
3648
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
3649
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
3650
|
+
*/
|
|
3651
|
+
private readonly connectedOrigins;
|
|
3625
3652
|
private readonly handlers;
|
|
3626
3653
|
private readonly commandHandlers;
|
|
3627
3654
|
private readonly getContext;
|
|
@@ -3672,6 +3699,13 @@ declare class VibeAppHost {
|
|
|
3672
3699
|
private handlePopState;
|
|
3673
3700
|
private pushToIframe;
|
|
3674
3701
|
private handleMessage;
|
|
3702
|
+
/**
|
|
3703
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
3704
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
3705
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
3706
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
3707
|
+
*/
|
|
3708
|
+
private concreteTargets;
|
|
3675
3709
|
private handleRequest;
|
|
3676
3710
|
private respond;
|
|
3677
3711
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3549,8 +3549,21 @@ interface RequestCompleteInfo {
|
|
|
3549
3549
|
interface VibeAppHostOptions {
|
|
3550
3550
|
/**
|
|
3551
3551
|
* Iframe origins allowed to talk to this host. Messages from any other origin
|
|
3552
|
-
* are ignored, and context/pushes are only sent to
|
|
3553
|
-
*
|
|
3552
|
+
* are ignored, and context/pushes are only sent to allowed origins.
|
|
3553
|
+
*
|
|
3554
|
+
* Each entry is either an exact origin (`'https://my-vibe-app.lovable.app'`)
|
|
3555
|
+
* or a **glob pattern** containing `*` (`'https://*.vercel.app'`), where `*`
|
|
3556
|
+
* matches exactly one DNS label or port — anchored, scheme literal — so
|
|
3557
|
+
* `https://*.vercel.app` matches `https://pr-7.vercel.app` but not
|
|
3558
|
+
* `https://a.b.vercel.app` or `https://pr-7.vercel.app.evil.com`.
|
|
3559
|
+
*
|
|
3560
|
+
* Patterns are honoured unconditionally, so **only add them for non-production
|
|
3561
|
+
* environments** — gate this list on your own deploy env (e.g. pass exact
|
|
3562
|
+
* origins only in production, exact + `*.vercel.app`/`*.lovable.app` in
|
|
3563
|
+
* staging/dev) to test against dynamic preview deployments. Because you cannot
|
|
3564
|
+
* `postMessage` to a pattern, proactive context/subroute pushes target the
|
|
3565
|
+
* exact entries plus any origin seen on a validated inbound message; pattern-
|
|
3566
|
+
* matched iframes still receive context via their `connect()` poll response.
|
|
3554
3567
|
*/
|
|
3555
3568
|
trustedOrigins: string[];
|
|
3556
3569
|
/** Returns the current context to send to the iframe on connect. */
|
|
@@ -3621,7 +3634,21 @@ interface VibeAppHostOptions {
|
|
|
3621
3634
|
* ```
|
|
3622
3635
|
*/
|
|
3623
3636
|
declare class VibeAppHost {
|
|
3637
|
+
/**
|
|
3638
|
+
* Configured allowlist. Entries may be exact origins or glob patterns
|
|
3639
|
+
* (`https://*.vercel.app`); inbound messages are validated against it with
|
|
3640
|
+
* {@link isOriginAllowed}. Pattern entries are honoured — nom-ui is expected
|
|
3641
|
+
* to supply patterns only in non-production environments.
|
|
3642
|
+
*/
|
|
3624
3643
|
private readonly trustedOrigins;
|
|
3644
|
+
/** The exact (non-pattern) subset of {@link trustedOrigins} — see {@link concreteTargets}. */
|
|
3645
|
+
private readonly exactOrigins;
|
|
3646
|
+
/**
|
|
3647
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
3648
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
3649
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
3650
|
+
*/
|
|
3651
|
+
private readonly connectedOrigins;
|
|
3625
3652
|
private readonly handlers;
|
|
3626
3653
|
private readonly commandHandlers;
|
|
3627
3654
|
private readonly getContext;
|
|
@@ -3672,6 +3699,13 @@ declare class VibeAppHost {
|
|
|
3672
3699
|
private handlePopState;
|
|
3673
3700
|
private pushToIframe;
|
|
3674
3701
|
private handleMessage;
|
|
3702
|
+
/**
|
|
3703
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
3704
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
3705
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
3706
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
3707
|
+
*/
|
|
3708
|
+
private concreteTargets;
|
|
3675
3709
|
private handleRequest;
|
|
3676
3710
|
private respond;
|
|
3677
3711
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// package.json
|
|
2
|
-
var version = "0.
|
|
2
|
+
var version = "0.3.0";
|
|
3
3
|
|
|
4
4
|
// ../protocol-types/dist/index.js
|
|
5
5
|
function normalizeSubroute(subroute) {
|
|
@@ -44,6 +44,33 @@ var HttpBridgeError = class extends BridgeError {
|
|
|
44
44
|
this.name = "HttpBridgeError";
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
+
function isOriginPattern(entry) {
|
|
48
|
+
return entry.includes("*");
|
|
49
|
+
}
|
|
50
|
+
var patternCache = /* @__PURE__ */ new Map();
|
|
51
|
+
function patternToRegExp(pattern) {
|
|
52
|
+
const cached = patternCache.get(pattern);
|
|
53
|
+
if (cached) return cached;
|
|
54
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
55
|
+
const body = escaped.replace(/\\\*/g, "[^./:@?#\\s]+");
|
|
56
|
+
const compiled = new RegExp(`^${body}$`);
|
|
57
|
+
patternCache.set(pattern, compiled);
|
|
58
|
+
return compiled;
|
|
59
|
+
}
|
|
60
|
+
function matchesOrigin(origin, entry) {
|
|
61
|
+
if (!isOriginPattern(entry)) return origin === entry;
|
|
62
|
+
return patternToRegExp(entry).test(origin);
|
|
63
|
+
}
|
|
64
|
+
function isOriginAllowed(origin, allowlist, { allowPatterns }) {
|
|
65
|
+
for (const entry of allowlist) {
|
|
66
|
+
if (isOriginPattern(entry)) {
|
|
67
|
+
if (allowPatterns && matchesOrigin(origin, entry)) return true;
|
|
68
|
+
} else if (origin === entry) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
47
74
|
|
|
48
75
|
// ../api-client/dist/index.js
|
|
49
76
|
import {
|
|
@@ -604,6 +631,12 @@ function rejectUnwired(op) {
|
|
|
604
631
|
// src/VibeAppHost.ts
|
|
605
632
|
var VibeAppHost = class {
|
|
606
633
|
constructor(opts) {
|
|
634
|
+
/**
|
|
635
|
+
* Concrete iframe origins observed on validated inbound messages. Because you
|
|
636
|
+
* cannot `postMessage` to a glob pattern, proactive pushes target these
|
|
637
|
+
* learned origins (plus the exact allowlist entries) rather than the patterns.
|
|
638
|
+
*/
|
|
639
|
+
this.connectedOrigins = /* @__PURE__ */ new Set();
|
|
607
640
|
this.iframeWindow = null;
|
|
608
641
|
/**
|
|
609
642
|
* Source windows we've already logged a successful handshake for. The bridge
|
|
@@ -617,6 +650,7 @@ var VibeAppHost = class {
|
|
|
617
650
|
};
|
|
618
651
|
const { trustedOrigins, getContext, appBasePath, clientConfig } = opts;
|
|
619
652
|
this.trustedOrigins = new Set(trustedOrigins);
|
|
653
|
+
this.exactOrigins = trustedOrigins.filter((entry) => !isOriginPattern(entry));
|
|
620
654
|
this.getContext = getContext;
|
|
621
655
|
this.appBasePath = appBasePath;
|
|
622
656
|
this.onRequestComplete = opts.onRequestComplete;
|
|
@@ -690,6 +724,7 @@ var VibeAppHost = class {
|
|
|
690
724
|
window.removeEventListener("message", this.boundHandleMessage);
|
|
691
725
|
window.removeEventListener("popstate", this.boundHandlePopState);
|
|
692
726
|
this.iframeWindow = null;
|
|
727
|
+
this.connectedOrigins.clear();
|
|
693
728
|
};
|
|
694
729
|
}
|
|
695
730
|
/**
|
|
@@ -709,7 +744,7 @@ var VibeAppHost = class {
|
|
|
709
744
|
type: "CONTEXT_PUSH",
|
|
710
745
|
payload: { ...this.getContext(), hostVersion: version }
|
|
711
746
|
};
|
|
712
|
-
for (const origin of this.
|
|
747
|
+
for (const origin of this.concreteTargets()) {
|
|
713
748
|
target.postMessage(message, origin);
|
|
714
749
|
}
|
|
715
750
|
}
|
|
@@ -729,15 +764,25 @@ var VibeAppHost = class {
|
|
|
729
764
|
type,
|
|
730
765
|
payload
|
|
731
766
|
};
|
|
732
|
-
for (const origin of this.
|
|
767
|
+
for (const origin of this.concreteTargets()) {
|
|
733
768
|
this.iframeWindow.postMessage(message, origin);
|
|
734
769
|
}
|
|
735
770
|
}
|
|
736
771
|
async handleMessage(event) {
|
|
737
|
-
if (!
|
|
772
|
+
if (!isOriginAllowed(event.origin, this.trustedOrigins, { allowPatterns: true })) return;
|
|
738
773
|
if (!isBridgeMessage(event.data)) return;
|
|
774
|
+
this.connectedOrigins.add(event.origin);
|
|
739
775
|
await this.kindHandlers[event.data.kind]?.(event.data, event.source, event.origin);
|
|
740
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Concrete origins a proactive push may target: the exact allowlist entries
|
|
779
|
+
* plus any origins seen on validated inbound messages. Never includes glob
|
|
780
|
+
* patterns (you cannot `postMessage` to one). When only exact origins are
|
|
781
|
+
* configured this equals the configured set, so behaviour is unchanged.
|
|
782
|
+
*/
|
|
783
|
+
concreteTargets() {
|
|
784
|
+
return /* @__PURE__ */ new Set([...this.exactOrigins, ...this.connectedOrigins]);
|
|
785
|
+
}
|
|
741
786
|
async handleRequest(msg, source, origin) {
|
|
742
787
|
const { requestId, type } = msg;
|
|
743
788
|
if (type === "GET_CONTEXT") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nominalso/vibe-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Host-side SDK for embedding Nominal Vibe Apps — receives bridge requests over a typed postMessage protocol and dispatches them to Nominal APIs (used by nom-ui).",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://github.com/nominalso/vibe-apps-sdk#readme",
|