@momentco-ai/moment-sdk 0.3.3-dev.22 → 0.4.0-dev.25

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
@@ -91,14 +91,16 @@ Clicking the button opens a modal where the fan selects Google Calendar or Outlo
91
91
 
92
92
  Set `data-moment-trigger-type` (or `triggerType` in `open()` / `createMomentButton()`):
93
93
 
94
- | Type | Required attributes | Subscribes to |
95
- | ------------------ | ------------------------------------------------- | ------------------------------------------------------ |
96
- | `moment` (default) | `data-moment-team-slug` + `data-moment-slug` | One or more specific games/events |
97
- | `list` | `data-moment-team-slug` + `data-moment-list-slug` | A curated list of moments |
98
- | `team` | `data-moment-team-slug` | All published moments for the brand/team |
99
- | `brand-playlist` | `data-moment-team-slug` | The brand's default playlist (dynamic schedule thread) |
94
+ | Type | Required attributes | Subscribes to |
95
+ | ------------------ | ------------------------------------------------------ | ----------------------------------------------------------------- |
96
+ | `moment` (default) | `data-moment-team-slug` + `data-moment-slug` | One or more specific games/events |
97
+ | `list` | `data-moment-team-slug` + `data-moment-list-slug` | A curated list of moments |
98
+ | `team` | `data-moment-team-slug` | All published moments for the brand/team |
99
+ | `brand-playlist` | `data-moment-team-slug` | The brand's default playlist (dynamic schedule thread) |
100
+ | `playlist` | `data-moment-team-slug` + `data-moment-playlist-slugs` | One of several playlists (picker when >1 slug) |
101
+ | `schedule` | `data-moment-team-slug` | Full schedule experience — user picks a mix of playlists + events |
100
102
 
101
- For `team` and `brand-playlist`, omit moment/list/ID and source-resolution attributes — only `data-moment-team-slug` is required.
103
+ For `team`, `brand-playlist`, and `schedule`, omit moment/list/ID and source-resolution attributes — only `data-moment-team-slug` is required.
102
104
 
103
105
  ---
104
106
 
@@ -106,18 +108,19 @@ For `team` and `brand-playlist`, omit moment/list/ID and source-resolution attri
106
108
 
107
109
  Place these on any element matching the trigger selector (default: `.moment-sync-trigger`):
108
110
 
109
- | Attribute | Required | Description |
110
- | ------------------------------- | ------------ | ----------------------------------------------------------------- |
111
- | `data-moment-team-slug` | Yes | Team/brand slug provided by Moment |
112
- | `data-moment-slug` | For `moment` | Brand-level moment slug (resolved server-side) |
113
- | `data-moment-list-slug` | For `list` | List slug |
114
- | `data-moment-trigger-type` | No | `moment` (default), `list`, `team`, or `brand-playlist` |
115
- | `data-moment-ids` | No | Comma-separated moment IDs (fallback if slugs don't resolve) |
116
- | `data-moment-game-id` | No | Alias for `data-moment-ids` |
117
- | `data-moment-calendar` | No | Pre-select provider: `google` or `outlook` |
118
- | `data-api-source-id` | No | Sync-subscription source ID (pairs with external id attributes) |
119
- | `data-external-event-id` | No | Exact external id (`event_id:showing_id`) resolved against source |
120
- | `data-external-event-id-prefix` | No | Upstream event id only user picks a showing in the embed modal |
111
+ | Attribute | Required | Description |
112
+ | ------------------------------- | -------------- | ------------------------------------------------------------------------------- |
113
+ | `data-moment-team-slug` | Yes | Team/brand slug provided by Moment |
114
+ | `data-moment-slug` | For `moment` | Brand-level moment slug (resolved server-side) |
115
+ | `data-moment-list-slug` | For `list` | List slug |
116
+ | `data-moment-trigger-type` | No | `moment` (default), `list`, `team`, `brand-playlist`, `playlist`, or `schedule` |
117
+ | `data-moment-playlist-slugs` | For `playlist` | Comma-separated playlist slugs (picker shown when more than one) |
118
+ | `data-moment-ids` | No | Comma-separated moment IDs (fallback if slugs don't resolve) |
119
+ | `data-moment-game-id` | No | Alias for `data-moment-ids` |
120
+ | `data-moment-calendar` | No | Pre-select provider: `google` or `outlook` |
121
+ | `data-api-source-id` | No | Sync-subscription source ID (pairs with external id attributes) |
122
+ | `data-external-event-id` | No | Exact external id (`event_id:showing_id`) resolved against source |
123
+ | `data-external-event-id-prefix` | No | Upstream event id only — user picks a showing in the embed modal |
121
124
 
122
125
  When `data-api-source-id` is set with either `data-external-event-id` (one showing) or `data-external-event-id-prefix` (all showings for an event), Live resolves moments server-side — useful when your CMS emits native event IDs instead of Moment slugs.
123
126
 
@@ -273,7 +276,7 @@ The `onAnalytics` callback receives:
273
276
  }
274
277
  ```
275
278
 
276
- `result` is one of `success`, `error`, or `cancelled`. `provider` is `google` or `outlook` when applicable. `triggerType` is `moment`, `list`, `team`, or `brand-playlist`.
279
+ `result` is one of `success`, `error`, `cancelled`, or `partial` (schedule trigger, when only some resources synced). `provider` is `google`, `outlook`, or `apple` when applicable. `triggerType` echoes the trigger (`moment`, `list`, `team`, `brand-playlist`, `playlist`, or `schedule`). For `schedule`, an optional `counts: { requested, succeeded, failed }` reports the aggregate outcome.
277
280
 
278
281
  ---
279
282
 
@@ -1,4 +1,4 @@
1
- (function(f){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),l=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${l},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=s=>{if(s.origin!==this.opts.liveOrigin||s.source!==this.popup)return;const r=s.data;!r||typeof r!="object"||r.source!=="moment-live-embed"||r.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(r,this.opts.liveOrigin),this.opts.onResult(r),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),e.selection&&i.set("selection",e.selection),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}function x(n,e,t=typeof window<"u"?window.location.origin:""){const i=new URLSearchParams;return i.set("teamSlug",e.teamSlug),e.momentSlug&&i.set("momentSlug",e.momentSlug),e.listSlug&&i.set("listSlug",e.listSlug),i.set("triggerType",e.triggerType),e.ids?.length&&i.set("ids",e.ids.join(",")),e.playlistSlugs?.length&&i.set("playlistSlugs",e.playlistSlugs.join(",")),e.calendar&&i.set("calendar",e.calendar),e.apiSourceId&&i.set("subscriptionId",e.apiSourceId),e.externalEventId&&i.set("externalId",e.externalEventId),e.externalEventIdPrefix&&!e.externalEventId&&i.set("externalIdPrefix",e.externalEventIdPrefix),i.set("returnOrigin",t),i.set("embed","1"),`${n}/en/embed/sync?${i.toString()}`}const v="moment-sdk-trigger-styles",T=`
1
+ (function(v){"use strict";class S{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),o=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${o},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=n=>{if(n.origin!==this.opts.liveOrigin||n.source!==this.popup)return;const r=n.data;!r||typeof r!="object"||r.source!=="moment-live-embed"||r.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(r,this.opts.liveOrigin),this.opts.onResult(r),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),e.scheduleMomentIds?.length&&i.set("scheduleMomentIds",e.scheduleMomentIds.join(",")),e.schedulePlaylistIds?.length&&i.set("schedulePlaylistIds",e.schedulePlaylistIds.join(",")),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}function T(s,e,t=typeof window<"u"?window.location.origin:""){if(e.triggerType==="schedule"){const o=new URLSearchParams;return o.set("teamSlug",e.teamSlug),o.set("returnOrigin",t),o.set("embed","1"),`${s}/en/embed/schedule?${o.toString()}`}const i=new URLSearchParams;return i.set("teamSlug",e.teamSlug),e.momentSlug&&i.set("momentSlug",e.momentSlug),e.listSlug&&i.set("listSlug",e.listSlug),i.set("triggerType",e.triggerType),e.ids?.length&&i.set("ids",e.ids.join(",")),e.playlistSlugs?.length&&i.set("playlistSlugs",e.playlistSlugs.join(",")),e.calendar&&i.set("calendar",e.calendar),e.apiSourceId&&i.set("subscriptionId",e.apiSourceId),e.externalEventId&&i.set("externalId",e.externalEventId),e.externalEventIdPrefix&&!e.externalEventId&&i.set("externalIdPrefix",e.externalEventIdPrefix),i.set("returnOrigin",t),i.set("embed","1"),`${s}/en/embed/sync?${i.toString()}`}const b="moment-sdk-trigger-styles",x=`
2
2
  :where(.moment-sync-trigger) {
3
3
  background-color: #1c1917;
4
4
  color: #ffffff;
@@ -22,4 +22,4 @@
22
22
  outline: 2px solid #1c1917;
23
23
  outline-offset: 2px;
24
24
  }
25
- `;function S(){if(typeof document>"u"||document.getElementById(v))return;const n=document.createElement("style");n.id=v,n.textContent=T,document.head.appendChild(n)}const E=".moment-sync-trigger",I="https://live.dev.momentco.ai";class b{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:E,...e},this.liveBaseUrl=I;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}S(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){e.externalEventId&&e.externalEventIdPrefix&&console.warn("[MomentSdk] Both externalEventId and externalEventIdPrefix provided; using externalEventId only.");const t=this.scopeOpenPayload(e);this.emitAnalytics("embed.open",{teamSlug:t.teamSlug,triggerType:t.triggerType}),this.createModal(t)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=s=>{s.preventDefault(),this.handleTriggerClick(t)},l=this.boundClickHandlers.get(t);l&&t.removeEventListener("click",l),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,l=e.getAttribute("data-moment-list-slug")??void 0,s=e.getAttribute("data-moment-trigger-type");let r="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"||s==="brand-playlist"||s==="playlist"||s==="schedule"?r=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const a=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",m=a?a.split(",").filter(Boolean):[],d=e.getAttribute("data-moment-playlist-slugs")??"",g=d?d.split(",").map(A=>A.trim()).filter(Boolean):[],o=e.getAttribute("data-moment-calendar");let c;o!=null&&o!==""&&(o==="google"||o==="outlook"?c=o:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",o));const h=e.getAttribute("data-api-source-id")??void 0,u=e.getAttribute("data-external-event-id")??void 0,p=e.getAttribute("data-external-event-id-prefix")??void 0;if(u&&p&&console.warn("[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const P=r==="team"||r==="brand-playlist"||r==="playlist"||r==="schedule";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:P?[]:m}),this.open({teamSlug:t,momentSlug:i,listSlug:l,triggerType:r,ids:m,playlistSlugs:g,calendar:c??void 0,apiSourceId:h,externalEventId:p&&!u?void 0:u,externalEventIdPrefix:u?void 0:p})}scopeOpenPayload(e){return e.triggerType==="playlist"||e.triggerType==="schedule"?{teamSlug:e.teamSlug,triggerType:e.triggerType,playlistSlugs:e.playlistSlugs,calendar:e.calendar}:e.triggerType==="team"||e.triggerType==="brand-playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,calendar:e.calendar}:e}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",o=>{o.target===this.overlay&&this.handleCancel()});const i=o=>{o.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const l=document.createElement("div");Object.assign(l.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const s=document.createElement("div");s.setAttribute("role","dialog"),s.setAttribute("aria-modal","true"),s.setAttribute("aria-label","Calendar sync modal"),s.tabIndex=-1,Object.assign(s.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const r=document.createElement("button");r.type="button",r.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',r.setAttribute("aria-label","Close"),Object.assign(r.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),r.addEventListener("mouseenter",()=>{r.style.color="#666"}),r.addEventListener("mouseleave",()=>{r.style.color="#999"}),r.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),s.appendChild(r),s.appendChild(this.iframe);const a=document.createElement("div");Object.assign(a.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const m=document.createElement("span");m.textContent="Powered by",Object.assign(m.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const d=document.createElement("img");d.alt="Moment",d.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(d.style,{height:"auto",width:"67px"}),a.appendChild(m),a.appendChild(d),l.appendChild(s),l.appendChild(a),this.overlay.appendChild(l),document.body.appendChild(this.overlay);const g=o=>{if(o.key!=="Tab")return;const c=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(c.length===0){o.preventDefault(),s.focus();return}const h=c[0],u=c[c.length-1],p=document.activeElement;o.shiftKey&&p===h?(o.preventDefault(),u.focus()):!o.shiftKey&&p===u&&(o.preventDefault(),h.focus())};s.addEventListener("keydown",g),r.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=g}buildIframeUrl(e){return x(this.liveBaseUrl,e)}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const l=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,l)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function y(n){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),n.className&&n.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",n.teamSlug),n.momentSlug&&e.setAttribute("data-moment-slug",n.momentSlug),n.listSlug&&e.setAttribute("data-moment-list-slug",n.listSlug),n.triggerType&&e.setAttribute("data-moment-trigger-type",n.triggerType),n.ids?.length&&e.setAttribute("data-moment-ids",n.ids.join(",")),n.playlistSlugs?.length&&e.setAttribute("data-moment-playlist-slugs",n.playlistSlugs.join(",")),n.calendar&&e.setAttribute("data-moment-calendar",n.calendar),n.apiSourceId&&e.setAttribute("data-api-source-id",n.apiSourceId),n.externalEventId&&e.setAttribute("data-external-event-id",n.externalEventId),n.externalEventIdPrefix&&e.setAttribute("data-external-event-id-prefix",n.externalEventIdPrefix),e.textContent=n.label??"Add to Calendar",e}if(typeof window<"u"){const n=window;n.MomentSdk=b,n.createMomentButton=y}f.MomentSdk=b,f.createMomentButton=y,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
25
+ `;function E(){if(typeof document>"u"||document.getElementById(b))return;const s=document.createElement("style");s.id=b,s.textContent=x,document.head.appendChild(s)}const I=".moment-sync-trigger",P="https://live.dev.momentco.ai";class y{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:I,...e},this.liveBaseUrl=P;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}E(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){e.externalEventId&&e.externalEventIdPrefix&&console.warn("[MomentSdk] Both externalEventId and externalEventIdPrefix provided; using externalEventId only.");const t=this.scopeOpenPayload(e);this.emitAnalytics("embed.open",{teamSlug:t.teamSlug,triggerType:t.triggerType}),this.createModal(t)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=n=>{n.preventDefault(),this.handleTriggerClick(t)},o=this.boundClickHandlers.get(t);o&&t.removeEventListener("click",o),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,o=e.getAttribute("data-moment-list-slug")??void 0,n=e.getAttribute("data-moment-trigger-type");let r="moment";n!=null&&n!==""&&(n==="moment"||n==="list"||n==="team"||n==="brand-playlist"||n==="playlist"||n==="schedule"?r=n:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",n));const h=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",d=h?h.split(",").filter(Boolean):[],c=e.getAttribute("data-moment-playlist-slugs")??"",m=c?c.split(",").map(k=>k.trim()).filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let l;a!=null&&a!==""&&(a==="google"||a==="outlook"?l=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const p=e.getAttribute("data-api-source-id")??void 0,u=e.getAttribute("data-external-event-id")??void 0,g=e.getAttribute("data-external-event-id-prefix")??void 0;if(u&&g&&console.warn("[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const f=r==="team"||r==="brand-playlist"||r==="playlist"||r==="schedule";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:f?[]:d}),this.open({teamSlug:t,momentSlug:i,listSlug:o,triggerType:r,ids:d,playlistSlugs:m,calendar:l??void 0,apiSourceId:p,externalEventId:g&&!u?void 0:u,externalEventIdPrefix:u?void 0:g})}scopeOpenPayload(e){return e.triggerType==="schedule"?{teamSlug:e.teamSlug,triggerType:e.triggerType}:e.triggerType==="playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,playlistSlugs:e.playlistSlugs,calendar:e.calendar}:e.triggerType==="team"||e.triggerType==="brand-playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,calendar:e.calendar}:e}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const o=document.createElement("div");Object.assign(o.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const n=document.createElement("div");n.setAttribute("role","dialog"),n.setAttribute("aria-modal","true"),n.setAttribute("aria-label","Calendar sync modal"),n.tabIndex=-1,Object.assign(n.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const r=document.createElement("button");r.type="button",r.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',r.setAttribute("aria-label","Close"),Object.assign(r.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),r.addEventListener("mouseenter",()=>{r.style.color="#666"}),r.addEventListener("mouseleave",()=>{r.style.color="#999"}),r.addEventListener("click",()=>this.handleCancel());const h=e.triggerType!=="schedule";this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),h&&n.appendChild(r),n.appendChild(this.iframe);const d=document.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const c=document.createElement("span");c.textContent="Powered by",Object.assign(c.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const m=document.createElement("img");m.alt="Moment",m.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(m.style,{height:"auto",width:"67px"}),d.appendChild(c),d.appendChild(m),o.appendChild(n),o.appendChild(d),this.overlay.appendChild(o),document.body.appendChild(this.overlay);const a=l=>{if(l.key!=="Tab")return;const p=n.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(p.length===0){l.preventDefault(),n.focus();return}const u=p[0],g=p[p.length-1],f=document.activeElement;l.shiftKey&&f===u?(l.preventDefault(),g.focus()):!l.shiftKey&&f===g&&(l.preventDefault(),u.focus())};n.addEventListener("keydown",a),h?r.focus():n.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=a}buildIframeUrl(e){return T(this.liveBaseUrl,e)}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const o=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,o)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.close"){this.handleCancel();return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new S({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success"||e.result==="partial")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),e.triggerType!=="schedule"&&(this.closeTimer=setTimeout(()=>this.cleanup(),1500))}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function w(s){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),s.className&&s.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",s.teamSlug),s.momentSlug&&e.setAttribute("data-moment-slug",s.momentSlug),s.listSlug&&e.setAttribute("data-moment-list-slug",s.listSlug),s.triggerType&&e.setAttribute("data-moment-trigger-type",s.triggerType),s.ids?.length&&e.setAttribute("data-moment-ids",s.ids.join(",")),s.playlistSlugs?.length&&e.setAttribute("data-moment-playlist-slugs",s.playlistSlugs.join(",")),s.calendar&&e.setAttribute("data-moment-calendar",s.calendar),s.apiSourceId&&e.setAttribute("data-api-source-id",s.apiSourceId),s.externalEventId&&e.setAttribute("data-external-event-id",s.externalEventId),s.externalEventIdPrefix&&e.setAttribute("data-external-event-id-prefix",s.externalEventIdPrefix),e.textContent=s.label??"Add to Calendar",e}if(typeof window<"u"){const s=window;s.MomentSdk=y,s.createMomentButton=w}v.MomentSdk=y,v.createMomentButton=w,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
@@ -12,18 +12,18 @@ class y {
12
12
  */
13
13
  open(e) {
14
14
  this.cleanup(), this.resultReceived = !1;
15
- const t = this.buildPopupUrl(e), i = Math.round(window.screenX + (window.outerWidth - 500) / 2), l = Math.round(window.screenY + (window.outerHeight - 700) / 2);
15
+ const t = this.buildPopupUrl(e), i = Math.round(window.screenX + (window.outerWidth - 500) / 2), o = Math.round(window.screenY + (window.outerHeight - 700) / 2);
16
16
  if (this.popup = window.open(
17
17
  t,
18
18
  "moment-oauth-popup",
19
- `width=500,height=700,left=${i},top=${l},menubar=no,toolbar=no,location=yes,status=no`
19
+ `width=500,height=700,left=${i},top=${o},menubar=no,toolbar=no,location=yes,status=no`
20
20
  ), !this.popup || this.popup.closed) {
21
21
  this.sendStatusToIframe("popup-blocked"), this.opts.onPopupBlocked();
22
22
  return;
23
23
  }
24
- this.sendStatusToIframe("popup-opened"), this.startPolling(), this.messageHandler = (s) => {
25
- if (s.origin !== this.opts.liveOrigin || s.source !== this.popup) return;
26
- const r = s.data;
24
+ this.sendStatusToIframe("popup-opened"), this.startPolling(), this.messageHandler = (n) => {
25
+ if (n.origin !== this.opts.liveOrigin || n.source !== this.popup) return;
26
+ const r = n.data;
27
27
  !r || typeof r != "object" || r.source !== "moment-live-embed" || r.type !== "moment.embed.result" || (this.resultReceived = !0, this.sendStatusToIframe("completed"), this.opts.iframeWindow && this.opts.iframeWindow.postMessage(r, this.opts.liveOrigin), this.opts.onResult(r), this.cleanup());
28
28
  }, window.addEventListener("message", this.messageHandler);
29
29
  }
@@ -46,7 +46,7 @@ class y {
46
46
  triggerType: e.triggerType,
47
47
  returnOrigin: t
48
48
  });
49
- return e.subscriptionType && i.set("subscriptionType", e.subscriptionType), e.selection && i.set("selection", e.selection), `${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`;
49
+ return e.subscriptionType && i.set("subscriptionType", e.subscriptionType), e.scheduleMomentIds?.length && i.set("scheduleMomentIds", e.scheduleMomentIds.join(",")), e.schedulePlaylistIds?.length && i.set("schedulePlaylistIds", e.schedulePlaylistIds.join(",")), `${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`;
50
50
  }
51
51
  startPolling() {
52
52
  this.pollTimer = setInterval(() => {
@@ -63,11 +63,15 @@ class y {
63
63
  this.opts.iframeWindow.postMessage(t, this.opts.liveOrigin);
64
64
  }
65
65
  }
66
- function w(n, e, t = typeof window < "u" ? window.location.origin : "") {
66
+ function w(s, e, t = typeof window < "u" ? window.location.origin : "") {
67
+ if (e.triggerType === "schedule") {
68
+ const o = new URLSearchParams();
69
+ return o.set("teamSlug", e.teamSlug), o.set("returnOrigin", t), o.set("embed", "1"), `${s}/en/embed/schedule?${o.toString()}`;
70
+ }
67
71
  const i = new URLSearchParams();
68
- return i.set("teamSlug", e.teamSlug), e.momentSlug && i.set("momentSlug", e.momentSlug), e.listSlug && i.set("listSlug", e.listSlug), i.set("triggerType", e.triggerType), e.ids?.length && i.set("ids", e.ids.join(",")), e.playlistSlugs?.length && i.set("playlistSlugs", e.playlistSlugs.join(",")), e.calendar && i.set("calendar", e.calendar), e.apiSourceId && i.set("subscriptionId", e.apiSourceId), e.externalEventId && i.set("externalId", e.externalEventId), e.externalEventIdPrefix && !e.externalEventId && i.set("externalIdPrefix", e.externalEventIdPrefix), i.set("returnOrigin", t), i.set("embed", "1"), `${n}/en/embed/sync?${i.toString()}`;
72
+ return i.set("teamSlug", e.teamSlug), e.momentSlug && i.set("momentSlug", e.momentSlug), e.listSlug && i.set("listSlug", e.listSlug), i.set("triggerType", e.triggerType), e.ids?.length && i.set("ids", e.ids.join(",")), e.playlistSlugs?.length && i.set("playlistSlugs", e.playlistSlugs.join(",")), e.calendar && i.set("calendar", e.calendar), e.apiSourceId && i.set("subscriptionId", e.apiSourceId), e.externalEventId && i.set("externalId", e.externalEventId), e.externalEventIdPrefix && !e.externalEventId && i.set("externalIdPrefix", e.externalEventIdPrefix), i.set("returnOrigin", t), i.set("embed", "1"), `${s}/en/embed/sync?${i.toString()}`;
69
73
  }
70
- const f = "moment-sdk-trigger-styles", x = `
74
+ const v = "moment-sdk-trigger-styles", x = `
71
75
  :where(.moment-sync-trigger) {
72
76
  background-color: #1c1917;
73
77
  color: #ffffff;
@@ -93,11 +97,11 @@ const f = "moment-sdk-trigger-styles", x = `
93
97
  }
94
98
  `;
95
99
  function T() {
96
- if (typeof document > "u" || document.getElementById(f)) return;
97
- const n = document.createElement("style");
98
- n.id = f, n.textContent = x, document.head.appendChild(n);
100
+ if (typeof document > "u" || document.getElementById(v)) return;
101
+ const s = document.createElement("style");
102
+ s.id = v, s.textContent = x, document.head.appendChild(s);
99
103
  }
100
- const E = ".moment-sync-trigger", S = "https://live.dev.momentco.ai";
104
+ const S = ".moment-sync-trigger", E = "https://live.dev.momentco.ai";
101
105
  class I {
102
106
  opts;
103
107
  liveBaseUrl;
@@ -114,9 +118,9 @@ class I {
114
118
  closeTimer = null;
115
119
  constructor(e) {
116
120
  this.opts = {
117
- triggerSelector: E,
121
+ triggerSelector: S,
118
122
  ...e
119
- }, this.liveBaseUrl = S;
123
+ }, this.liveBaseUrl = E;
120
124
  try {
121
125
  this.liveOrigin = new URL(this.liveBaseUrl).origin;
122
126
  } catch {
@@ -158,10 +162,10 @@ class I {
158
162
  // ── Private: trigger binding ──────────────────────────────────────
159
163
  bindTriggers() {
160
164
  document.querySelectorAll(this.opts.triggerSelector).forEach((t) => {
161
- const i = (s) => {
162
- s.preventDefault(), this.handleTriggerClick(t);
163
- }, l = this.boundClickHandlers.get(t);
164
- l && t.removeEventListener("click", l), t.addEventListener("click", i), this.boundClickHandlers.set(t, i);
165
+ const i = (n) => {
166
+ n.preventDefault(), this.handleTriggerClick(t);
167
+ }, o = this.boundClickHandlers.get(t);
168
+ o && t.removeEventListener("click", o), t.addEventListener("click", i), this.boundClickHandlers.set(t, i);
165
169
  });
166
170
  }
167
171
  unbindTriggers() {
@@ -170,43 +174,46 @@ class I {
170
174
  }), this.boundClickHandlers.clear();
171
175
  }
172
176
  handleTriggerClick(e) {
173
- const t = e.getAttribute("data-moment-team-slug") ?? "", i = e.getAttribute("data-moment-slug") ?? void 0, l = e.getAttribute("data-moment-list-slug") ?? void 0, s = e.getAttribute("data-moment-trigger-type");
177
+ const t = e.getAttribute("data-moment-team-slug") ?? "", i = e.getAttribute("data-moment-slug") ?? void 0, o = e.getAttribute("data-moment-list-slug") ?? void 0, n = e.getAttribute("data-moment-trigger-type");
174
178
  let r = "moment";
175
- s != null && s !== "" && (s === "moment" || s === "list" || s === "team" || s === "brand-playlist" || s === "playlist" || s === "schedule" ? r = s : console.warn(
179
+ n != null && n !== "" && (n === "moment" || n === "list" || n === "team" || n === "brand-playlist" || n === "playlist" || n === "schedule" ? r = n : console.warn(
176
180
  "[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",
177
- s
181
+ n
178
182
  ));
179
- const a = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", m = a ? a.split(",").filter(Boolean) : [], d = e.getAttribute("data-moment-playlist-slugs") ?? "", g = d ? d.split(",").map((b) => b.trim()).filter(Boolean) : [], o = e.getAttribute("data-moment-calendar");
180
- let c;
181
- o != null && o !== "" && (o === "google" || o === "outlook" ? c = o : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", o));
182
- const h = e.getAttribute("data-api-source-id") ?? void 0, u = e.getAttribute("data-external-event-id") ?? void 0, p = e.getAttribute("data-external-event-id-prefix") ?? void 0;
183
- if (u && p && console.warn(
183
+ const h = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", d = h ? h.split(",").filter(Boolean) : [], c = e.getAttribute("data-moment-playlist-slugs") ?? "", m = c ? c.split(",").map((b) => b.trim()).filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
184
+ let l;
185
+ a != null && a !== "" && (a === "google" || a === "outlook" ? l = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a));
186
+ const p = e.getAttribute("data-api-source-id") ?? void 0, u = e.getAttribute("data-external-event-id") ?? void 0, g = e.getAttribute("data-external-event-id-prefix") ?? void 0;
187
+ if (u && g && console.warn(
184
188
  "[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."
185
189
  ), !t) {
186
190
  console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");
187
191
  return;
188
192
  }
189
- const v = r === "team" || r === "brand-playlist" || r === "playlist" || r === "schedule";
193
+ const f = r === "team" || r === "brand-playlist" || r === "playlist" || r === "schedule";
190
194
  this.emitAnalytics("embed.trigger.click", {
191
195
  teamSlug: t,
192
196
  triggerType: r,
193
- momentIds: v ? [] : m
197
+ momentIds: f ? [] : d
194
198
  }), this.open({
195
199
  teamSlug: t,
196
200
  momentSlug: i,
197
- listSlug: l,
201
+ listSlug: o,
198
202
  triggerType: r,
199
- ids: m,
200
- playlistSlugs: g,
201
- calendar: c ?? void 0,
202
- apiSourceId: h,
203
- externalEventId: p && !u ? void 0 : u,
204
- externalEventIdPrefix: u ? void 0 : p
203
+ ids: d,
204
+ playlistSlugs: m,
205
+ calendar: l ?? void 0,
206
+ apiSourceId: p,
207
+ externalEventId: g && !u ? void 0 : u,
208
+ externalEventIdPrefix: u ? void 0 : g
205
209
  });
206
210
  }
207
211
  /** Drop moment/list/source scoping for brand-level subscription triggers. */
208
212
  scopeOpenPayload(e) {
209
- return e.triggerType === "playlist" || e.triggerType === "schedule" ? {
213
+ return e.triggerType === "schedule" ? {
214
+ teamSlug: e.teamSlug,
215
+ triggerType: e.triggerType
216
+ } : e.triggerType === "playlist" ? {
210
217
  teamSlug: e.teamSlug,
211
218
  triggerType: e.triggerType,
212
219
  playlistSlugs: e.playlistSlugs,
@@ -231,15 +238,15 @@ class I {
231
238
  backgroundColor: "rgba(0, 0, 0, 0.5)",
232
239
  backdropFilter: "blur(2px)",
233
240
  padding: "16px"
234
- }), this.overlay.addEventListener("click", (o) => {
235
- o.target === this.overlay && this.handleCancel();
241
+ }), this.overlay.addEventListener("click", (l) => {
242
+ l.target === this.overlay && this.handleCancel();
236
243
  });
237
- const i = (o) => {
238
- o.key === "Escape" && this.handleCancel();
244
+ const i = (l) => {
245
+ l.key === "Escape" && this.handleCancel();
239
246
  };
240
247
  document.addEventListener("keydown", i);
241
- const l = document.createElement("div");
242
- Object.assign(l.style, {
248
+ const o = document.createElement("div");
249
+ Object.assign(o.style, {
243
250
  position: "relative",
244
251
  width: "100%",
245
252
  maxWidth: "380px",
@@ -250,8 +257,8 @@ class I {
250
257
  display: "flex",
251
258
  flexDirection: "column"
252
259
  });
253
- const s = document.createElement("div");
254
- s.setAttribute("role", "dialog"), s.setAttribute("aria-modal", "true"), s.setAttribute("aria-label", "Calendar sync modal"), s.tabIndex = -1, Object.assign(s.style, {
260
+ const n = document.createElement("div");
261
+ n.setAttribute("role", "dialog"), n.setAttribute("aria-modal", "true"), n.setAttribute("aria-label", "Calendar sync modal"), n.tabIndex = -1, Object.assign(n.style, {
255
262
  position: "relative",
256
263
  width: "100%",
257
264
  maxHeight: "80vh",
@@ -279,47 +286,49 @@ class I {
279
286
  r.style.color = "#666";
280
287
  }), r.addEventListener("mouseleave", () => {
281
288
  r.style.color = "#999";
282
- }), r.addEventListener("click", () => this.handleCancel()), this.iframe = document.createElement("iframe"), this.iframe.src = t, this.iframe.setAttribute("allow", ""), this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-forms"), this.iframe.setAttribute("scrolling", "no"), Object.assign(this.iframe.style, {
289
+ }), r.addEventListener("click", () => this.handleCancel());
290
+ const h = e.triggerType !== "schedule";
291
+ this.iframe = document.createElement("iframe"), this.iframe.src = t, this.iframe.setAttribute("allow", ""), this.iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-popups allow-forms"), this.iframe.setAttribute("scrolling", "no"), Object.assign(this.iframe.style, {
283
292
  width: "100%",
284
293
  border: "none",
285
294
  display: "block",
286
295
  height: "420px",
287
296
  overflow: "hidden",
288
297
  transition: "height 0.15s ease"
289
- }), s.appendChild(r), s.appendChild(this.iframe);
290
- const a = document.createElement("div");
291
- Object.assign(a.style, {
298
+ }), h && n.appendChild(r), n.appendChild(this.iframe);
299
+ const d = document.createElement("div");
300
+ Object.assign(d.style, {
292
301
  display: "flex",
293
302
  alignItems: "center",
294
303
  justifyContent: "center",
295
304
  gap: "6px",
296
305
  padding: "12px 0"
297
306
  });
298
- const m = document.createElement("span");
299
- m.textContent = "Powered by", Object.assign(m.style, {
307
+ const c = document.createElement("span");
308
+ c.textContent = "Powered by", Object.assign(c.style, {
300
309
  fontFamily: "Inter, system-ui, -apple-system, sans-serif",
301
310
  fontSize: "12px",
302
311
  lineHeight: "16px",
303
312
  color: "#ffffff"
304
313
  });
305
- const d = document.createElement("img");
306
- d.alt = "Moment", d.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(d.style, {
314
+ const m = document.createElement("img");
315
+ m.alt = "Moment", m.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(m.style, {
307
316
  height: "auto",
308
317
  width: "67px"
309
- }), a.appendChild(m), a.appendChild(d), l.appendChild(s), l.appendChild(a), this.overlay.appendChild(l), document.body.appendChild(this.overlay);
310
- const g = (o) => {
311
- if (o.key !== "Tab") return;
312
- const c = s.querySelectorAll(
318
+ }), d.appendChild(c), d.appendChild(m), o.appendChild(n), o.appendChild(d), this.overlay.appendChild(o), document.body.appendChild(this.overlay);
319
+ const a = (l) => {
320
+ if (l.key !== "Tab") return;
321
+ const p = n.querySelectorAll(
313
322
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
314
323
  );
315
- if (c.length === 0) {
316
- o.preventDefault(), s.focus();
324
+ if (p.length === 0) {
325
+ l.preventDefault(), n.focus();
317
326
  return;
318
327
  }
319
- const h = c[0], u = c[c.length - 1], p = document.activeElement;
320
- o.shiftKey && p === h ? (o.preventDefault(), u.focus()) : !o.shiftKey && p === u && (o.preventDefault(), h.focus());
328
+ const u = p[0], g = p[p.length - 1], f = document.activeElement;
329
+ l.shiftKey && f === u ? (l.preventDefault(), g.focus()) : !l.shiftKey && f === g && (l.preventDefault(), u.focus());
321
330
  };
322
- s.addEventListener("keydown", g), r.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = g;
331
+ n.addEventListener("keydown", a), h ? r.focus() : n.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = a;
323
332
  }
324
333
  buildIframeUrl(e) {
325
334
  return w(this.liveBaseUrl, e);
@@ -341,8 +350,8 @@ class I {
341
350
  if (t.source === "moment-live-embed" && t.type === "moment.embed.resize") {
342
351
  const i = typeof t.height == "number" ? t.height : 0;
343
352
  if (i > 0 && this.iframe) {
344
- const l = Math.round(window.innerHeight * 0.8);
345
- this.iframe.style.height = `${Math.min(i, l)}px`;
353
+ const o = Math.round(window.innerHeight * 0.8);
354
+ this.iframe.style.height = `${Math.min(i, o)}px`;
346
355
  }
347
356
  return;
348
357
  }
@@ -351,6 +360,10 @@ class I {
351
360
  i && typeof i == "string" && i.startsWith("webcal://") && window.open(i, "_blank");
352
361
  return;
353
362
  }
363
+ if (t.source === "moment-live-embed" && t.type === "moment.embed.close") {
364
+ this.handleCancel();
365
+ return;
366
+ }
354
367
  if (t.source === "moment-live-embed" && t.type === "moment.embed.result") {
355
368
  const i = t;
356
369
  this.handleResult(i);
@@ -379,7 +392,7 @@ class I {
379
392
  }), this.popupBridge.open(e);
380
393
  }
381
394
  handleResult(e) {
382
- if (e.result === "success")
395
+ if (e.result === "success" || e.result === "partial")
383
396
  this.emitAnalytics("subscribe.success", {
384
397
  provider: e.provider,
385
398
  triggerType: e.triggerType,
@@ -393,7 +406,7 @@ class I {
393
406
  }), this.opts.onError?.(e);
394
407
  else if (e.result === "cancelled")
395
408
  return;
396
- this.opts.onClose?.(e), this.emitAnalytics("embed.close"), this.closeTimer = setTimeout(() => this.cleanup(), 1500);
409
+ this.opts.onClose?.(e), this.emitAnalytics("embed.close"), e.triggerType !== "schedule" && (this.closeTimer = setTimeout(() => this.cleanup(), 1500));
397
410
  }
398
411
  handleCancel() {
399
412
  this.emitAnalytics("embed.close");
@@ -425,15 +438,15 @@ class I {
425
438
  });
426
439
  }
427
440
  }
428
- function P(n) {
441
+ function P(s) {
429
442
  const e = document.createElement("button");
430
- return e.type = "button", e.classList.add("moment-sync-trigger"), n.className && n.className.split(" ").forEach((t) => {
443
+ return e.type = "button", e.classList.add("moment-sync-trigger"), s.className && s.className.split(" ").forEach((t) => {
431
444
  t && e.classList.add(t);
432
- }), e.setAttribute("data-moment-team-slug", n.teamSlug), n.momentSlug && e.setAttribute("data-moment-slug", n.momentSlug), n.listSlug && e.setAttribute("data-moment-list-slug", n.listSlug), n.triggerType && e.setAttribute("data-moment-trigger-type", n.triggerType), n.ids?.length && e.setAttribute("data-moment-ids", n.ids.join(",")), n.playlistSlugs?.length && e.setAttribute("data-moment-playlist-slugs", n.playlistSlugs.join(",")), n.calendar && e.setAttribute("data-moment-calendar", n.calendar), n.apiSourceId && e.setAttribute("data-api-source-id", n.apiSourceId), n.externalEventId && e.setAttribute("data-external-event-id", n.externalEventId), n.externalEventIdPrefix && e.setAttribute("data-external-event-id-prefix", n.externalEventIdPrefix), e.textContent = n.label ?? "Add to Calendar", e;
445
+ }), e.setAttribute("data-moment-team-slug", s.teamSlug), s.momentSlug && e.setAttribute("data-moment-slug", s.momentSlug), s.listSlug && e.setAttribute("data-moment-list-slug", s.listSlug), s.triggerType && e.setAttribute("data-moment-trigger-type", s.triggerType), s.ids?.length && e.setAttribute("data-moment-ids", s.ids.join(",")), s.playlistSlugs?.length && e.setAttribute("data-moment-playlist-slugs", s.playlistSlugs.join(",")), s.calendar && e.setAttribute("data-moment-calendar", s.calendar), s.apiSourceId && e.setAttribute("data-api-source-id", s.apiSourceId), s.externalEventId && e.setAttribute("data-external-event-id", s.externalEventId), s.externalEventIdPrefix && e.setAttribute("data-external-event-id-prefix", s.externalEventIdPrefix), e.textContent = s.label ?? "Add to Calendar", e;
433
446
  }
434
447
  if (typeof window < "u") {
435
- const n = window;
436
- n.MomentSdk = I, n.createMomentButton = P;
448
+ const s = window;
449
+ s.MomentSdk = I, s.createMomentButton = P;
437
450
  }
438
451
  export {
439
452
  I as MomentSdk,
@@ -10,10 +10,7 @@ export declare function createMomentButton(options: {
10
10
  listSlug?: string;
11
11
  triggerType?: MomentTriggerType;
12
12
  ids?: string[];
13
- /**
14
- * Playlist slugs offered under a `playlist` trigger (picker when >1) or the
15
- * collections shown in the `schedule` trigger's multi-select modal.
16
- */
13
+ /** Playlist slugs offered under a `playlist` trigger (picker when >1). */
17
14
  playlistSlugs?: string[];
18
15
  calendar?: CalendarProvider;
19
16
  /** Sync subscription source id; pairs with `externalEventId` for server-side resolution. */
@@ -9,12 +9,18 @@
9
9
  * - `playlist` → subscribe to one of several specific playlists. When more than
10
10
  * one slug is supplied, Live fetches each playlist and prompts the user to
11
11
  * pick which one to sync.
12
- * - `schedule` → open the brand's schedule modal. Live fetches the supplied
13
- * collection playlists and lets the fan multi-select which ones to sync in a
14
- * single pass (the generic "Add to Calendar" experience).
12
+ * - `schedule` → open the brand's full schedule experience: a multi-select
13
+ * surface where the fan picks any mix of the brand's collections (playlists)
14
+ * and individual events (moments) and syncs them all at once.
15
15
  */
16
16
  export type MomentTriggerType = 'moment' | 'list' | 'team' | 'brand-playlist' | 'playlist' | 'schedule';
17
- export type CalendarProvider = 'google' | 'outlook';
17
+ /**
18
+ * Calendar providers. `google` / `outlook` are the OAuth providers a partner may
19
+ * pre-select via `data-moment-calendar`. `apple` is never a hint — it only ever
20
+ * appears on a result payload's `provider` (guest webcal path), so consumers
21
+ * switching on `provider` must tolerate it.
22
+ */
23
+ export type CalendarProvider = 'google' | 'outlook' | 'apple';
18
24
  export interface MomentSdkInitOptions {
19
25
  /** CSS selector for trigger elements. Default: ".moment-sync-trigger" */
20
26
  triggerSelector?: string;
@@ -45,10 +51,8 @@ export interface MomentSdkInitOptions {
45
51
  * With a single slug Live syncs that playlist directly; with several it shows
46
52
  * a picker. Omit `momentSlug`, `listSlug`, `ids`, `apiSourceId`, and
47
53
  * `externalEventId` for this trigger type.
48
- * - `schedule` → `teamSlug` plus `playlistSlugs` (the collections to surface).
49
- * Live renders the schedule modal where the fan multi-selects collections and
50
- * syncs them together. Omit `momentSlug`, `listSlug`, `ids`, `apiSourceId`,
51
- * and `externalEventId` for this trigger type.
54
+ * - `schedule` → `teamSlug` only. Opens the brand's full schedule experience.
55
+ * Omit every other field (including `calendar`, which is ignored here).
52
56
  */
53
57
  export interface MomentSdkOpenPayload {
54
58
  teamSlug: string;
@@ -58,11 +62,10 @@ export interface MomentSdkOpenPayload {
58
62
  triggerType: MomentTriggerType;
59
63
  ids?: string[];
60
64
  /**
61
- * Playlist slugs to offer under a `playlist` or `schedule` trigger. Live
62
- * fetches each playlist's details. Under `playlist`, more than one slug
63
- * renders a single-choice picker; under `schedule`, the slugs become the
64
- * multi-select collections in the schedule modal. Forwarded to the Live embed
65
- * as the `playlistSlugs` query param (comma-separated).
65
+ * Playlist slugs to offer under a `playlist` trigger. Live fetches each
66
+ * playlist's details; with more than one slug it renders a picker so the user
67
+ * can choose which playlist to sync. Forwarded to the Live embed as the
68
+ * `playlistSlugs` query param (comma-separated).
66
69
  */
67
70
  playlistSlugs?: string[];
68
71
  calendar?: CalendarProvider;
@@ -107,11 +110,13 @@ export interface MomentOAuthStartPayload {
107
110
  */
108
111
  subscriptionType?: MomentSubscriptionType;
109
112
  /**
110
- * Mixed-type selection encoded as `type:id,id;type:id`. Set by the `schedule`
111
- * trigger when the cart mixes collections and individual events. Forwarded
112
- * verbatim to the popup, which subscribes each group; opaque to the SDK.
113
+ * Present for the `schedule` trigger only: the mixed selection's moment ids.
114
+ * Carried in the popup URL (not localStorage) so the handoff survives browser
115
+ * third-party storage partitioning, matching the single-resource `ids` flow.
113
116
  */
114
- selection?: string;
117
+ scheduleMomentIds?: string[];
118
+ /** Present for the `schedule` trigger only: the mixed selection's playlist ids. */
119
+ schedulePlaylistIds?: string[];
115
120
  returnOrigin: string;
116
121
  }
117
122
  /** Parent → Iframe: relay OAuth popup status */
@@ -124,10 +129,25 @@ export interface MomentOAuthStatusPayload {
124
129
  export interface MomentResultPayload {
125
130
  source: 'moment-live-embed';
126
131
  type: 'moment.embed.result';
127
- result: 'success' | 'error' | 'cancelled';
132
+ /**
133
+ * `partial` is emitted only by the `schedule` trigger when some — but not all —
134
+ * of a mixed selection subscribed successfully. Inspect `counts` for detail.
135
+ */
136
+ result: 'success' | 'error' | 'cancelled' | 'partial';
128
137
  provider?: CalendarProvider;
129
138
  triggerType: MomentTriggerType;
130
139
  momentIds: string[];
140
+ /**
141
+ * Aggregate outcome for multi-resource syncs (schedule trigger). `moments` and
142
+ * `playlists` break down the successful count by resource type.
143
+ */
144
+ counts?: {
145
+ requested: number;
146
+ succeeded: number;
147
+ failed: number;
148
+ moments?: number;
149
+ playlists?: number;
150
+ };
131
151
  message?: string;
132
152
  }
133
153
  /**
@@ -139,6 +159,17 @@ export interface MomentOpenUrlPayload {
139
159
  type: 'moment.embed.open.url';
140
160
  url: string;
141
161
  }
162
+ /**
163
+ * Iframe → Parent: request to close the modal.
164
+ * Used by interactive embeds (e.g. the `schedule` trigger) whose in-iframe
165
+ * controls — a header close, a "Done" on the success screen — must dismiss the
166
+ * modal. A `cancelled` result is deliberately NOT enough (the SDK keeps the
167
+ * modal open on cancel so the iframe can show retry UI), so this is explicit.
168
+ */
169
+ export interface MomentClosePayload {
170
+ source: 'moment-live-embed';
171
+ type: 'moment.embed.close';
172
+ }
142
173
  export type MomentAnalyticsEventName = 'embed.init' | 'embed.trigger.click' | 'embed.open' | 'oauth.start' | 'oauth.popup.blocked' | 'oauth.popup.closed' | 'subscribe.success' | 'subscribe.error' | 'embed.close';
143
174
  export interface MomentAnalyticsEvent {
144
175
  event: MomentAnalyticsEventName;
@@ -148,4 +179,4 @@ export interface MomentAnalyticsEvent {
148
179
  momentIds?: string[];
149
180
  timestamp: number;
150
181
  }
151
- export type MomentPostMessage = MomentOAuthStartPayload | MomentOAuthStatusPayload | MomentResultPayload | MomentOpenUrlPayload;
182
+ export type MomentPostMessage = MomentOAuthStartPayload | MomentOAuthStatusPayload | MomentResultPayload | MomentOpenUrlPayload | MomentClosePayload;
@@ -23,6 +23,7 @@ Opens **http://localhost:5051** with:
23
23
  | `/moment/` | `moment` |
24
24
  | `/collection/` | `list` |
25
25
  | `/brand-schedule/` | `brand-playlist` |
26
+ | `/schedule/` | `schedule` |
26
27
 
27
28
  Demo slugs are in `shared/fixtures.js`. Edit that file if your account uses different values.
28
29
 
@@ -29,6 +29,10 @@
29
29
  <h2>Brand schedule</h2>
30
30
  <p>Follow the full schedule with <code>triggerType: brand-playlist</code>.</p>
31
31
  </a>
32
+ <a class="hub-card" href="/schedule/">
33
+ <h2>Schedule</h2>
34
+ <p>Multi-select collections + events with <code>triggerType: schedule</code>.</p>
35
+ </a>
32
36
  </div>
33
37
 
34
38
  <div id="demo-log-host"></div>
@@ -0,0 +1,43 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Moment SDK — Schedule trigger</title>
7
+ <link rel="stylesheet" href="/shared/demo.css" />
8
+ </head>
9
+ <body>
10
+ <main class="demo-shell">
11
+ <div id="demo-nav" class="demo-top"></div>
12
+
13
+ <h1>Schedule trigger</h1>
14
+ <p class="lead">
15
+ The full brand schedule experience. The fan picks any mix of the brand's collections
16
+ (playlists) and individual events, then syncs them all at once. Only
17
+ <code>data-moment-team-slug</code> and <code>data-moment-trigger-type="schedule"</code>
18
+ are required.
19
+ </p>
20
+
21
+ <article class="hero-card">
22
+ <div class="card-info">
23
+ <div class="card-title">Customize your sdk-demo schedule</div>
24
+ <div class="card-meta">Team slug <code id="team-slug-label"></code></div>
25
+ </div>
26
+ <button type="button" id="schedule-trigger" class="moment-sync-trigger demo-btn">
27
+ View Schedule
28
+ </button>
29
+ </article>
30
+
31
+ <section class="section">
32
+ <h2>Embed snippet</h2>
33
+ <div class="snippet">
34
+ <button class="copy-btn" type="button" data-copy-target="snippet-code">Copy</button>
35
+ <pre id="snippet-code">Loading snippet…</pre>
36
+ </div>
37
+ </section>
38
+
39
+ <div id="demo-log-host"></div>
40
+ </main>
41
+ <script type="module" src="/schedule/main.js"></script>
42
+ </body>
43
+ </html>
@@ -0,0 +1,23 @@
1
+ import { DEMO_FIXTURES } from '../shared/fixtures.js';
2
+ import { embedSnippetHtml, initDemoSdk, mountLogPanel, mountNav } from '../shared/boot.js';
3
+
4
+ mountNav({ title: 'Schedule' });
5
+ mountLogPanel();
6
+
7
+ // Allow ?teamSlug=<brand> to point the demo at any brand on the connected
8
+ // backend (handy for local testing); falls back to the sdk-demo fixture.
9
+ const teamSlug =
10
+ new URLSearchParams(window.location.search).get('teamSlug') || DEMO_FIXTURES.teamSlug;
11
+
12
+ document.getElementById('team-slug-label').textContent = teamSlug;
13
+
14
+ const trigger = document.getElementById('schedule-trigger');
15
+ trigger.setAttribute('data-moment-team-slug', teamSlug);
16
+ trigger.setAttribute('data-moment-trigger-type', 'schedule');
17
+
18
+ document.getElementById('snippet-code').textContent = await embedSnippetHtml({
19
+ triggerType: 'schedule',
20
+ label: 'View Schedule',
21
+ });
22
+
23
+ await initDemoSdk();
@@ -26,6 +26,8 @@ const ROUTES = {
26
26
  '/collection/': 'collection/index.html',
27
27
  '/brand-schedule': 'brand-schedule/index.html',
28
28
  '/brand-schedule/': 'brand-schedule/index.html',
29
+ '/schedule': 'schedule/index.html',
30
+ '/schedule/': 'schedule/index.html',
29
31
  };
30
32
 
31
33
  function send(res, status, body, type = 'text/plain; charset=utf-8') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentco-ai/moment-sdk",
3
- "version": "0.3.3-dev.22",
3
+ "version": "0.4.0-dev.25",
4
4
  "type": "module",
5
5
  "description": "Embeddable calendar sync widget for external team websites",
6
6
  "author": "Moment Co. <support@momentco.ai> (https://momentco.ai)",
@@ -55,7 +55,6 @@
55
55
  "build": "vite build && tsc --emitDeclarationOnly --outDir dist/types",
56
56
  "build:dev": "vite build --mode development && tsc --emitDeclarationOnly --outDir dist/types",
57
57
  "build:prod": "vite build --mode production && tsc --emitDeclarationOnly --outDir dist/types",
58
- "build:local": "VITE_LIVE_BASE_URL=http://localhost:8081 vite build && tsc --emitDeclarationOnly --outDir dist/types",
59
58
  "type-check": "tsc --noEmit",
60
59
  "typecheck": "pnpm run type-check",
61
60
  "lint": "eslint .",
@@ -65,8 +64,6 @@
65
64
  "quality": "pnpm run type-check && pnpm run lint && pnpm run format",
66
65
  "test": "vitest run",
67
66
  "examples": "pnpm build && node examples/serve.mjs",
68
- "integration-test": "node integration-test/serve.mjs",
69
- "integration-test:local": "INTEGRATION_USE_LOCAL_SDK=1 node integration-test/serve.mjs",
70
67
  "prepare": "husky"
71
68
  },
72
69
  "devDependencies": {