@aiworkbench/vibe-bridge 0.0.5 → 0.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/auth.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=event-bus.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-bus.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/event-bus.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=navigation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/navigation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=permissions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/permissions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/storage.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=toast.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/toast.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,15 @@
1
+ import type { ChatBridge } from "@aiworkbench/vibe-types";
2
+ export interface ChatAdapterDeps {
3
+ getToken: () => Promise<string>;
4
+ /** Base path for the chat API route on the Host. Defaults to "/api/chat". */
5
+ chatEndpoint?: string;
6
+ }
7
+ /**
8
+ * Create the production ChatBridge adapter.
9
+ *
10
+ * The production adapter sends messages through the Host's `/api/chat`
11
+ * endpoint which handles auth, session management, and MCP header injection
12
+ * server-side.
13
+ */
14
+ export declare function createChatAdapter(deps: ChatAdapterDeps): ChatBridge;
15
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/adapters/chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA0FD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CA2DnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/adapters/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,qBAAqB,GAC1B,gBAAgB,CAYlB"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/adapters/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,qBAAqB,GAC1B,gBAAgB,CAgBlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAgBxE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,gBAAgB,EAAE,GAC9B,MAAM,CAqBR"}
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/core/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAgBxE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,gBAAgB,EAAE,GAC9B,MAAM,CAsBR"}
@@ -1,4 +1,4 @@
1
- import type { Bridge, VibeManifest, AuthBridge, ApiBridge, NavigationBridge, ThemeBridge, ToastBridge, StorageBridge, EventsBridge } from "@aiworkbench/vibe-types";
1
+ import type { Bridge, VibeManifest, AuthBridge, ApiBridge, NavigationBridge, ThemeBridge, ToastBridge, StorageBridge, EventsBridge, ChatBridge } from "@aiworkbench/vibe-types";
2
2
  import type { ReactNode, CSSProperties } from "react";
3
3
  export interface VibeHostProps {
4
4
  /** The mini-app manifest describing its id, permissions, etc. */
@@ -30,6 +30,7 @@ export interface AdapterOverrides {
30
30
  toast: ToastBridge;
31
31
  storage: StorageBridge;
32
32
  events: EventsBridge;
33
+ chat: ChatBridge;
33
34
  }
34
35
  export type LoaderStatus = "loading" | "ready" | "error";
35
36
  export interface UseVibeLoaderResult {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,EACb,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,QAAQ,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;4EAEwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,yEAAyE;IACzE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACrC,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,SAAS,CAAC;IACf,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,SAAS,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACvC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,EACZ,UAAU,EACX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,QAAQ,EAAE,YAAY,CAAC;IACvB,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;4EAEwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,yEAAyE;IACzE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACrC,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,SAAS,CAAC;IACf,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,SAAS,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useVibeBridge.d.ts","sourceRoot":"","sources":["../../src/hooks/useVibeBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAYxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA2BnE"}
1
+ {"version":3,"file":"useVibeBridge.d.ts","sourceRoot":"","sources":["../../src/hooks/useVibeBridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAaxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA4BnE"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { createThemeAdapter } from "./adapters/theme";
10
10
  export { createToastAdapter } from "./adapters/toast";
11
11
  export { createStorageAdapter } from "./adapters/storage";
12
12
  export { createEventsAdapter } from "./adapters/events";
13
+ export { createChatAdapter } from "./adapters/chat";
13
14
  export { filterByPermissions } from "./core/permissions";
14
15
  export { getEventBus, emitEvent } from "./core/event-bus";
15
16
  export { setAllowedOrigins, setSharedDependencies } from "./core/loader";
@@ -18,4 +19,5 @@ export type { AuthAdapterDeps } from "./adapters/auth";
18
19
  export type { ApiAdapterDeps } from "./adapters/api";
19
20
  export type { NavigationAdapterDeps } from "./adapters/navigation";
20
21
  export type { ToastAdapterDeps } from "./adapters/toast";
22
+ export type { ChatAdapterDeps } from "./adapters/chat";
21
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGzE,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGzE,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,487 +1,3 @@
1
- // src/components/VibeHost.tsx
2
- import { useRef, useEffect as useEffect2, useCallback } from "react";
3
-
4
- // src/hooks/useVibeLoader.ts
5
- import { useState, useEffect } from "react";
6
-
7
- // src/core/loader.ts
8
- var loadedScripts = new Set;
9
- var ALLOWED_PROTOCOLS = new Set(["https:", "http:"]);
10
- var allowedOrigins = null;
11
- var sharedDeps = null;
12
- var importMapInjected = false;
13
- function setSharedDependencies(deps) {
14
- if (importMapInjected) {
15
- console.warn("[vibe-bridge] setSharedDependencies() called after an import map " + "was already injected. The new entries will be ignored.");
16
- return;
17
- }
18
- sharedDeps = { ...deps };
19
- }
20
- function ensureImportMap() {
21
- if (importMapInjected || !sharedDeps)
22
- return;
23
- const script = document.createElement("script");
24
- script.type = "importmap";
25
- script.textContent = JSON.stringify({ imports: sharedDeps });
26
- document.head.appendChild(script);
27
- importMapInjected = true;
28
- }
29
- function setAllowedOrigins(origins) {
30
- allowedOrigins = new Set(origins.map((o) => {
31
- const url = new URL(o);
32
- return url.origin;
33
- }));
34
- }
35
- function validateScriptSrc(src) {
36
- let url;
37
- try {
38
- url = new URL(src, window.location.href);
39
- } catch {
40
- return `Invalid vibe app script URL: ${src}`;
41
- }
42
- if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
43
- return `Refused to load vibe app script with disallowed protocol "${url.protocol}": ${src}`;
44
- }
45
- if (allowedOrigins && !allowedOrigins.has(url.origin)) {
46
- return `Refused to load vibe app script from untrusted origin "${url.origin}": ${src}. ` + `Allowed origins: ${[...allowedOrigins].join(", ")}`;
47
- }
48
- return null;
49
- }
50
- function loadScript(src, options) {
51
- const validationError = validateScriptSrc(src);
52
- if (validationError) {
53
- return Promise.reject(new Error(validationError));
54
- }
55
- if (loadedScripts.has(src)) {
56
- return Promise.resolve();
57
- }
58
- return new Promise((resolve, reject) => {
59
- const existing = document.querySelector(`script[src="${CSS.escape(src)}"]`);
60
- if (existing) {
61
- loadedScripts.add(src);
62
- resolve();
63
- return;
64
- }
65
- const script = document.createElement("script");
66
- script.type = "module";
67
- script.src = src;
68
- if (options?.integrity) {
69
- script.integrity = options.integrity;
70
- script.crossOrigin = options.crossOrigin ?? "anonymous";
71
- }
72
- script.onload = () => {
73
- loadedScripts.add(src);
74
- resolve();
75
- };
76
- script.onerror = () => {
77
- const integrityHint = options?.integrity ? " This may be an SRI integrity mismatch — the bundle content doesn't match the expected hash." : "";
78
- reject(new Error(`Failed to load vibe app script: ${src}.${integrityHint}`));
79
- };
80
- ensureImportMap();
81
- document.head.appendChild(script);
82
- });
83
- }
84
- function waitForElement(tagName) {
85
- if (customElements.get(tagName)) {
86
- return Promise.resolve();
87
- }
88
- return customElements.whenDefined(tagName).then(() => {
89
- return;
90
- });
91
- }
92
-
93
- // src/hooks/useVibeLoader.ts
94
- function useVibeLoader(src, tagName, integrity) {
95
- const [status, setStatus] = useState("loading");
96
- const [error, setError] = useState(null);
97
- useEffect(() => {
98
- let cancelled = false;
99
- setStatus("loading");
100
- setError(null);
101
- loadScript(src, integrity ? { integrity } : undefined).then(() => waitForElement(tagName)).then(() => {
102
- if (!cancelled) {
103
- setStatus("ready");
104
- }
105
- }).catch((err) => {
106
- if (!cancelled) {
107
- const e = err instanceof Error ? err : new Error(`Failed to load vibe app: ${tagName}`);
108
- setError(e);
109
- setStatus("error");
110
- }
111
- });
112
- return () => {
113
- cancelled = true;
114
- };
115
- }, [src, tagName, integrity]);
116
- return { status, error };
117
- }
118
-
119
- // src/hooks/useVibeBridge.ts
120
- import { useMemo } from "react";
121
- import { useSession } from "next-auth/react";
122
- import { useRouter } from "next/navigation";
123
- import { toast } from "sonner";
124
-
125
- // src/adapters/auth.ts
126
- function createAuthAdapter(deps) {
127
- return {
128
- getUser() {
129
- const user = deps.session?.user;
130
- return {
131
- id: user?.email ?? "unknown",
132
- name: user?.name ?? "Unknown User",
133
- email: user?.email ?? undefined
134
- };
135
- },
136
- async getToken() {
137
- const token = deps.session?.idToken;
138
- if (!token) {
139
- throw new Error("No authentication token available.");
140
- }
141
- return token;
142
- }
143
- };
144
- }
145
-
146
- // src/adapters/api.ts
147
- function createApiAdapter(deps) {
148
- return {
149
- async fetch(endpoint, options) {
150
- const url = endpoint.startsWith("/api/") ? endpoint : `/api/${endpoint.replace(/^\//, "")}`;
151
- const token = await deps.getToken();
152
- const headers = new Headers(options?.headers);
153
- headers.set("Authorization", `Bearer ${token}`);
154
- return globalThis.fetch(url, {
155
- ...options,
156
- headers
157
- });
158
- }
159
- };
160
- }
161
-
162
- // src/adapters/navigation.ts
163
- function createNavigationAdapter(deps) {
164
- return {
165
- navigate(path) {
166
- if (/^https?:\/\//i.test(path) || /^javascript:/i.test(path)) {
167
- throw new Error(`Navigation to external URLs is not allowed: ${path}`);
168
- }
169
- deps.push(path);
170
- }
171
- };
172
- }
173
-
174
- // src/adapters/theme.ts
175
- function createThemeAdapter() {
176
- return {
177
- current() {
178
- const html = document.documentElement;
179
- if (html.classList.contains("dark"))
180
- return "dark";
181
- if (html.classList.contains("light"))
182
- return "light";
183
- const dataTheme = html.getAttribute("data-theme");
184
- if (dataTheme === "dark")
185
- return "dark";
186
- if (dataTheme === "light")
187
- return "light";
188
- if (globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches) {
189
- return "dark";
190
- }
191
- return "light";
192
- }
193
- };
194
- }
195
-
196
- // src/adapters/toast.ts
197
- function createToastAdapter(deps) {
198
- return {
199
- show(message, options) {
200
- const type = options?.type ?? "info";
201
- switch (type) {
202
- case "success":
203
- deps.toast.success(message);
204
- break;
205
- case "error":
206
- deps.toast.error(message);
207
- break;
208
- case "info":
209
- deps.toast.info(message);
210
- break;
211
- default:
212
- deps.toast(message);
213
- }
214
- }
215
- };
216
- }
217
-
218
- // src/adapters/storage.ts
219
- function createStorageAdapter(appId) {
220
- const prefix = `vibe:${appId}:`;
221
- return {
222
- async get(key) {
223
- return localStorage.getItem(`${prefix}${key}`);
224
- },
225
- async set(key, value) {
226
- localStorage.setItem(`${prefix}${key}`, value);
227
- },
228
- async remove(key) {
229
- localStorage.removeItem(`${prefix}${key}`);
230
- },
231
- async keys() {
232
- const result = [];
233
- for (let i = 0;i < localStorage.length; i++) {
234
- const k = localStorage.key(i);
235
- if (k?.startsWith(prefix)) {
236
- result.push(k.slice(prefix.length));
237
- }
238
- }
239
- return result;
240
- }
241
- };
242
- }
243
-
244
- // src/core/event-bus.ts
245
- var VIBE_EVENT_PREFIX = "vibe:";
246
- var sharedBus = null;
247
- function getEventBus() {
248
- if (!sharedBus) {
249
- sharedBus = new EventTarget;
250
- }
251
- return sharedBus;
252
- }
253
- function prefixedEventName(event) {
254
- if (event.startsWith(VIBE_EVENT_PREFIX)) {
255
- return event;
256
- }
257
- return `${VIBE_EVENT_PREFIX}${event}`;
258
- }
259
- function emitEvent(event, payload, sourceAppId) {
260
- const bus = getEventBus();
261
- const detail = { payload, sourceAppId };
262
- bus.dispatchEvent(new CustomEvent(prefixedEventName(event), { detail }));
263
- }
264
- function onEvent(event, handler) {
265
- const bus = getEventBus();
266
- const prefixed = prefixedEventName(event);
267
- const listener = (e) => {
268
- handler(e.detail);
269
- };
270
- bus.addEventListener(prefixed, listener);
271
- return () => bus.removeEventListener(prefixed, listener);
272
- }
273
- function onceEvent(event, handler) {
274
- const bus = getEventBus();
275
- const prefixed = prefixedEventName(event);
276
- const listener = (e) => {
277
- handler(e.detail);
278
- };
279
- bus.addEventListener(prefixed, listener, { once: true });
280
- return () => bus.removeEventListener(prefixed, listener);
281
- }
282
-
283
- // src/adapters/events.ts
284
- function createEventsAdapter(appId) {
285
- return {
286
- emit(event, payload) {
287
- emitEvent(event, payload ?? {}, appId);
288
- },
289
- on(event, handler) {
290
- return onEvent(event, (detail) => handler(detail.payload));
291
- },
292
- once(event, handler) {
293
- return onceEvent(event, (detail) => handler(detail.payload));
294
- }
295
- };
296
- }
297
-
298
- // src/core/permissions.ts
299
- function createDeniedProxy(capability) {
300
- return new Proxy({}, {
301
- get(_target, prop) {
302
- if (typeof prop === "symbol")
303
- return;
304
- throw new Error(`Access denied: ${capability}.${prop} is not permitted. Add '${capability}' to your manifest.json permissions.`);
305
- }
306
- });
307
- }
308
- function filterByPermissions(bridge, permissions) {
309
- const allCapabilities = [
310
- "auth",
311
- "api",
312
- "navigation",
313
- "theme",
314
- "toast",
315
- "storage",
316
- "events"
317
- ];
318
- const filtered = {};
319
- for (const cap of allCapabilities) {
320
- if (permissions.includes(cap)) {
321
- filtered[cap] = bridge[cap];
322
- } else {
323
- filtered[cap] = createDeniedProxy(cap);
324
- }
325
- }
326
- return filtered;
327
- }
328
-
329
- // src/hooks/useVibeBridge.ts
330
- function useVibeBridge(options) {
331
- const { appId, permissions, overrides } = options;
332
- const { data: session } = useSession();
333
- const router = useRouter();
334
- return useMemo(() => {
335
- const authAdapter = overrides?.auth ?? createAuthAdapter({ session: session ?? null });
336
- const fullBridge = {
337
- auth: authAdapter,
338
- api: overrides?.api ?? createApiAdapter({ getToken: () => authAdapter.getToken() }),
339
- navigation: overrides?.navigation ?? createNavigationAdapter({ push: (path) => router.push(path) }),
340
- theme: overrides?.theme ?? createThemeAdapter(),
341
- toast: overrides?.toast ?? createToastAdapter({ toast }),
342
- storage: overrides?.storage ?? createStorageAdapter(appId),
343
- events: overrides?.events ?? createEventsAdapter(appId)
344
- };
345
- return filterByPermissions(fullBridge, permissions);
346
- }, [session, router, appId, permissions, overrides]);
347
- }
348
-
349
- // src/components/VibeErrorBoundary.tsx
350
- import { Component } from "react";
351
-
352
- class VibeErrorBoundary extends Component {
353
- constructor(props) {
354
- super(props);
355
- this.state = { error: null };
356
- }
357
- static getDerivedStateFromError(error) {
358
- return { error };
359
- }
360
- componentDidCatch(error, _info) {
361
- this.props.onError?.(error);
362
- }
363
- render() {
364
- if (this.state.error) {
365
- return this.props.fallback(this.state.error);
366
- }
367
- return this.props.children;
368
- }
369
- }
370
-
371
- // src/components/VibeHost.tsx
372
- import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
373
- function VibeHostInner({
374
- manifest,
375
- src,
376
- integrity,
377
- fallback,
378
- errorFallback,
379
- adapters,
380
- onReady,
381
- onError,
382
- className,
383
- style
384
- }) {
385
- const containerRef = useRef(null);
386
- const elementRef = useRef(null);
387
- const readyFiredRef = useRef(false);
388
- const { status, error: loadError } = useVibeLoader(src, manifest.id, integrity);
389
- const bridge = useVibeBridge({
390
- appId: manifest.id,
391
- permissions: manifest.permissions,
392
- overrides: adapters
393
- });
394
- useEffect2(() => {
395
- if (loadError) {
396
- onError?.(loadError);
397
- }
398
- }, [loadError, onError]);
399
- useEffect2(() => {
400
- const container = containerRef.current;
401
- if (status !== "ready" || !container)
402
- return;
403
- const el = document.createElement(manifest.id);
404
- el.bridge = bridge;
405
- container.appendChild(el);
406
- elementRef.current = el;
407
- if (!readyFiredRef.current) {
408
- readyFiredRef.current = true;
409
- onReady?.();
410
- }
411
- return () => {
412
- if (container.contains(el)) {
413
- container.removeChild(el);
414
- }
415
- elementRef.current = null;
416
- };
417
- }, [status, manifest.id]);
418
- useEffect2(() => {
419
- if (elementRef.current) {
420
- elementRef.current.bridge = bridge;
421
- }
422
- }, [bridge]);
423
- if (status === "error" && loadError) {
424
- if (errorFallback) {
425
- return /* @__PURE__ */ jsxDEV(Fragment, {
426
- children: errorFallback(loadError)
427
- }, undefined, false, undefined, this);
428
- }
429
- return null;
430
- }
431
- if (status === "loading") {
432
- return /* @__PURE__ */ jsxDEV(Fragment, {
433
- children: fallback ?? null
434
- }, undefined, false, undefined, this);
435
- }
436
- return /* @__PURE__ */ jsxDEV("div", {
437
- ref: containerRef,
438
- className,
439
- style
440
- }, undefined, false, undefined, this);
441
- }
442
- function VibeHost(props) {
443
- const defaultErrorFallback = useCallback((error) => {
444
- props.onError?.(error);
445
- if (props.errorFallback) {
446
- return /* @__PURE__ */ jsxDEV(Fragment, {
447
- children: props.errorFallback(error)
448
- }, undefined, false, undefined, this);
449
- }
450
- return null;
451
- }, [props.onError, props.errorFallback]);
452
- return /* @__PURE__ */ jsxDEV(VibeErrorBoundary, {
453
- fallback: defaultErrorFallback,
454
- onError: props.onError,
455
- children: /* @__PURE__ */ jsxDEV(VibeHostInner, {
456
- ...props
457
- }, undefined, false, undefined, this)
458
- }, undefined, false, undefined, this);
459
- }
460
- // src/hooks/useVibeEvents.ts
461
- import { useEffect as useEffect3 } from "react";
462
- function useVibeEvents(event, handler) {
463
- useEffect3(() => {
464
- return onEvent(event, (detail) => {
465
- handler(detail.payload, detail.sourceAppId);
466
- });
467
- }, [event, handler]);
468
- }
469
- export {
470
- useVibeLoader,
471
- useVibeEvents,
472
- useVibeBridge,
473
- setSharedDependencies,
474
- setAllowedOrigins,
475
- getEventBus,
476
- filterByPermissions,
477
- emitEvent,
478
- createToastAdapter,
479
- createThemeAdapter,
480
- createStorageAdapter,
481
- createNavigationAdapter,
482
- createEventsAdapter,
483
- createAuthAdapter,
484
- createApiAdapter,
485
- VibeHost,
486
- VibeErrorBoundary
487
- };
1
+ import{useRef as H,useEffect as q,useCallback as ut}from"react";import{useState as R,useEffect as tt}from"react";var T=new Set,Z=new Set(["https:","http:"]),B=null,k=null,V=!1;function O(t){if(V){console.warn("[vibe-bridge] setSharedDependencies() called after an import map was already injected. The new entries will be ignored.");return}k={...t}}function M(){if(V||!k)return;let t=document.createElement("script");t.type="importmap",t.textContent=JSON.stringify({imports:k}),document.head.appendChild(t),V=!0}function F(t){B=new Set(t.map((e)=>{return new URL(e).origin}))}function j(t){let e;try{e=new URL(t,window.location.href)}catch{return`Invalid vibe app script URL: ${t}`}if(!Z.has(e.protocol))return`Refused to load vibe app script with disallowed protocol "${e.protocol}": ${t}`;if(B&&!B.has(e.origin))return`Refused to load vibe app script from untrusted origin "${e.origin}": ${t}. Allowed origins: ${[...B].join(", ")}`;return null}function K(t,e){let r=j(t);if(r)return Promise.reject(Error(r));if(T.has(t))return Promise.resolve();return new Promise((o,n)=>{if(document.querySelector(`script[src="${CSS.escape(t)}"]`)){T.add(t),o();return}let a=document.createElement("script");if(a.type="module",a.src=t,e?.integrity)a.integrity=e.integrity,a.crossOrigin=e.crossOrigin??"anonymous";a.onload=()=>{T.add(t),o()},a.onerror=()=>{let s=e?.integrity?" This may be an SRI integrity mismatch — the bundle content doesn't match the expected hash.":"";n(Error(`Failed to load vibe app script: ${t}.${s}`))},M(),document.head.appendChild(a)})}function Q(t){if(customElements.get(t))return Promise.resolve();return customElements.whenDefined(t).then(()=>{return})}function w(t,e,r){let[o,n]=R("loading"),[i,a]=R(null);return tt(()=>{let s=!1;return n("loading"),a(null),K(t,r?{integrity:r}:void 0).then(()=>Q(e)).then(()=>{if(!s)n("ready")}).catch((u)=>{if(!s){let p=u instanceof Error?u:Error(`Failed to load vibe app: ${e}`);a(p),n("error")}}),()=>{s=!0}},[t,e,r]),{status:o,error:i}}import{useMemo as ot}from"react";import{useSession as nt}from"next-auth/react";import{useRouter as it}from"next/navigation";import{toast as at}from"sonner";function D(t){return{getUser(){let e=t.session?.user;return{id:e?.email??"unknown",name:e?.name??"Unknown User",email:e?.email??void 0}},async getToken(){let e=t.session?.idToken;if(!e)throw Error("No authentication token available.");return e}}}function $(t){return{async fetch(e,r){let o=e.startsWith("/api/")?e:`/api/${e.replace(/^\//,"")}`,n=await t.getToken(),i=new Headers(r?.headers);return i.set("Authorization",`Bearer ${n}`),globalThis.fetch(o,{...r,headers:i})}}}function S(t){return{navigate(e){if(/^https?:\/\//i.test(e)||/^\/\//.test(e)||/^(javascript|data|blob|vbscript):/i.test(e))throw Error(`Navigation to external URLs is not allowed: ${e}`);t.push(e)}}}function C(){return{current(){let t=document.documentElement;if(t.classList.contains("dark"))return"dark";if(t.classList.contains("light"))return"light";let e=t.getAttribute("data-theme");if(e==="dark")return"dark";if(e==="light")return"light";if(globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches)return"dark";return"light"}}}function l(t){return{show(e,r){switch(r?.type??"info"){case"success":t.toast.success(e);break;case"error":t.toast.error(e);break;case"info":t.toast.info(e);break;default:t.toast(e)}}}}function _(t){let e=`vibe:${t}:`;return{async get(r){return localStorage.getItem(`${e}${r}`)},async set(r,o){localStorage.setItem(`${e}${r}`,o)},async remove(r){localStorage.removeItem(`${e}${r}`)},async keys(){let r=[];for(let o=0;o<localStorage.length;o++){let n=localStorage.key(o);if(n?.startsWith(e))r.push(n.slice(e.length))}return r}}}var U=null;function x(){if(!U)U=new EventTarget;return U}function N(t){if(t.startsWith("vibe:"))return t;return`vibe:${t}`}function I(t,e,r){let o=x(),n={payload:e,sourceAppId:r};o.dispatchEvent(new CustomEvent(N(t),{detail:n}))}function h(t,e){let r=x(),o=N(t),n=(i)=>{e(i.detail)};return r.addEventListener(o,n),()=>r.removeEventListener(o,n)}function Y(t,e){let r=x(),o=N(t),n=(i)=>{e(i.detail)};return r.addEventListener(o,n,{once:!0}),()=>r.removeEventListener(o,n)}function X(t){return{emit(e,r){I(e,r??{},t)},on(e,r){return h(e,(o)=>r(o.payload))},once(e,r){return Y(e,(o)=>r(o.payload))}}}async function*et(t,e){let r=new TextDecoder("utf-8"),o="",n,i,a=[];function s(){if(a.length===0&&n===void 0&&i===void 0)return null;let p=a.join(`
2
+ `),m;try{m=JSON.parse(p)}catch{m={raw:p}}let f={kind:n??m.kind?.toString()??"unknown",...m};return n=void 0,i=void 0,a=[],f}while(!0){if(e?.aborted)break;let{value:p,done:m}=await t.read();if(m)break;let f=r.decode(p,{stream:!0}),E=(o+f).split(`
3
+ `);o=E.pop()??"";for(let v of E){let d=v.endsWith("\r")?v.slice(0,-1):v;if(d===""){let J=s();if(J)yield J;continue}if(d.startsWith(":"))continue;let g=d.indexOf(":"),c=g===-1?d:d.slice(0,g),A=g===-1?"":d.slice(g+1);if(A.startsWith(" "))A=A.slice(1);if(c==="data")a.push(A);else if(c==="event")n=A;else if(c==="id")i=A}}let u=s();if(u)yield u}function L(t){let e=t.chatEndpoint??"/api/chat";return{send(r,o,n){let i=new AbortController;if(n?.signal)n.signal.addEventListener("abort",()=>i.abort(),{once:!0});let a=async function*(){let s=await t.getToken(),u={serverUrl:r,parts:o};if(n?.contextId)u.contextId=n.contextId;if(n?.messageId)u.messageId=n.messageId;if(n?.metadata!==void 0)u.metadata=n.metadata;let p=await globalThis.fetch(e,{method:"POST",headers:{Accept:"text/event-stream",Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify(u),signal:i.signal});if(!p.ok)throw Error(`Chat stream failed: ${p.status} ${p.statusText}`);if(!p.body)throw Error("Chat response has no body");let m=p.body.getReader();yield*et(m,i.signal)}();return{[Symbol.asyncIterator]:()=>a,abort:()=>i.abort()}}}}function rt(t){return new Proxy({},{get(e,r){if(typeof r==="symbol")return;throw Error(`Access denied: ${t}.${r} is not permitted. Add '${t}' to your manifest.json permissions.`)}})}function W(t,e){let r=["auth","api","navigation","theme","toast","storage","events","chat"],o={};for(let n of r)if(e.includes(n))o[n]=t[n];else o[n]=rt(n);return o}function z(t){let{appId:e,permissions:r,overrides:o}=t,{data:n}=nt(),i=it();return ot(()=>{let a=o?.auth??D({session:n??null}),s={auth:a,api:o?.api??$({getToken:()=>a.getToken()}),navigation:o?.navigation??S({push:(u)=>i.push(u)}),theme:o?.theme??C(),toast:o?.toast??l({toast:at}),storage:o?.storage??_(e),events:o?.events??X(e),chat:o?.chat??L({getToken:()=>a.getToken()})};return W(s,r)},[n,i,e,r,o])}import{Component as st}from"react";class b extends st{constructor(t){super(t);this.state={error:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t,e){this.props.onError?.(t)}render(){if(this.state.error)return this.props.fallback(this.state.error);return this.props.children}}import{jsx as y,Fragment as G}from"react/jsx-runtime";function pt({manifest:t,src:e,integrity:r,fallback:o,errorFallback:n,adapters:i,onReady:a,onError:s,className:u,style:p}){let m=H(null),f=H(null),P=H(!1),{status:E,error:v}=w(e,t.id,r),d=z({appId:t.id,permissions:t.permissions,overrides:i});if(q(()=>{if(v)s?.(v)},[v,s]),q(()=>{let g=m.current;if(E!=="ready"||!g)return;let c=document.createElement(t.id);if(c.bridge=d,g.appendChild(c),f.current=c,!P.current)P.current=!0,a?.();return()=>{if(g.contains(c))g.removeChild(c);f.current=null}},[E,t.id]),q(()=>{if(f.current)f.current.bridge=d},[d]),E==="error"&&v){if(n)return y(G,{children:n(v)});return null}if(E==="loading")return y(G,{children:o??null});return y("div",{ref:m,className:u,style:p})}function mt(t){let e=ut((r)=>{if(t.onError?.(r),t.errorFallback)return y(G,{children:t.errorFallback(r)});return null},[t.onError,t.errorFallback]);return y(b,{fallback:e,onError:t.onError,children:y(pt,{...t})})}import{useEffect as dt}from"react";function vt(t,e){dt(()=>{return h(t,(r)=>{e(r.payload,r.sourceAppId)})},[t,e])}export{w as useVibeLoader,vt as useVibeEvents,z as useVibeBridge,O as setSharedDependencies,F as setAllowedOrigins,x as getEventBus,W as filterByPermissions,I as emitEvent,l as createToastAdapter,C as createThemeAdapter,_ as createStorageAdapter,S as createNavigationAdapter,X as createEventsAdapter,L as createChatAdapter,D as createAuthAdapter,$ as createApiAdapter,mt as VibeHost,b as VibeErrorBoundary};
package/package.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "name": "@aiworkbench/vibe-bridge",
3
- "version": "0.0.5",
4
- "publishConfig": { "access": "public" },
3
+ "version": "0.0.7",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
5
7
  "type": "module",
6
8
  "main": "dist/index.js",
7
9
  "types": "dist/index.d.ts",
@@ -12,13 +14,16 @@
12
14
  }
13
15
  },
14
16
  "scripts": {
15
- "build": "bun build ./src/index.ts --outdir ./dist --target=browser --external react --external react-dom --external next --external next-auth --external next-auth/react --external next/navigation --external sonner && tsc --emitDeclarationOnly",
17
+ "build": "bun build ./src/index.ts --outdir ./dist --target=browser --production --external react --external react-dom --external next --external next-auth --external next-auth/react --external next/navigation --external sonner && tsc --emitDeclarationOnly",
16
18
  "dev": "tsc --watch --emitDeclarationOnly",
19
+ "test": "bun test",
17
20
  "type-check": "tsc --noEmit",
18
21
  "clean": "rm -rf dist",
19
22
  "prepublishOnly": "bun run build"
20
23
  },
21
- "files": ["dist"],
24
+ "files": [
25
+ "dist"
26
+ ],
22
27
  "dependencies": {
23
28
  "@aiworkbench/vibe-types": "^0.0.4"
24
29
  },
@@ -39,4 +44,4 @@
39
44
  "@types/react-dom": "^19.2.3",
40
45
  "sonner": "^2.0.0"
41
46
  }
42
- }
47
+ }