@hopecloud/jetstream-player 1.1.4 → 1.3.0-rc.1

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
@@ -1 +1,76 @@
1
- Docs: https://jetstream-player.docs.hope.cloud/
1
+ # @hopecloud/jetstream-player
2
+
3
+ Control an embedded [Jetstream](https://jetstream-player.docs.hope.cloud/) player
4
+ from your page — play, pause, seek, mute, read playback state, and subscribe to
5
+ events — across the iframe boundary, with no server glue.
6
+
7
+ **[Full documentation →](https://jetstream-player.docs.hope.cloud/)**
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm i @hopecloud/jetstream-player
13
+ ```
14
+
15
+ ## Quick start
16
+
17
+ ```ts
18
+ import { JetstreamPlayer } from '@hopecloud/jetstream-player';
19
+
20
+ const player = new JetstreamPlayer('#player', {
21
+ mediaId: 'jsv:xxxxxxxxxx',
22
+ events: {
23
+ ready: () => console.log('ready'),
24
+ timeupdate: (time) => console.log('time', time),
25
+ },
26
+ });
27
+
28
+ player.play();
29
+ const muted = await player.isMuted();
30
+ ```
31
+
32
+ `mediaId` is the only required option; the library creates and mounts the iframe
33
+ at the given selector.
34
+
35
+ ## What you can do
36
+
37
+ - **Commands** — `play()`, `pause()`, `mute()`, `unmute()`, `seekTo(seconds)`,
38
+ `playNext()`, `playPrev()`.
39
+ - **Getters** (Promise-based) — `isMuted()`, `isPaused()`,
40
+ `getVideoCurrentTime()`, `getDuration()`. They reject after 5s if the player
41
+ doesn't reply.
42
+ - **Events** — subscribe via `options.events`: `ready`, `timeupdate`, `play`,
43
+ `pause`, `loadedmetadata`, `ended`, `volumechange`, `error`.
44
+ - **Teardown** — `dispose()` removes listeners and tears down Picture-in-Picture.
45
+
46
+ See the [options](https://jetstream-player.docs.hope.cloud/api/player-options)
47
+ and [methods](https://jetstream-player.docs.hope.cloud/api/player-methods)
48
+ reference for the full list.
49
+
50
+ ## Targeting a non-prod embed
51
+
52
+ By default the player loads the production embed. Point it at another host
53
+ (staging/dev) with `origin`:
54
+
55
+ ```ts
56
+ new JetstreamPlayer('#player', {
57
+ mediaId: 'jsv:xxxxxxxxxx',
58
+ origin: 'https://embed-stage.jetstream.studio',
59
+ });
60
+ ```
61
+
62
+ Communication is cross-origin `postMessage`, and the SDK learns the iframe's
63
+ real origin at runtime — so it keeps working even when the embed redirects.
64
+
65
+ ## Development
66
+
67
+ ```bash
68
+ pnpm install
69
+ pnpm build # rolldown + .d.ts
70
+ pnpm test # vitest
71
+ pnpm lint
72
+ pnpm typecheck
73
+ pnpm docs:dev # vitepress docs
74
+ ```
75
+
76
+ Architecture decisions are recorded in [`adr/`](./adr).
@@ -0,0 +1,7 @@
1
+ import type { EmbedFrameOptions } from './player.type';
2
+ export declare const buildEmbedSrc: (options: EmbedFrameOptions) => string;
3
+ export interface MountedFrame {
4
+ iframe: HTMLIFrameElement;
5
+ src: string;
6
+ }
7
+ export declare const mountEmbedFrame: (selector: string, options: EmbedFrameOptions) => MountedFrame;
@@ -2,7 +2,11 @@ export declare class IFramePiPPlugin {
2
2
  containerSelector: string;
3
3
  iframe: HTMLIFrameElement;
4
4
  private observer;
5
+ private originalParentHeight;
6
+ private originalIframeStyle;
7
+ private parent;
5
8
  constructor(containerSelector: string, iframe: HTMLIFrameElement);
6
9
  init(): void;
10
+ destroy(): void;
7
11
  destory(): void;
8
12
  }
@@ -1,3 +1,2 @@
1
1
  export { JetstreamPlayer } from './player';
2
2
  export * from './player.type';
3
- export * from './utils';
package/dist/index.js CHANGED
@@ -1,2 +1,300 @@
1
- const e=["ready","timeupdate","play","pause","loadedmetadata","ended","volumechange","error"];class t{options;iframe=null;constructor(e){this.options=e,this.iframe=this.generateIframe()}generateIframe(){const e=document.createElement("iframe");return e.allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",e.allowFullscreen=!0,e.frameBorder="0",e.src=this.options.src,e.width=this.options.width||"480px",e.height=this.options.height||"270px",e.classList.add("jet-stream-iframe-player"),e}insertIframe(e){const t=document.querySelector(this.options.selector);t?t.appendChild(e):console.error(`Div with id "${this.options.selector}" not found.`)}}class i{options;origin="https://jstre.am";baseURL=new URL(this.origin);constructor(e){if(""===e.mediaId)throw new Error("mediaId not provided!");this.options=e}getRegionBasedOrigin(){const e=this.baseURL,t=e.hostname.split(".");return this.options.region&&"eu"===this.options.region&&(t.length>2?t.splice(1,0,"eu"):t.splice(0,0,"eu")),e.hostname=t.join("."),e.origin}appendPath(e,t){const i=String(e).trim();return 0===i.indexOf("/")?t+=i:t+=`/${i}`,t}buildSrc(){const e=new URL(`${this.getRegionBasedOrigin()}/embed/${this.options.mediaId}`);return this.options.playerId&&(e.href=this.appendPath(this.options.playerId,e.href)),"eu"===this.options.region&&e.searchParams.append("region","eu"),this.options.audioLang&&e.searchParams.append("audioLang",this.options.audioLang.toLowerCase()),this.options.subtitleLang&&e.searchParams.append("subtitleLang",this.options.subtitleLang.toLowerCase()),e.href}}const s="fixed",r="20px",n="20px",o="330px",a="180px";class h{containerSelector;iframe;observer=null;constructor(e,t){this.containerSelector=e,this.iframe=t,this.containerSelector=e,this.iframe=t,this.init()}init(){if(!this.iframe||!this.containerSelector)return;const e=document.querySelector(this.containerSelector);if(!e)throw new Error("Selector of iframe not found");this.observer=new IntersectionObserver((([{isIntersecting:t}])=>{if(t)e.style.height="auto",this.iframe.removeAttribute("style");else{const t=e.getBoundingClientRect().height;e.style.height=`${t}px`,this.iframe.style.position=s,this.iframe.style.bottom=r,this.iframe.style.right=n,this.iframe.style.width=o,this.iframe.style.height=a}}),{root:null,rootMargin:"0px",threshold:.1}),this.observer.observe(e)}destory(){this.observer&&(this.observer.disconnect(),this.observer=null)}}const d=e=>JSON.parse(e.data),c=e=>JSON.stringify(e),p=e=>{if("string"!=typeof e)return!1;try{return JSON.parse(e),!0}catch(e){return console.log("Error parsing JSON",e),!1}};class m{iframe=null;options;registeredEvents={};iFramePiP=null;constructor(e,s){this.options=s;const r=new i({mediaId:s.mediaId,playerId:s.playerId,region:s.region}).buildSrc(),n=new t({selector:e,src:r,width:this.options.width,height:this.options.height});this.iframe=n.iframe,this.iframe&&(n.insertIframe(this.iframe),this.iframe.addEventListener("load",(()=>{this.registerEvents(),this.subscribeToRegisteredEvents()}),{once:!0}),void 0!==this.options.sticky&&this.options.sticky&&(this.iFramePiP=new h(e,this.iframe)))}registerEvents(){!this.options.events||this.options.events&&Object.keys(this.options.events).length<0||Object.entries(this.options.events).forEach((([e,t])=>{e in this.registeredEvents||(this.registeredEvents[e]=t)}))}subscribeEventHandler(e){if(e.source!==this.iframe.contentWindow)return;const t=d(e),i=t.type;i in this.registeredEvents&&this.registeredEvents[i](t.value)}subscribeToRegisteredEvents(){window.addEventListener("message",this.subscribeEventHandler.bind(this))}dispatch(e){if(!this.iframe||!this.iframe.contentWindow)throw new Error("iframe is not provided or parent window not found!");this.iframe.contentWindow.postMessage(c(e),"*")}getterDispatch(e){return new Promise((t=>{this.dispatch(e),window.addEventListener("message",(i=>{const s=d(i);s.type===e.name&&t(s.value)}),{once:!0})}))}play(){this.dispatch({name:"play"})}pause(){this.dispatch({name:"pause"})}mute(){this.dispatch({name:"mute"})}unmute(){this.dispatch({name:"unmute"})}seekTo(e){this.dispatch({name:"seekTo",params:e})}playNext(){this.dispatch({name:"play-next"})}playPrev(){this.dispatch({name:"play-prev"})}isMuted(){return this.getterDispatch({name:"is-muted"})}isPaused(){return this.getterDispatch({name:"is-paused"})}getVideoCurrentTime(){return this.getterDispatch({name:"get-video-current-time"})}getDuration(){return this.getterDispatch({name:"get-duration"})}dispose(){window.removeEventListener("message",this.subscribeEventHandler.bind(this)),this.registeredEvents={},this.iFramePiP?.destory()}}export{m as JetstreamPlayer,c as denormalizeEventData,p as isValidEventData,d as normalizeEventData,e as playerListenEvents};
2
- //# sourceMappingURL=index.js.map
1
+ //#region src/embed-frame.ts
2
+ const DEFAULT_ORIGIN = "https://jstre.am";
3
+ const regionOrigin = (origin, region) => {
4
+ const url = new URL(origin);
5
+ if (region === "eu") {
6
+ const parts = url.hostname.split(".");
7
+ parts.splice(parts.length > 2 ? 1 : 0, 0, "eu");
8
+ url.hostname = parts.join(".");
9
+ }
10
+ return url.origin;
11
+ };
12
+ const buildEmbedSrc = (options) => {
13
+ if (options.mediaId === "") throw new Error("mediaId not provided!");
14
+ const origin = regionOrigin(options.origin ?? DEFAULT_ORIGIN, options.region);
15
+ const path = options.playerId ? `/embed/${options.mediaId}/${options.playerId}` : `/embed/${options.mediaId}`;
16
+ const url = new URL(`${origin}${path}`);
17
+ if (options.region === "eu") url.searchParams.append("region", "eu");
18
+ if (options.audioLang) url.searchParams.append("audioLang", options.audioLang.toLowerCase());
19
+ if (options.subtitleLang) url.searchParams.append("subtitleLang", options.subtitleLang.toLowerCase());
20
+ return url.href;
21
+ };
22
+ const mountEmbedFrame = (selector, options) => {
23
+ const src = buildEmbedSrc(options);
24
+ const iframe = document.createElement("iframe");
25
+ iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
26
+ iframe.allowFullscreen = true;
27
+ iframe.frameBorder = "0";
28
+ iframe.title = "Jetstream player";
29
+ iframe.src = src;
30
+ iframe.width = options.width || "480px";
31
+ iframe.height = options.height || "270px";
32
+ iframe.classList.add("jet-stream-iframe-player");
33
+ const parent = document.querySelector(selector);
34
+ if (parent) parent.appendChild(iframe);
35
+ else console.error(`Element matching selector "${selector}" not found.`);
36
+ return {
37
+ iframe,
38
+ src
39
+ };
40
+ };
41
+ //#endregion
42
+ //#region src/iframe-pip-plugin.ts
43
+ const PiP_STYLES = {
44
+ position: "fixed",
45
+ bottom: "20px",
46
+ right: "20px",
47
+ width: "330px",
48
+ height: "180px"
49
+ };
50
+ var IFramePiPPlugin = class {
51
+ containerSelector;
52
+ iframe;
53
+ observer = null;
54
+ originalParentHeight = null;
55
+ originalIframeStyle = null;
56
+ parent = null;
57
+ constructor(containerSelector, iframe) {
58
+ this.containerSelector = containerSelector;
59
+ this.iframe = iframe;
60
+ this.containerSelector = containerSelector;
61
+ this.iframe = iframe;
62
+ this.init();
63
+ }
64
+ init() {
65
+ if (!this.iframe || !this.containerSelector) return;
66
+ const parent = document.querySelector(this.containerSelector);
67
+ if (!parent) throw new Error("Selector of iframe not found");
68
+ this.parent = parent;
69
+ this.originalParentHeight = parent.style.height || null;
70
+ this.originalIframeStyle = this.iframe.getAttribute("style");
71
+ this.observer = new IntersectionObserver(([{ isIntersecting }]) => {
72
+ if (!isIntersecting) {
73
+ const height = parent.getBoundingClientRect().height;
74
+ parent.style.height = `${height}px`;
75
+ this.iframe.style.position = PiP_STYLES.position;
76
+ this.iframe.style.bottom = PiP_STYLES.bottom;
77
+ this.iframe.style.right = PiP_STYLES.right;
78
+ this.iframe.style.width = PiP_STYLES.width;
79
+ this.iframe.style.height = PiP_STYLES.height;
80
+ } else {
81
+ parent.style.height = this.originalParentHeight ?? "";
82
+ if (this.originalIframeStyle) this.iframe.setAttribute("style", this.originalIframeStyle);
83
+ else this.iframe.removeAttribute("style");
84
+ }
85
+ }, {
86
+ root: null,
87
+ rootMargin: "0px",
88
+ threshold: .1
89
+ });
90
+ this.observer.observe(parent);
91
+ }
92
+ destroy() {
93
+ if (this.observer) {
94
+ this.observer.disconnect();
95
+ this.observer = null;
96
+ }
97
+ if (this.parent) this.parent.style.height = this.originalParentHeight ?? "";
98
+ if (this.iframe) if (this.originalIframeStyle) this.iframe.setAttribute("style", this.originalIframeStyle);
99
+ else this.iframe.removeAttribute("style");
100
+ }
101
+ destory() {
102
+ this.destroy();
103
+ }
104
+ };
105
+ //#endregion
106
+ //#region src/protocol.ts
107
+ const encode = (command) => JSON.stringify(command);
108
+ const decode = (raw) => {
109
+ if (typeof raw !== "string") return null;
110
+ try {
111
+ const parsed = JSON.parse(raw);
112
+ if (typeof parsed === "object" && parsed !== null && typeof parsed.type === "string") return parsed;
113
+ return null;
114
+ } catch {
115
+ return null;
116
+ }
117
+ };
118
+ //#endregion
119
+ //#region src/player-bridge.ts
120
+ const REQUEST_TIMEOUT_MS = 5e3;
121
+ var PlayerBridge = class {
122
+ transport;
123
+ targetOrigin;
124
+ listeners = /* @__PURE__ */ new Set();
125
+ detach;
126
+ constructor(transport, initialTargetOrigin) {
127
+ this.transport = transport;
128
+ this.targetOrigin = initialTargetOrigin;
129
+ this.detach = this.transport.subscribe((message, origin) => {
130
+ const event = decode(message);
131
+ if (!event) return;
132
+ this.targetOrigin = origin;
133
+ this.listeners.forEach((listener) => listener(event));
134
+ });
135
+ }
136
+ send(command) {
137
+ this.transport.send(encode(command), this.targetOrigin);
138
+ }
139
+ subscribe(listener) {
140
+ this.listeners.add(listener);
141
+ return () => {
142
+ this.listeners.delete(listener);
143
+ };
144
+ }
145
+ request(command, timeoutMs = REQUEST_TIMEOUT_MS) {
146
+ return new Promise((resolve, reject) => {
147
+ let off = () => void 0;
148
+ const timer = setTimeout(() => {
149
+ off();
150
+ reject(/* @__PURE__ */ new Error(`Timed out waiting for "${command.name}" response`));
151
+ }, timeoutMs);
152
+ off = this.subscribe((event) => {
153
+ if (event.type === command.name) {
154
+ clearTimeout(timer);
155
+ off();
156
+ resolve(event.value);
157
+ }
158
+ });
159
+ this.send(command);
160
+ });
161
+ }
162
+ dispose() {
163
+ this.detach();
164
+ this.listeners.clear();
165
+ }
166
+ };
167
+ //#endregion
168
+ //#region src/player-transport.ts
169
+ const windowTransport = (iframe) => ({
170
+ send: (message, targetOrigin) => {
171
+ iframe.contentWindow?.postMessage(message, targetOrigin);
172
+ },
173
+ subscribe: (handler) => {
174
+ const listener = (event) => {
175
+ if (event.source !== iframe.contentWindow) return;
176
+ handler(event.data, event.origin);
177
+ };
178
+ window.addEventListener("message", listener);
179
+ return () => {
180
+ window.removeEventListener("message", listener);
181
+ };
182
+ }
183
+ });
184
+ //#endregion
185
+ //#region src/player.ts
186
+ var JetstreamPlayer = class {
187
+ options;
188
+ listeners = /* @__PURE__ */ new Map();
189
+ iFramePiP = null;
190
+ bridge = null;
191
+ constructor(el, options) {
192
+ this.options = options;
193
+ const { iframe, src } = mountEmbedFrame(el, options);
194
+ let initialTargetOrigin;
195
+ try {
196
+ initialTargetOrigin = new URL(src).origin;
197
+ } catch {
198
+ initialTargetOrigin = "*";
199
+ }
200
+ this.bridge = new PlayerBridge(windowTransport(iframe), initialTargetOrigin);
201
+ iframe.addEventListener("load", () => {
202
+ this.registerEvents();
203
+ this.bridge?.subscribe((event) => {
204
+ this.emit(event.type, event.value);
205
+ });
206
+ }, { once: true });
207
+ if (typeof this.options.sticky !== "undefined" && this.options.sticky) this.iFramePiP = new IFramePiPPlugin(el, iframe);
208
+ }
209
+ registerEvents() {
210
+ if (!this.options.events) return;
211
+ Object.entries(this.options.events).forEach(([eventName, handler]) => {
212
+ if (handler) this.on(eventName, handler);
213
+ });
214
+ }
215
+ emit(type, value) {
216
+ const handlers = this.listeners.get(type);
217
+ if (!handlers) return;
218
+ [...handlers].forEach((handler) => handler(value));
219
+ }
220
+ on(event, handler) {
221
+ let handlers = this.listeners.get(event);
222
+ if (!handlers) {
223
+ handlers = /* @__PURE__ */ new Set();
224
+ this.listeners.set(event, handlers);
225
+ }
226
+ handlers.add(handler);
227
+ return () => {
228
+ this.off(event, handler);
229
+ };
230
+ }
231
+ off(event, handler) {
232
+ if (!handler) {
233
+ this.listeners.delete(event);
234
+ return;
235
+ }
236
+ const handlers = this.listeners.get(event);
237
+ if (!handlers) return;
238
+ handlers.delete(handler);
239
+ if (handlers.size === 0) this.listeners.delete(event);
240
+ }
241
+ request(command) {
242
+ if (!this.bridge) return Promise.reject(/* @__PURE__ */ new Error("iframe is not provided or parent window not found!"));
243
+ return this.bridge.request(command);
244
+ }
245
+ play() {
246
+ this.bridge?.send({ name: "play" });
247
+ }
248
+ pause() {
249
+ this.bridge?.send({ name: "pause" });
250
+ }
251
+ mute() {
252
+ this.bridge?.send({ name: "mute" });
253
+ }
254
+ unmute() {
255
+ this.bridge?.send({ name: "unmute" });
256
+ }
257
+ seekTo(seconds) {
258
+ this.bridge?.send({
259
+ name: "seekTo",
260
+ params: seconds
261
+ });
262
+ }
263
+ playNext() {
264
+ this.bridge?.send({ name: "play-next" });
265
+ }
266
+ playPrev() {
267
+ this.bridge?.send({ name: "play-prev" });
268
+ }
269
+ isMuted() {
270
+ return this.request({ name: "is-muted" });
271
+ }
272
+ isPaused() {
273
+ return this.request({ name: "is-paused" });
274
+ }
275
+ getVideoCurrentTime() {
276
+ return this.request({ name: "get-video-current-time" });
277
+ }
278
+ getDuration() {
279
+ return this.request({ name: "get-duration" });
280
+ }
281
+ dispose() {
282
+ this.bridge?.dispose();
283
+ this.listeners.clear();
284
+ this.iFramePiP?.destroy();
285
+ }
286
+ };
287
+ //#endregion
288
+ //#region src/player.type.ts
289
+ const playerListenEvents = [
290
+ "ready",
291
+ "timeupdate",
292
+ "play",
293
+ "pause",
294
+ "loadedmetadata",
295
+ "ended",
296
+ "volumechange",
297
+ "error"
298
+ ];
299
+ //#endregion
300
+ export { JetstreamPlayer, playerListenEvents };
@@ -0,0 +1,13 @@
1
+ import type { PlayerCommand, PlayerEvent } from './player.type';
2
+ import type { PlayerTransport } from './player-transport';
3
+ export declare class PlayerBridge {
4
+ private readonly transport;
5
+ private targetOrigin;
6
+ private readonly listeners;
7
+ private readonly detach;
8
+ constructor(transport: PlayerTransport, initialTargetOrigin: string);
9
+ send(command: PlayerCommand): void;
10
+ subscribe(listener: (event: PlayerEvent) => void): () => void;
11
+ request<T>(command: PlayerCommand, timeoutMs?: number): Promise<T>;
12
+ dispose(): void;
13
+ }
@@ -0,0 +1,5 @@
1
+ export interface PlayerTransport {
2
+ send(message: string, targetOrigin: string): void;
3
+ subscribe(handler: (message: string, origin: string) => void): () => void;
4
+ }
5
+ export declare const windowTransport: (iframe: HTMLIFrameElement) => PlayerTransport;
@@ -1,15 +1,15 @@
1
- import type { JetstreamPlayerOptions } from './player.type';
1
+ import type { PlayerEvents, JetstreamPlayerOptions, PlayerEventHandler } from './player.type';
2
2
  export declare class JetstreamPlayer {
3
- private iframe;
4
3
  private options;
5
- private registeredEvents;
4
+ private readonly listeners;
6
5
  private iFramePiP;
6
+ private bridge;
7
7
  constructor(el: string, options: JetstreamPlayerOptions);
8
8
  private registerEvents;
9
- private subscribeEventHandler;
10
- private subscribeToRegisteredEvents;
11
- private dispatch;
12
- private getterDispatch;
9
+ private emit;
10
+ on(event: PlayerEvents, handler: PlayerEventHandler): () => void;
11
+ off(event: PlayerEvents, handler?: PlayerEventHandler): void;
12
+ private request;
13
13
  play(): void;
14
14
  pause(): void;
15
15
  mute(): void;
@@ -1,29 +1,27 @@
1
1
  export declare const playerListenEvents: readonly ["ready", "timeupdate", "play", "pause", "loadedmetadata", "ended", "volumechange", "error"];
2
2
  export type PlayerEvents = 'ready' | 'timeupdate' | 'play' | 'pause' | 'loadedmetadata' | 'ended' | 'volumechange' | 'error';
3
3
  export type PlayerGetterEvents = 'is-muted' | 'get-video-current-time' | 'get-duration' | 'is-paused';
4
- export type PlayerEvent = {
4
+ export interface PlayerEvent {
5
5
  type: PlayerEvents | PlayerGetterEvents;
6
6
  value?: unknown;
7
- };
7
+ }
8
+ export type PlayerEventHandler = (value?: unknown) => void;
8
9
  export type PlayerCommandEvent = 'play' | 'pause' | 'mute' | 'unmute' | 'seekTo' | 'play-next' | 'play-prev';
9
- export type PlayerCommand = {
10
+ export interface PlayerCommand {
10
11
  name: PlayerCommandEvent | PlayerGetterEvents;
11
12
  params?: unknown;
12
- };
13
- export type IFrameBuilderOpt = {
14
- selector: string;
15
- src: string;
16
- width?: string;
17
- height?: string;
18
- };
19
- export type IFrameBuilderSrcOpt = {
13
+ }
14
+ export interface EmbedFrameOptions {
20
15
  mediaId: string;
21
16
  playerId?: string;
22
17
  region?: 'eu';
23
18
  audioLang?: string;
24
19
  subtitleLang?: string;
25
- };
20
+ origin?: string;
21
+ width?: string;
22
+ height?: string;
23
+ }
26
24
  export type JetstreamPlayerOptions = {
27
25
  sticky?: boolean;
28
- events?: Partial<Record<PlayerEvents, (value?: unknown) => void>>;
29
- } & Omit<IFrameBuilderOpt, 'selector' | 'src'> & IFrameBuilderSrcOpt;
26
+ events?: Partial<Record<PlayerEvents, PlayerEventHandler>>;
27
+ } & EmbedFrameOptions;
@@ -0,0 +1,3 @@
1
+ import type { PlayerCommand, PlayerEvent } from './player.type';
2
+ export declare const encode: (command: PlayerCommand) => string;
3
+ export declare const decode: (raw: string) => PlayerEvent | null;
package/package.json CHANGED
@@ -1,59 +1,54 @@
1
1
  {
2
2
  "name": "@hopecloud/jetstream-player",
3
- "version": "1.1.4",
4
- "description": "Jetstream Embed Player API",
3
+ "version": "1.3.0-rc.1",
4
+ "description": "Jetstream Embed Player SDK",
5
5
  "type": "module",
6
- "author": {
7
- "email": "code@deepvision.team",
8
- "name": "DeepVision Code"
9
- },
6
+ "sideEffects": false,
10
7
  "repository": {
11
8
  "type": "git",
12
- "url": "https://github.com/deeepvision/hcc-jetstream-player.git"
9
+ "url": "git+https://github.com/hopechannel/hopecloud.git",
10
+ "directory": "libs/jetstream-player"
11
+ },
12
+ "homepage": "https://jetstream-player.docs.hope.cloud/",
13
+ "engines": {
14
+ "node": "24"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
13
18
  },
14
19
  "main": "dist/index.js",
15
- "module": "dist/index.js",
16
- "types": "dist/@types/index.d.ts",
20
+ "files": [
21
+ "dist"
22
+ ],
17
23
  "exports": {
18
24
  ".": {
19
- "import": {
20
- "types": "./dist/@types/index.d.ts",
21
- "default": "./dist/index.js"
22
- }
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
23
27
  }
24
28
  },
25
- "files": [
26
- "dist"
27
- ],
29
+ "author": {
30
+ "email": "code@deepvision.software",
31
+ "name": "DV Software"
32
+ },
33
+ "devDependencies": {
34
+ "@deepvision/eslint-plugin": "2.0.3",
35
+ "@types/node": "24.12.0",
36
+ "eslint": "10.1.0",
37
+ "happy-dom": "^20.4.0",
38
+ "rolldown": "^1.1.4",
39
+ "typescript": "6.0.3",
40
+ "vitepress": "^1.6.4",
41
+ "vitest": "^4.0.18"
42
+ },
28
43
  "scripts": {
29
- "build": "rollup -c",
30
- "pack": "npm run build && npm pack",
31
- "publish:patch": "npm version patch -m \"release: %s\"",
32
- "publish:minor": "npm version minor -m \"release: %s\"",
33
- "postpublish:patch": "git push origin && git push origin --tags",
34
- "postpublish:minor": "git push origin && git push origin --tags",
44
+ "build": "rolldown --config && tsc --emitDeclarationOnly",
35
45
  "docs:dev": "vitepress dev docs",
36
46
  "docs:build": "vitepress build docs",
37
47
  "docs:preview": "vitepress preview docs",
38
48
  "lint": "eslint",
39
49
  "lint:fix": "eslint --fix",
40
- "typecheck": "tsc --noEmit"
41
- },
42
- "devDependencies": {
43
- "@eslint/eslintrc": "^3.2.0",
44
- "@eslint/js": "^9.17.0",
45
- "@rollup/plugin-terser": "^0.4.4",
46
- "@typescript-eslint/eslint-plugin": "^8.19.1",
47
- "@typescript-eslint/parser": "^8.19.1",
48
- "eslint": "^9.17.0",
49
- "eslint-config-prettier": "^9.1.0",
50
- "globals": "^15.14.0",
51
- "prettier": "3.4.2",
52
- "rimraf": "^6.0.1",
53
- "rollup": "^4.30.1",
54
- "rollup-plugin-delete": "^2.1.0",
55
- "rollup-plugin-typescript2": "^0.36.0",
56
- "typescript": "^5.7.3",
57
- "vitepress": "1.5.0"
50
+ "type-check": "tsc --noEmit",
51
+ "test": "vitest",
52
+ "test:run": "vitest run"
58
53
  }
59
- }
54
+ }
@@ -1,10 +0,0 @@
1
- import { type IFrameBuilderSrcOpt } from './player.type';
2
- export declare class IFrameBuilderSrc {
3
- private options;
4
- origin: string;
5
- baseURL: URL;
6
- constructor(options: IFrameBuilderSrcOpt);
7
- getRegionBasedOrigin(): string;
8
- appendPath(path: string, builtUrl: string): string;
9
- buildSrc(): string;
10
- }
@@ -1,8 +0,0 @@
1
- import { type IFrameBuilderOpt } from './player.type';
2
- export declare class IFrameBuilder {
3
- options: IFrameBuilderOpt;
4
- iframe: HTMLIFrameElement | null;
5
- constructor(options: IFrameBuilderOpt);
6
- private generateIframe;
7
- insertIframe(iframe: HTMLIFrameElement): void;
8
- }
@@ -1,3 +0,0 @@
1
- export declare const normalizeEventData: <T = unknown>(event: MessageEvent<string>) => T;
2
- export declare const denormalizeEventData: <T = unknown>(data: T) => string;
3
- export declare const isValidEventData: (rawData: string) => boolean;
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}