@momentco-ai/moment-sdk 0.3.0 → 0.3.1-dev.20

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/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - `externalEventIdPrefix` on `MomentSdkOpenPayload`, `createMomentButton()`, and `data-external-event-id-prefix` for event-level sync buttons. Live fetches all showings for the upstream event and prompts the user to pick one. Mutually exclusive with `externalEventId`.
13
+
8
14
  ## [0.3.0] - 2026-06-08
9
15
 
10
16
  ### Added
package/README.md CHANGED
@@ -106,19 +106,20 @@ For `team` and `brand-playlist`, omit moment/list/ID and source-resolution attri
106
106
 
107
107
  Place these on any element matching the trigger selector (default: `.moment-sync-trigger`):
108
108
 
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 `data-external-event-id`) |
119
- | `data-external-event-id` | No | External event ID from your feed, resolved against the source ID |
120
-
121
- When both `data-api-source-id` and `data-external-event-id` are set, Live resolves the moment server-side — useful when your CMS emits native event IDs instead of Moment slugs.
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 |
121
+
122
+ 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.
122
123
 
123
124
  ---
124
125
 
@@ -150,12 +151,20 @@ sdk.open({
150
151
  triggerType: 'moment',
151
152
  });
152
153
 
153
- // Source-based resolution (CMS event ID)
154
+ // Source-based resolution (CMS event ID — single showing)
154
155
  sdk.open({
155
156
  teamSlug: 'your-team-slug',
156
157
  triggerType: 'moment',
157
158
  apiSourceId: 'your-sync-source-id',
158
- externalEventId: 'native-event-12345',
159
+ externalEventId: '1420:2706',
160
+ });
161
+
162
+ // Source-based resolution (event with multiple showings — user picks date)
163
+ sdk.open({
164
+ teamSlug: 'your-team-slug',
165
+ triggerType: 'moment',
166
+ apiSourceId: 'your-sync-source-id',
167
+ externalEventIdPrefix: '1420',
159
168
  });
160
169
 
161
170
  // Follow full team schedule
@@ -1,4 +1,4 @@
1
- (function(h){"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),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),`${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)}}const g="moment-sdk-trigger-styles",T=`
1
+ (function(g){"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),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=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),`${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.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 f="moment-sdk-trigger-styles",E=`
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(g))return;const s=document.createElement("style");s.id=g,s.textContent=T,document.head.appendChild(s)}const E=".moment-sync-trigger",x="https://live.momentco.ai";class f{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=x;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){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"?r=n:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",n));const d=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",c=d?d.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let m;a!=null&&a!==""&&(a==="google"||a==="outlook"?m=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const l=e.getAttribute("data-api-source-id")??void 0,u=e.getAttribute("data-external-event-id")??void 0;if(!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const p=r==="team"||r==="brand-playlist";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:p?[]:c}),this.open({teamSlug:t,momentSlug:i,listSlug:o,triggerType:r,ids:c,calendar:m??void 0,apiSourceId:l,externalEventId:u})}scopeOpenPayload(e){return 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()),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"}),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 a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),d.appendChild(c),d.appendChild(a),o.appendChild(n),o.appendChild(d),this.overlay.appendChild(o),document.body.appendChild(this.overlay);const m=l=>{if(l.key!=="Tab")return;const u=n.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(u.length===0){l.preventDefault(),n.focus();return}const p=u[0],b=u[u.length-1],y=document.activeElement;l.shiftKey&&y===p?(l.preventDefault(),b.focus()):!l.shiftKey&&y===b&&(l.preventDefault(),p.focus())};n.addEventListener("keydown",m),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=m}buildIframeUrl(e){const t=new URLSearchParams;return t.set("teamSlug",e.teamSlug),e.momentSlug&&t.set("momentSlug",e.momentSlug),e.listSlug&&t.set("listSlug",e.listSlug),t.set("triggerType",e.triggerType),e.ids?.length&&t.set("ids",e.ids.join(",")),e.calendar&&t.set("calendar",e.calendar),e.apiSourceId&&t.set("subscriptionId",e.apiSourceId),e.externalEventId&&t.set("externalId",e.externalEventId),t.set("returnOrigin",window.location.origin),t.set("embed","1"),`${this.liveBaseUrl}/en/embed/sync?${t.toString()}`}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.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 v(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.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),e.textContent=s.label??"Add to Calendar",e}if(typeof window<"u"){const s=window;s.MomentSdk=f,s.createMomentButton=v}h.MomentSdk=f,h.createMomentButton=v,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
25
+ `;function T(){if(typeof document>"u"||document.getElementById(f))return;const n=document.createElement("style");n.id=f,n.textContent=E,document.head.appendChild(n)}const S=".moment-sync-trigger",I="https://live.dev.momentco.ai";class v{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:S,...e},this.liveBaseUrl=I;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}T(),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)},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,s=e.getAttribute("data-moment-trigger-type");let r="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"||s==="brand-playlist"?r=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const c=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",u=c?c.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let p;a!=null&&a!==""&&(a==="google"||a==="outlook"?p=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const l=e.getAttribute("data-api-source-id")??void 0,d=e.getAttribute("data-external-event-id")??void 0,m=e.getAttribute("data-external-event-id-prefix")??void 0;if(d&&m&&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 h=r==="team"||r==="brand-playlist";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:h?[]:u}),this.open({teamSlug:t,momentSlug:i,listSlug:o,triggerType:r,ids:u,calendar:p??void 0,apiSourceId:l,externalEventId:m&&!d?void 0:d,externalEventIdPrefix:d?void 0:m})}scopeOpenPayload(e){return 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 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 c=document.createElement("div");Object.assign(c.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const u=document.createElement("span");u.textContent="Powered by",Object.assign(u.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),c.appendChild(u),c.appendChild(a),o.appendChild(s),o.appendChild(c),this.overlay.appendChild(o),document.body.appendChild(this.overlay);const p=l=>{if(l.key!=="Tab")return;const d=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(d.length===0){l.preventDefault(),s.focus();return}const m=d[0],h=d[d.length-1],y=document.activeElement;l.shiftKey&&y===m?(l.preventDefault(),h.focus()):!l.shiftKey&&y===h&&(l.preventDefault(),m.focus())};s.addEventListener("keydown",p),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=p}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 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.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 b(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.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=v,n.createMomentButton=b}g.MomentSdk=v,g.createMomentButton=b,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
@@ -21,9 +21,9 @@ class v {
21
21
  this.sendStatusToIframe("popup-blocked"), this.opts.onPopupBlocked();
22
22
  return;
23
23
  }
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;
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;
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
  }
@@ -63,7 +63,11 @@ class v {
63
63
  this.opts.iframeWindow.postMessage(t, this.opts.liveOrigin);
64
64
  }
65
65
  }
66
- const f = "moment-sdk-trigger-styles", b = `
66
+ function b(n, e, t = typeof window < "u" ? window.location.origin : "") {
67
+ 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.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()}`;
69
+ }
70
+ const f = "moment-sdk-trigger-styles", y = `
67
71
  :where(.moment-sync-trigger) {
68
72
  background-color: #1c1917;
69
73
  color: #ffffff;
@@ -88,13 +92,13 @@ const f = "moment-sdk-trigger-styles", b = `
88
92
  outline-offset: 2px;
89
93
  }
90
94
  `;
91
- function y() {
95
+ function w() {
92
96
  if (typeof document > "u" || document.getElementById(f)) return;
93
- const s = document.createElement("style");
94
- s.id = f, s.textContent = b, document.head.appendChild(s);
97
+ const n = document.createElement("style");
98
+ n.id = f, n.textContent = y, document.head.appendChild(n);
95
99
  }
96
- const w = ".moment-sync-trigger", T = "https://live.momentco.ai";
97
- class E {
100
+ const x = ".moment-sync-trigger", E = "https://live.dev.momentco.ai";
101
+ class T {
98
102
  opts;
99
103
  liveBaseUrl;
100
104
  liveOrigin;
@@ -110,20 +114,23 @@ class E {
110
114
  closeTimer = null;
111
115
  constructor(e) {
112
116
  this.opts = {
113
- triggerSelector: w,
117
+ triggerSelector: x,
114
118
  ...e
115
- }, this.liveBaseUrl = T;
119
+ }, this.liveBaseUrl = E;
116
120
  try {
117
121
  this.liveOrigin = new URL(this.liveBaseUrl).origin;
118
122
  } catch {
119
123
  throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`);
120
124
  }
121
- y(), this.bindTriggers(), this.emitAnalytics("embed.init");
125
+ w(), this.bindTriggers(), this.emitAnalytics("embed.init");
122
126
  }
123
127
  /**
124
128
  * Programmatically open the sync modal.
125
129
  */
126
130
  open(e) {
131
+ e.externalEventId && e.externalEventIdPrefix && console.warn(
132
+ "[MomentSdk] Both externalEventId and externalEventIdPrefix provided; using externalEventId only."
133
+ );
127
134
  const t = this.scopeOpenPayload(e);
128
135
  this.emitAnalytics("embed.open", {
129
136
  teamSlug: t.teamSlug,
@@ -151,8 +158,8 @@ class E {
151
158
  // ── Private: trigger binding ──────────────────────────────────────
152
159
  bindTriggers() {
153
160
  document.querySelectorAll(this.opts.triggerSelector).forEach((t) => {
154
- const i = (n) => {
155
- n.preventDefault(), this.handleTriggerClick(t);
161
+ const i = (s) => {
162
+ s.preventDefault(), this.handleTriggerClick(t);
156
163
  }, o = this.boundClickHandlers.get(t);
157
164
  o && t.removeEventListener("click", o), t.addEventListener("click", i), this.boundClickHandlers.set(t, i);
158
165
  });
@@ -163,34 +170,37 @@ class E {
163
170
  }), this.boundClickHandlers.clear();
164
171
  }
165
172
  handleTriggerClick(e) {
166
- 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");
173
+ 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, s = e.getAttribute("data-moment-trigger-type");
167
174
  let r = "moment";
168
- n != null && n !== "" && (n === "moment" || n === "list" || n === "team" || n === "brand-playlist" ? r = n : console.warn(
175
+ s != null && s !== "" && (s === "moment" || s === "list" || s === "team" || s === "brand-playlist" ? r = s : console.warn(
169
176
  "[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",
170
- n
177
+ s
171
178
  ));
172
- const d = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", c = d ? d.split(",").filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
173
- let m;
174
- a != null && a !== "" && (a === "google" || a === "outlook" ? m = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a));
175
- const l = e.getAttribute("data-api-source-id") ?? void 0, u = e.getAttribute("data-external-event-id") ?? void 0;
176
- if (!t) {
179
+ const c = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", u = c ? c.split(",").filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
180
+ let p;
181
+ a != null && a !== "" && (a === "google" || a === "outlook" ? p = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a));
182
+ const l = e.getAttribute("data-api-source-id") ?? void 0, d = e.getAttribute("data-external-event-id") ?? void 0, m = e.getAttribute("data-external-event-id-prefix") ?? void 0;
183
+ if (d && m && console.warn(
184
+ "[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."
185
+ ), !t) {
177
186
  console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");
178
187
  return;
179
188
  }
180
- const p = r === "team" || r === "brand-playlist";
189
+ const h = r === "team" || r === "brand-playlist";
181
190
  this.emitAnalytics("embed.trigger.click", {
182
191
  teamSlug: t,
183
192
  triggerType: r,
184
- momentIds: p ? [] : c
193
+ momentIds: h ? [] : u
185
194
  }), this.open({
186
195
  teamSlug: t,
187
196
  momentSlug: i,
188
197
  listSlug: o,
189
198
  triggerType: r,
190
- ids: c,
191
- calendar: m ?? void 0,
199
+ ids: u,
200
+ calendar: p ?? void 0,
192
201
  apiSourceId: l,
193
- externalEventId: u
202
+ externalEventId: m && !d ? void 0 : d,
203
+ externalEventIdPrefix: d ? void 0 : m
194
204
  });
195
205
  }
196
206
  /** Drop moment/list/source scoping for brand-level subscription triggers. */
@@ -234,8 +244,8 @@ class E {
234
244
  display: "flex",
235
245
  flexDirection: "column"
236
246
  });
237
- const n = document.createElement("div");
238
- n.setAttribute("role", "dialog"), n.setAttribute("aria-modal", "true"), n.setAttribute("aria-label", "Calendar sync modal"), n.tabIndex = -1, Object.assign(n.style, {
247
+ const s = document.createElement("div");
248
+ s.setAttribute("role", "dialog"), s.setAttribute("aria-modal", "true"), s.setAttribute("aria-label", "Calendar sync modal"), s.tabIndex = -1, Object.assign(s.style, {
239
249
  position: "relative",
240
250
  width: "100%",
241
251
  maxHeight: "80vh",
@@ -270,17 +280,17 @@ class E {
270
280
  height: "420px",
271
281
  overflow: "hidden",
272
282
  transition: "height 0.15s ease"
273
- }), n.appendChild(r), n.appendChild(this.iframe);
274
- const d = document.createElement("div");
275
- Object.assign(d.style, {
283
+ }), s.appendChild(r), s.appendChild(this.iframe);
284
+ const c = document.createElement("div");
285
+ Object.assign(c.style, {
276
286
  display: "flex",
277
287
  alignItems: "center",
278
288
  justifyContent: "center",
279
289
  gap: "6px",
280
290
  padding: "12px 0"
281
291
  });
282
- const c = document.createElement("span");
283
- c.textContent = "Powered by", Object.assign(c.style, {
292
+ const u = document.createElement("span");
293
+ u.textContent = "Powered by", Object.assign(u.style, {
284
294
  fontFamily: "Inter, system-ui, -apple-system, sans-serif",
285
295
  fontSize: "12px",
286
296
  lineHeight: "16px",
@@ -290,24 +300,23 @@ class E {
290
300
  a.alt = "Moment", a.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(a.style, {
291
301
  height: "auto",
292
302
  width: "67px"
293
- }), d.appendChild(c), d.appendChild(a), o.appendChild(n), o.appendChild(d), this.overlay.appendChild(o), document.body.appendChild(this.overlay);
294
- const m = (l) => {
303
+ }), c.appendChild(u), c.appendChild(a), o.appendChild(s), o.appendChild(c), this.overlay.appendChild(o), document.body.appendChild(this.overlay);
304
+ const p = (l) => {
295
305
  if (l.key !== "Tab") return;
296
- const u = n.querySelectorAll(
306
+ const d = s.querySelectorAll(
297
307
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
298
308
  );
299
- if (u.length === 0) {
300
- l.preventDefault(), n.focus();
309
+ if (d.length === 0) {
310
+ l.preventDefault(), s.focus();
301
311
  return;
302
312
  }
303
- const p = u[0], h = u[u.length - 1], g = document.activeElement;
304
- l.shiftKey && g === p ? (l.preventDefault(), h.focus()) : !l.shiftKey && g === h && (l.preventDefault(), p.focus());
313
+ const m = d[0], h = d[d.length - 1], g = document.activeElement;
314
+ l.shiftKey && g === m ? (l.preventDefault(), h.focus()) : !l.shiftKey && g === h && (l.preventDefault(), m.focus());
305
315
  };
306
- n.addEventListener("keydown", m), 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 = m;
316
+ s.addEventListener("keydown", p), 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 = p;
307
317
  }
308
318
  buildIframeUrl(e) {
309
- const t = new URLSearchParams();
310
- return t.set("teamSlug", e.teamSlug), e.momentSlug && t.set("momentSlug", e.momentSlug), e.listSlug && t.set("listSlug", e.listSlug), t.set("triggerType", e.triggerType), e.ids?.length && t.set("ids", e.ids.join(",")), e.calendar && t.set("calendar", e.calendar), e.apiSourceId && t.set("subscriptionId", e.apiSourceId), e.externalEventId && t.set("externalId", e.externalEventId), t.set("returnOrigin", window.location.origin), t.set("embed", "1"), `${this.liveBaseUrl}/en/embed/sync?${t.toString()}`;
319
+ return b(this.liveBaseUrl, e);
311
320
  }
312
321
  // ── Private: message handling ─────────────────────────────────────
313
322
  setupMessageListener() {
@@ -410,17 +419,17 @@ class E {
410
419
  });
411
420
  }
412
421
  }
413
- function S(s) {
422
+ function I(n) {
414
423
  const e = document.createElement("button");
415
- return e.type = "button", e.classList.add("moment-sync-trigger"), s.className && s.className.split(" ").forEach((t) => {
424
+ return e.type = "button", e.classList.add("moment-sync-trigger"), n.className && n.className.split(" ").forEach((t) => {
416
425
  t && e.classList.add(t);
417
- }), 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.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), e.textContent = s.label ?? "Add to Calendar", e;
426
+ }), 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.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;
418
427
  }
419
428
  if (typeof window < "u") {
420
- const s = window;
421
- s.MomentSdk = E, s.createMomentButton = S;
429
+ const n = window;
430
+ n.MomentSdk = T, n.createMomentButton = I;
422
431
  }
423
432
  export {
424
- E as MomentSdk,
425
- S as createMomentButton
433
+ T as MomentSdk,
434
+ I as createMomentButton
426
435
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -13,8 +13,10 @@ export declare function createMomentButton(options: {
13
13
  calendar?: CalendarProvider;
14
14
  /** Sync subscription source id; pairs with `externalEventId` for server-side resolution. */
15
15
  apiSourceId?: string;
16
- /** External event identifier resolved against `apiSourceId`. */
16
+ /** External event identifier resolved against `apiSourceId` (exact showing). */
17
17
  externalEventId?: string;
18
+ /** Upstream event id — Live resolves all showings and prompts user to pick. */
19
+ externalEventIdPrefix?: string;
18
20
  label?: string;
19
21
  className?: string;
20
22
  }): HTMLButtonElement;
@@ -0,0 +1,3 @@
1
+ import type { MomentSdkOpenPayload } from './types';
2
+ /** Build the Live embed sync iframe URL from an open payload. */
3
+ export declare function buildSyncIframeUrl(liveBaseUrl: string, payload: MomentSdkOpenPayload, returnOrigin?: string): string;
@@ -54,9 +54,17 @@ export interface MomentSdkOpenPayload {
54
54
  /**
55
55
  * External event identifier emitted by the publisher's source feed (e.g. the
56
56
  * provider's native event id). Resolved against `apiSourceId` to a moment.
57
+ * Use the composite `event_id:showing_id` format for a single showing.
57
58
  * Forwarded to the Live embed as the `externalId` query param.
59
+ * Mutually exclusive with `externalEventIdPrefix`.
58
60
  */
59
61
  externalEventId?: string;
62
+ /**
63
+ * Upstream event id without showing suffix — Live fetches all showings for
64
+ * that event and prompts the user to pick one. Forwarded as `externalIdPrefix`.
65
+ * Mutually exclusive with `externalEventId`.
66
+ */
67
+ externalEventIdPrefix?: string;
60
68
  }
61
69
  /** Backend subscription resource type that the embed will create */
62
70
  export type MomentSubscriptionType = 'moment' | 'list' | 'team' | 'playlist';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentco-ai/moment-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.3.1-dev.20",
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)",
@@ -62,6 +62,7 @@
62
62
  "format": "prettier --write .",
63
63
  "format:check": "prettier --check .",
64
64
  "quality": "pnpm run type-check && pnpm run lint && pnpm run format",
65
+ "test": "vitest run",
65
66
  "examples": "pnpm build && node examples/serve.mjs",
66
67
  "prepare": "husky"
67
68
  },
@@ -72,6 +73,7 @@
72
73
  "prettier": "^3.2.5",
73
74
  "typescript": "^5.9.2",
74
75
  "typescript-eslint": "^8.32.1",
75
- "vite": "^7.1.5"
76
+ "vite": "^7.1.5",
77
+ "vitest": "^3.2.4"
76
78
  }
77
79
  }