@lovable-widgets/core 0.1.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.
@@ -0,0 +1,3 @@
1
+ import type { WidgetContextValue } from './types';
2
+ export declare const WidgetContext: import("react").Context<WidgetContextValue | null>;
3
+ export declare function useWidgetContext(): WidgetContextValue | null;
@@ -0,0 +1,7 @@
1
+ import type { CreateWidgetConfig, WidgetDefinition } from './types';
2
+ declare global {
3
+ interface Window {
4
+ __LOVABLE_WIDGET_SECRETS__?: Record<string, string>;
5
+ }
6
+ }
7
+ export declare function createWidget<P extends Record<string, any>>(config: CreateWidgetConfig<P>): WidgetDefinition<P>;
@@ -0,0 +1,19 @@
1
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
2
+ interface Props {
3
+ widgetName: string;
4
+ onError?: (error: Error, errorInfo?: {
5
+ componentStack?: string;
6
+ }) => void;
7
+ children: ReactNode;
8
+ }
9
+ interface State {
10
+ hasError: boolean;
11
+ error: Error | null;
12
+ }
13
+ export declare class WidgetErrorBoundary extends Component<Props, State> {
14
+ state: State;
15
+ static getDerivedStateFromError(error: Error): State;
16
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
17
+ render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
18
+ }
19
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { WidgetEventType, WidgetEvent } from './types';
2
+ export declare function emitWidgetEvent<T = any>(widgetName: string, type: WidgetEventType, payload?: T): void;
3
+ export declare function onWidgetEvent<T = any>(type: WidgetEventType | '*', callback: (event: WidgetEvent<T>) => void): () => void;
@@ -0,0 +1,7 @@
1
+ import type { ThemeTokens, WidgetEventType, WidgetEvent } from './types';
2
+ export declare function useWidgetTheme(): ThemeTokens;
3
+ export declare function useWidgetId(prefix?: string): string;
4
+ export declare function useWidgetSecrets(): Record<string, string>;
5
+ export declare function useWidgetStatus(): 'initializing' | 'ready' | 'missing-secrets' | 'unknown';
6
+ export declare function useWidgetEmit(): (type: WidgetEventType, payload?: any) => void;
7
+ export declare function useWidgetListener<T = any>(type: WidgetEventType | '*', callback: (event: WidgetEvent<T>) => void): void;
@@ -0,0 +1,9 @@
1
+ export { createWidget } from './createWidget';
2
+ export { useWidgetContext } from './context';
3
+ export { useWidgetTheme, useWidgetId, useWidgetSecrets, useWidgetStatus, useWidgetEmit, useWidgetListener } from './hooks';
4
+ export { registerWidget, getWidget, listWidgets, unregisterWidget, clearRegistry } from './registry';
5
+ export { emitWidgetEvent, onWidgetEvent } from './events';
6
+ export { WidgetErrorBoundary } from './errorBoundary';
7
+ export { provideSecrets } from './provideSecrets';
8
+ export { mountWidget, unmountWidget } from './mount';
9
+ export type { WidgetSecretDef, WidgetMeta, WidgetConfig, WidgetPropDef, WidgetDefinition, CreateWidgetConfig, WidgetLifecycle, ThemeTokens, WidgetContextValue, WidgetEventType, WidgetEvent } from './types';
@@ -0,0 +1 @@
1
+ var ie=Object.create;var p=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,ge=Object.prototype.hasOwnProperty;var ce=(e,t,r)=>t in e?p(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var ue=(e,t)=>{for(var r in t)p(e,r,{get:t[r],enumerable:!0})},b=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of se(t))!ge.call(e,o)&&o!==r&&p(e,o,{get:()=>t[o],enumerable:!(n=de(t,o))||n.enumerable});return e};var me=(e,t,r)=>(r=e!=null?ie(ae(e)):{},b(t||!e||!e.__esModule?p(r,"default",{value:e,enumerable:!0}):r,e)),fe=e=>b(p({},"__esModule",{value:!0}),e);var M=(e,t,r)=>ce(e,typeof t!="symbol"?t+"":t,r);var We={};ue(We,{WidgetErrorBoundary:()=>c,clearRegistry:()=>X,createWidget:()=>B,emitWidgetEvent:()=>d,getWidget:()=>w,listWidgets:()=>q,mountWidget:()=>Z,onWidgetEvent:()=>y,provideSecrets:()=>J,registerWidget:()=>K,unmountWidget:()=>h,unregisterWidget:()=>U,useWidgetContext:()=>g,useWidgetEmit:()=>H,useWidgetId:()=>$,useWidgetListener:()=>z,useWidgetSecrets:()=>G,useWidgetStatus:()=>O,useWidgetTheme:()=>A});module.exports=fe(We);var i=require("react");var v=require("react"),_=(0,v.createContext)(null);function g(){return(0,v.useContext)(_)}var I=require("react");var C="lovable-widget:";function d(e,t,r){if(typeof window>"u")return;let n={widget:e,type:t,payload:r,timestamp:Date.now()};window.dispatchEvent(new CustomEvent(`${C}${t}`,{detail:n})),window.dispatchEvent(new CustomEvent(`${C}*`,{detail:n}))}function y(e,t){if(typeof window>"u")return()=>{};let r=`${C}${e}`,n=o=>{t(o.detail)};return window.addEventListener(r,n),()=>window.removeEventListener(r,n)}var P=require("react/jsx-runtime"),c=class extends I.Component{constructor(){super(...arguments);M(this,"state",{hasError:!1,error:null})}static getDerivedStateFromError(r){return{hasError:!0,error:r}}componentDidCatch(r,n){let{widgetName:o,onError:l}=this.props;d(o,"error",{message:r.message,stack:r.stack,componentStack:n.componentStack}),l?.(r,{componentStack:n.componentStack??void 0})}render(){return this.state.hasError?(0,P.jsxs)("div",{"data-lw-error":this.props.widgetName,children:['Widget "',this.props.widgetName,'" encountered an error.']}):this.props.children}};var s=require("react/jsx-runtime");function pe({lifecycle:e,widgetName:t}){return(0,i.useEffect)(()=>(e.onMount?.(),d(t,"ready"),()=>{e.onUnmount?.()}),[]),null}function B(e){let{meta:t,component:r,propDefs:n,secrets:o=[],config:l={},lifecycle:x={}}=e,L=(0,i.forwardRef)((j,R)=>{let{className:D="",...ee}=j,[te,re]=(0,i.useState)(o.filter(()=>!1)),[T,ne]=(0,i.useState)({}),[E,k]=(0,i.useState)("initializing");(0,i.useEffect)(()=>{if(o.length===0){k("ready");return}let u=typeof window<"u"&&window.__LOVABLE_WIDGET_SECRETS__||{},m=[],N={};for(let f of o){let V=f.envKey||f.name;u[V]?N[f.name]=u[V]:f.required!==!1&&m.push(f)}ne(N),re(m),k(m.length>0?"missing-secrets":"ready"),m.length>0&&d(t.name,"missing-secrets",{secrets:m})},[]);let oe=(0,i.useMemo)(()=>({meta:t,secrets:T,config:l,status:E}),[T,E]);return E==="missing-secrets"?(0,s.jsxs)("div",{ref:R,className:D||void 0,"data-lw-widget":t.name,"data-lw-version":t.version,"data-lw-status":"missing-secrets",children:['Widget "',t.name,'" requires secrets: ',te.map(u=>u.envKey||u.name).join(", ")]}):(0,s.jsx)(_.Provider,{value:oe,children:(0,s.jsx)(c,{widgetName:t.name,onError:x.onError,children:(0,s.jsxs)("div",{ref:R,className:D||void 0,"data-lw-widget":t.name,"data-lw-version":t.version,"data-lw-status":E,children:[(0,s.jsx)(pe,{lifecycle:x,widgetName:t.name}),(0,s.jsx)(r,{...ee,__widgetSecrets:T})]})})})});return L.displayName=`LovableWidget(${t.name})`,{component:L,meta:t,propDefs:n,secrets:o,config:l,lifecycle:x,__LOVABLE_WIDGET__:!0}}var a=require("react");function A(){return(0,a.useMemo)(()=>{let e=t=>typeof window>"u"?"":getComputedStyle(document.documentElement).getPropertyValue("--"+t).trim();return{background:e("background"),foreground:e("foreground"),card:e("card"),cardForeground:e("card-foreground"),primary:e("primary"),primaryForeground:e("primary-foreground"),secondary:e("secondary"),secondaryForeground:e("secondary-foreground"),muted:e("muted"),mutedForeground:e("muted-foreground"),accent:e("accent"),accentForeground:e("accent-foreground"),destructive:e("destructive"),destructiveForeground:e("destructive-foreground"),border:e("border"),ring:e("ring"),radius:e("radius")}},[])}var F=0;function $(e){return(0,a.useMemo)(()=>(F+=1,(e||"lw")+"-"+F),[e])}function G(){let e=g();return e?e.secrets:{}}function O(){let e=g();return e?e.status:"unknown"}function H(){let t=g()?.meta.name??"unknown";return(0,a.useCallback)((r,n)=>{d(t,r,n)},[t])}function z(e,t){(0,a.useEffect)(()=>y(e,t),[e,t])}var W=new Map;function K(e){if(!e.__LOVABLE_WIDGET__)throw new Error("registerWidget: argument must be a WidgetDefinition created by createWidget()");W.set(e.meta.name,e)}function w(e){return W.get(e)}function q(){return Array.from(W.values())}function U(e){return W.delete(e)}function X(){W.clear()}function J(e){typeof window<"u"&&(window.__LOVABLE_WIDGET_SECRETS__={...window.__LOVABLE_WIDGET_SECRETS__||{},...e})}var Q=me(require("react"),1),Y=require("react-dom/client");var S=new WeakMap;function Z(e,t,r={}){let n=w(t);if(!n)throw new Error(`mountWidget: widget "${t}" is not registered.`);h(e);let o=(0,Y.createRoot)(e);o.render(Q.default.createElement(n.component,r)),S.set(e,o)}function h(e){let t=S.get(e);t&&(t.unmount(),S.delete(e))}
@@ -0,0 +1 @@
1
+ var G=Object.defineProperty;var O=(e,t,r)=>t in e?G(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var D=(e,t,r)=>O(e,typeof t!="symbol"?t+"":t,r);import{forwardRef as U,useState as w,useEffect as N,useMemo as X}from"react";import{createContext as H,useContext as z}from"react";var E=H(null);function g(){return z(E)}import{Component as K}from"react";var v="lovable-widget:";function i(e,t,r){if(typeof window>"u")return;let n={widget:e,type:t,payload:r,timestamp:Date.now()};window.dispatchEvent(new CustomEvent(`${v}${t}`,{detail:n})),window.dispatchEvent(new CustomEvent(`${v}*`,{detail:n}))}function y(e,t){if(typeof window>"u")return()=>{};let r=`${v}${e}`,n=o=>{t(o.detail)};return window.addEventListener(r,n),()=>window.removeEventListener(r,n)}import{jsxs as q}from"react/jsx-runtime";var c=class extends K{constructor(){super(...arguments);D(this,"state",{hasError:!1,error:null})}static getDerivedStateFromError(r){return{hasError:!0,error:r}}componentDidCatch(r,n){let{widgetName:o,onError:m}=this.props;i(o,"error",{message:r.message,stack:r.stack,componentStack:n.componentStack}),m?.(r,{componentStack:n.componentStack??void 0})}render(){return this.state.hasError?q("div",{"data-lw-error":this.props.widgetName,children:['Widget "',this.props.widgetName,'" encountered an error.']}):this.props.children}};import{jsx as p,jsxs as k}from"react/jsx-runtime";function J({lifecycle:e,widgetName:t}){return N(()=>(e.onMount?.(),i(t,"ready"),()=>{e.onUnmount?.()}),[]),null}function Q(e){let{meta:t,component:r,propDefs:n,secrets:o=[],config:m={},lifecycle:W={}}=e,_=U((I,C)=>{let{className:S="",...P}=I,[B,F]=w(o.filter(()=>!1)),[l,A]=w({}),[f,h]=w("initializing");N(()=>{if(o.length===0){h("ready");return}let d=typeof window<"u"&&window.__LOVABLE_WIDGET_SECRETS__||{},s=[],L={};for(let a of o){let R=a.envKey||a.name;d[R]?L[a.name]=d[R]:a.required!==!1&&s.push(a)}A(L),F(s),h(s.length>0?"missing-secrets":"ready"),s.length>0&&i(t.name,"missing-secrets",{secrets:s})},[]);let $=X(()=>({meta:t,secrets:l,config:m,status:f}),[l,f]);return f==="missing-secrets"?k("div",{ref:C,className:S||void 0,"data-lw-widget":t.name,"data-lw-version":t.version,"data-lw-status":"missing-secrets",children:['Widget "',t.name,'" requires secrets: ',B.map(d=>d.envKey||d.name).join(", ")]}):p(E.Provider,{value:$,children:p(c,{widgetName:t.name,onError:W.onError,children:k("div",{ref:C,className:S||void 0,"data-lw-widget":t.name,"data-lw-version":t.version,"data-lw-status":f,children:[p(J,{lifecycle:W,widgetName:t.name}),p(r,{...P,__widgetSecrets:l})]})})})});return _.displayName=`LovableWidget(${t.name})`,{component:_,meta:t,propDefs:n,secrets:o,config:m,lifecycle:W,__LOVABLE_WIDGET__:!0}}import{useMemo as b,useEffect as Y,useCallback as Z}from"react";function j(){return b(()=>{let e=t=>typeof window>"u"?"":getComputedStyle(document.documentElement).getPropertyValue("--"+t).trim();return{background:e("background"),foreground:e("foreground"),card:e("card"),cardForeground:e("card-foreground"),primary:e("primary"),primaryForeground:e("primary-foreground"),secondary:e("secondary"),secondaryForeground:e("secondary-foreground"),muted:e("muted"),mutedForeground:e("muted-foreground"),accent:e("accent"),accentForeground:e("accent-foreground"),destructive:e("destructive"),destructiveForeground:e("destructive-foreground"),border:e("border"),ring:e("ring"),radius:e("radius")}},[])}var V=0;function ee(e){return b(()=>(V+=1,(e||"lw")+"-"+V),[e])}function te(){let e=g();return e?e.secrets:{}}function re(){let e=g();return e?e.status:"unknown"}function ne(){let t=g()?.meta.name??"unknown";return Z((r,n)=>{i(t,r,n)},[t])}function oe(e,t){Y(()=>y(e,t),[e,t])}var u=new Map;function ie(e){if(!e.__LOVABLE_WIDGET__)throw new Error("registerWidget: argument must be a WidgetDefinition created by createWidget()");u.set(e.meta.name,e)}function x(e){return u.get(e)}function de(){return Array.from(u.values())}function se(e){return u.delete(e)}function ae(){u.clear()}function ge(e){typeof window<"u"&&(window.__LOVABLE_WIDGET_SECRETS__={...window.__LOVABLE_WIDGET_SECRETS__||{},...e})}import ce from"react";import{createRoot as ue}from"react-dom/client";var T=new WeakMap;function me(e,t,r={}){let n=x(t);if(!n)throw new Error(`mountWidget: widget "${t}" is not registered.`);M(e);let o=ue(e);o.render(ce.createElement(n.component,r)),T.set(e,o)}function M(e){let t=T.get(e);t&&(t.unmount(),T.delete(e))}export{c as WidgetErrorBoundary,ae as clearRegistry,Q as createWidget,i as emitWidgetEvent,x as getWidget,de as listWidgets,me as mountWidget,y as onWidgetEvent,ge as provideSecrets,ie as registerWidget,M as unmountWidget,se as unregisterWidget,g as useWidgetContext,ne as useWidgetEmit,ee as useWidgetId,oe as useWidgetListener,te as useWidgetSecrets,re as useWidgetStatus,j as useWidgetTheme};
@@ -0,0 +1,2 @@
1
+ export declare function mountWidget(container: HTMLElement, widgetName: string, props?: Record<string, any>): void;
2
+ export declare function unmountWidget(container: HTMLElement): void;
@@ -0,0 +1 @@
1
+ export declare function provideSecrets(secretMap: Record<string, string>): void;
@@ -0,0 +1,6 @@
1
+ import type { WidgetDefinition } from './types';
2
+ export declare function registerWidget(definition: WidgetDefinition): void;
3
+ export declare function getWidget(name: string): WidgetDefinition | undefined;
4
+ export declare function listWidgets(): WidgetDefinition[];
5
+ export declare function unregisterWidget(name: string): boolean;
6
+ export declare function clearRegistry(): void;
@@ -0,0 +1,91 @@
1
+ import { ComponentType } from 'react';
2
+ export interface WidgetSecretDef {
3
+ name: string;
4
+ envKey: string;
5
+ description: string;
6
+ required?: boolean;
7
+ acquireUrl?: string;
8
+ service?: string;
9
+ }
10
+ export interface WidgetMeta {
11
+ name: string;
12
+ version: string;
13
+ description: string;
14
+ author: string;
15
+ category: 'ui' | 'commerce' | 'navigation' | 'social-proof' | 'data' | 'forms' | 'media' | 'layout' | 'ai' | 'analytics' | 'communication';
16
+ tags: string[];
17
+ icon?: string;
18
+ license?: string;
19
+ repository?: string;
20
+ homepage?: string;
21
+ }
22
+ export interface WidgetConfig {
23
+ ssr?: boolean;
24
+ minCoreVersion?: string;
25
+ sandbox?: 'strict' | 'permissive';
26
+ cssPrefix?: string;
27
+ }
28
+ export interface WidgetPropDef {
29
+ name: string;
30
+ type: string;
31
+ required: boolean;
32
+ default?: string;
33
+ description: string;
34
+ options?: string[];
35
+ }
36
+ export interface WidgetLifecycle {
37
+ onMount?: () => void;
38
+ onUnmount?: () => void;
39
+ onError?: (error: Error, errorInfo?: {
40
+ componentStack?: string;
41
+ }) => void;
42
+ }
43
+ export interface WidgetDefinition<P = any> {
44
+ component: ComponentType<P>;
45
+ meta: WidgetMeta;
46
+ propDefs: WidgetPropDef[];
47
+ secrets: WidgetSecretDef[];
48
+ config: WidgetConfig;
49
+ lifecycle: WidgetLifecycle;
50
+ __LOVABLE_WIDGET__: true;
51
+ }
52
+ export interface CreateWidgetConfig<P> {
53
+ meta: WidgetMeta;
54
+ component: ComponentType<P>;
55
+ propDefs: WidgetPropDef[];
56
+ secrets?: WidgetSecretDef[];
57
+ config?: WidgetConfig;
58
+ lifecycle?: WidgetLifecycle;
59
+ }
60
+ export interface ThemeTokens {
61
+ background: string;
62
+ foreground: string;
63
+ card: string;
64
+ cardForeground: string;
65
+ primary: string;
66
+ primaryForeground: string;
67
+ secondary: string;
68
+ secondaryForeground: string;
69
+ muted: string;
70
+ mutedForeground: string;
71
+ accent: string;
72
+ accentForeground: string;
73
+ destructive: string;
74
+ destructiveForeground: string;
75
+ border: string;
76
+ ring: string;
77
+ radius: string;
78
+ }
79
+ export interface WidgetContextValue {
80
+ meta: WidgetMeta;
81
+ secrets: Record<string, string>;
82
+ config: WidgetConfig;
83
+ status: 'initializing' | 'ready' | 'missing-secrets';
84
+ }
85
+ export type WidgetEventType = 'ready' | 'error' | 'state-change' | 'action' | 'missing-secrets' | 'resize';
86
+ export interface WidgetEvent<T = any> {
87
+ widget: string;
88
+ type: WidgetEventType;
89
+ payload: T;
90
+ timestamp: number;
91
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@lovable-widgets/core",
3
+ "version": "0.1.0",
4
+ "description": "Pure integration SDK for building Lovable-compatible widgets",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ },
14
+ "./styles.css": "./dist/style.css"
15
+ },
16
+ "peerDependencies": {
17
+ "react": ">=18.0.0",
18
+ "react-dom": ">=18.0.0"
19
+ },
20
+ "license": "MIT",
21
+ "author": "Lovable Widgets"
22
+ }