@tinyweb_dev/oe-meeting-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INTEGRATION.md ADDED
@@ -0,0 +1,76 @@
1
+ # Hướng dẫn tích hợp OE Meeting vào OE.School
2
+
3
+ React SDK cho phép OE.School nhúng toàn bộ phòng học trực tuyến vào trang lesson,
4
+ SSO bằng join token và trao đổi typed events/commands với iframe.
5
+
6
+ ## Tài liệu
7
+
8
+ - [Tích hợp backend OE.School](docs/BACKEND-INTEGRATION.md): credentials, external
9
+ API, tạo phòng, cấp token và kiểm tra roster.
10
+ - [Tích hợp React SDK](docs/REACT-INTEGRATION.md): component `OeMeeting`, props,
11
+ events, commands, xử lý lỗi và local development.
12
+ - [README package](README.md): ví dụ sử dụng nhanh và public API.
13
+ - [Release npm](docs/RELEASE.md): cấu hình `NPM_TOKEN`, dry-run và quy trình
14
+ phát hành bằng tag.
15
+
16
+ ## Kiến trúc
17
+
18
+ ```text
19
+ Browser OE.School
20
+ │
21
+ │ GET /api/lessons/:lessonId/meeting-access
22
+ ▼
23
+ OE.School backend
24
+ ├─ Xác thực session
25
+ ├─ Kiểm tra user thuộc roster của lesson
26
+ ├─ Loại học sinh nghỉ/không được phép tham gia
27
+ │
28
+ │ Server-to-server: tạo phòng + cấp join token
29
+ ▼
30
+ OE Meeting external API
31
+ │
32
+ │ Trả roomSlug + joinToken cho browser
33
+ ▼
34
+ <OeMeeting roomSlug={roomSlug} joinToken={joinToken} />
35
+ └─ iframe https://meet.oceanedu.org/embed/:roomSlug
36
+ ```
37
+
38
+ ## Access control hai lớp
39
+
40
+ 1. **OE.School** là nguồn dữ liệu chính của roster. Backend chỉ cấp token khi
41
+ user được phép tham gia đúng buổi học.
42
+ 2. **OE Meeting** tạo phòng với `access_level: "restricted"`, ngăn người dùng
43
+ vào qua link guest. Người dùng phải có join token hợp lệ.
44
+
45
+ Không gọi external API từ browser. `X-Client-Secret` chỉ được lưu tại backend
46
+ OE.School hoặc secret manager.
47
+
48
+ ## Luồng tích hợp tối thiểu
49
+
50
+ 1. OE Meeting cấp application credentials có scope `rooms:create` và
51
+ `rooms:retrieve` cho backend OE.School.
52
+ 2. Giáo viên mở một lesson; backend tạo phòng idempotent theo lesson ID.
53
+ 3. User mở trang học; backend xác thực session và kiểm tra roster.
54
+ 4. Backend gọi external API để cấp token riêng cho user hiện tại.
55
+ 5. Frontend nhận `roomSlug` và `joinToken`, sau đó render `OeMeeting`.
56
+ 6. Frontend xử lý events join/leave/error hoặc gửi commands mic/camera/leave.
57
+
58
+ ## Production endpoint
59
+
60
+ ```text
61
+ Meeting origin: https://meet.oceanedu.org
62
+ External API: https://meet.oceanedu.org/external-api/v1.0
63
+ Embed route: https://meet.oceanedu.org/embed/:roomSlug
64
+ ```
65
+
66
+ ## Checklist nhanh
67
+
68
+ - [ ] Client secret không xuất hiện trong browser, log hoặc repository.
69
+ - [ ] Phòng lesson dùng access level `restricted`.
70
+ - [ ] Mỗi lần cấp token đều xác thực session và kiểm tra roster.
71
+ - [ ] Học sinh nghỉ/không thuộc lesson nhận HTTP 403 từ OE.School.
72
+ - [ ] API meeting-access dùng `Cache-Control: no-store`.
73
+ - [ ] Frontend có trạng thái loading, forbidden, token expired và connection
74
+ failed.
75
+ - [ ] Không dùng `targetOrigin="*"`.
76
+ - [ ] Không log join token hoặc URL iframe đầy đủ.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # `@tinyweb_dev/oe-meeting-react`
2
+
3
+ React SDK để nhúng phòng OE Meeting vào OE.School hoặc hệ thống LMS khác.
4
+
5
+ ## Tài liệu tích hợp
6
+
7
+ - [Tổng quan và checklist](INTEGRATION.md)
8
+ - [Backend OE.School: external API, roster và join token](docs/BACKEND-INTEGRATION.md)
9
+ - [Frontend OE.School: React component, events và commands](docs/REACT-INTEGRATION.md)
10
+ - [Quy trình release npm và cấu hình `NPM_TOKEN`](docs/RELEASE.md)
11
+
12
+ ## Ví dụ nhanh
13
+
14
+ ```tsx
15
+ 'use client';
16
+
17
+ import { useRef } from 'react';
18
+ import {
19
+ OeMeeting,
20
+ type OeMeetingHandle,
21
+ } from '@tinyweb_dev/oe-meeting-react';
22
+
23
+ export function OnlineLesson({
24
+ roomSlug,
25
+ joinToken,
26
+ }: {
27
+ roomSlug: string;
28
+ joinToken: string;
29
+ }) {
30
+ const meetingRef = useRef<OeMeetingHandle>(null);
31
+
32
+ return (
33
+ <OeMeeting
34
+ ref={meetingRef}
35
+ roomSlug={roomSlug}
36
+ joinToken={joinToken}
37
+ height="100vh"
38
+ hideLeave
39
+ onJoined={(event) => console.info('joined', event.payload)}
40
+ onLeft={(event) => console.info('left', event.payload)}
41
+ />
42
+ );
43
+ }
44
+ ```
45
+
46
+ ## Bảo mật
47
+
48
+ - `joinToken` phải được backend OE.School lấy server-to-server từ external API.
49
+ - Không đưa `X-Client-Secret` vào browser.
50
+ - Backend OE.School chỉ mint token sau khi kiểm tra user thuộc roster buổi học.
51
+ - Tạo phòng buổi học với access level `restricted`.
52
+
53
+ ## Imperative API
54
+
55
+ `OeMeetingHandle` cung cấp `leave()`, `toggleMic()`, `toggleCamera()` và
56
+ `getIframe()`.
package/dist/index.cjs ADDED
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_EMBED_ORIGIN: () => DEFAULT_EMBED_ORIGIN,
24
+ DEFAULT_IFRAME_ALLOW: () => DEFAULT_IFRAME_ALLOW,
25
+ EMBED_MESSAGE_SOURCE: () => EMBED_MESSAGE_SOURCE,
26
+ EMBED_PROTOCOL_VERSION: () => EMBED_PROTOCOL_VERSION,
27
+ EmbedCommandAction: () => EmbedCommandAction,
28
+ EmbedCommandType: () => EmbedCommandType,
29
+ EmbedErrorCode: () => EmbedErrorCode,
30
+ EmbedEventType: () => EmbedEventType,
31
+ EmbedQueryParam: () => EmbedQueryParam,
32
+ OeMeeting: () => OeMeeting,
33
+ buildEmbedUrl: () => buildEmbedUrl,
34
+ parseEmbedEvent: () => parseEmbedEvent,
35
+ useOeMeeting: () => useOeMeeting
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/OeMeeting.tsx
40
+ var import_react2 = require("react");
41
+
42
+ // src/embed-protocol.ts
43
+ var EMBED_PROTOCOL_VERSION = 1;
44
+ var EMBED_MESSAGE_SOURCE = "oemeeting";
45
+ var DEFAULT_EMBED_ORIGIN = "https://meet.oceanedu.org";
46
+ var EmbedEventType = /* @__PURE__ */ ((EmbedEventType2) => {
47
+ EmbedEventType2["READY"] = "oemeeting:ready";
48
+ EmbedEventType2["JOINED"] = "oemeeting:joined";
49
+ EmbedEventType2["LEFT"] = "oemeeting:left";
50
+ EmbedEventType2["PARTICIPANT_JOINED"] = "oemeeting:participant-joined";
51
+ EmbedEventType2["PARTICIPANT_LEFT"] = "oemeeting:participant-left";
52
+ EmbedEventType2["ENDED"] = "oemeeting:ended";
53
+ EmbedEventType2["ERROR"] = "oemeeting:error";
54
+ return EmbedEventType2;
55
+ })(EmbedEventType || {});
56
+ var EmbedCommandType = /* @__PURE__ */ ((EmbedCommandType2) => {
57
+ EmbedCommandType2["COMMAND"] = "oemeeting:command";
58
+ return EmbedCommandType2;
59
+ })(EmbedCommandType || {});
60
+ var EmbedCommandAction = /* @__PURE__ */ ((EmbedCommandAction2) => {
61
+ EmbedCommandAction2["LEAVE"] = "leave";
62
+ EmbedCommandAction2["TOGGLE_MIC"] = "toggleMic";
63
+ EmbedCommandAction2["TOGGLE_CAMERA"] = "toggleCamera";
64
+ return EmbedCommandAction2;
65
+ })(EmbedCommandAction || {});
66
+ var EmbedErrorCode = /* @__PURE__ */ ((EmbedErrorCode2) => {
67
+ EmbedErrorCode2["TOKEN_INVALID"] = "TOKEN_INVALID";
68
+ EmbedErrorCode2["TOKEN_EXPIRED"] = "TOKEN_EXPIRED";
69
+ EmbedErrorCode2["ROOM_NOT_FOUND"] = "ROOM_NOT_FOUND";
70
+ EmbedErrorCode2["ROOM_ACCESS_DENIED"] = "ROOM_ACCESS_DENIED";
71
+ EmbedErrorCode2["CONNECTION_FAILED"] = "CONNECTION_FAILED";
72
+ EmbedErrorCode2["PROTOCOL_MISMATCH"] = "PROTOCOL_MISMATCH";
73
+ EmbedErrorCode2["UNKNOWN"] = "UNKNOWN";
74
+ return EmbedErrorCode2;
75
+ })(EmbedErrorCode || {});
76
+ var EmbedQueryParam = /* @__PURE__ */ ((EmbedQueryParam2) => {
77
+ EmbedQueryParam2["INSTANCE_ID"] = "instanceId";
78
+ EmbedQueryParam2["HOST_ORIGIN"] = "hostOrigin";
79
+ EmbedQueryParam2["JOIN_TOKEN"] = "joinToken";
80
+ EmbedQueryParam2["USER_NAME"] = "userName";
81
+ EmbedQueryParam2["EXTERNAL_USER_ID"] = "externalUserId";
82
+ EmbedQueryParam2["LANG"] = "lang";
83
+ EmbedQueryParam2["AUTO_JOIN"] = "autoJoin";
84
+ EmbedQueryParam2["HIDE_LEAVE"] = "hideLeave";
85
+ return EmbedQueryParam2;
86
+ })(EmbedQueryParam || {});
87
+
88
+ // src/buildEmbedUrl.ts
89
+ var QUERY_FLAG_ENABLED = "1";
90
+ var QUERY_FLAG_DISABLED = "0";
91
+ function toFlag(value) {
92
+ if (value === void 0) return void 0;
93
+ return value ? QUERY_FLAG_ENABLED : QUERY_FLAG_DISABLED;
94
+ }
95
+ function normalizeOrigin(origin) {
96
+ return origin.replace(/\/+$/, "");
97
+ }
98
+ function buildEmbedUrl(options) {
99
+ const roomSlug = options.roomSlug.trim();
100
+ const joinToken = options.joinToken.trim();
101
+ if (!roomSlug) {
102
+ throw new Error("OE Meeting embed requires a non-empty roomSlug.");
103
+ }
104
+ if (!joinToken) {
105
+ throw new Error("OE Meeting embed requires a non-empty joinToken.");
106
+ }
107
+ const origin = normalizeOrigin(options.origin ?? DEFAULT_EMBED_ORIGIN);
108
+ const url = new URL(`${origin}/embed/${encodeURIComponent(roomSlug)}`);
109
+ const entries = [
110
+ ["instanceId" /* INSTANCE_ID */, options.instanceId],
111
+ ["hostOrigin" /* HOST_ORIGIN */, options.hostOrigin],
112
+ ["joinToken" /* JOIN_TOKEN */, joinToken],
113
+ ["userName" /* USER_NAME */, options.userName],
114
+ ["externalUserId" /* EXTERNAL_USER_ID */, options.externalUserId],
115
+ ["lang" /* LANG */, options.lang],
116
+ ["autoJoin" /* AUTO_JOIN */, toFlag(options.autoJoin)],
117
+ ["hideLeave" /* HIDE_LEAVE */, toFlag(options.hideLeave)]
118
+ ];
119
+ for (const [key, value] of entries) {
120
+ if (value !== void 0 && value !== "") {
121
+ url.searchParams.set(key, value);
122
+ }
123
+ }
124
+ return url.toString();
125
+ }
126
+
127
+ // src/useOeMeeting.ts
128
+ var import_react = require("react");
129
+
130
+ // src/parseEmbedEvent.ts
131
+ var EVENT_TYPES = new Set(Object.values(EmbedEventType));
132
+ function isRecord(value) {
133
+ return typeof value === "object" && value !== null;
134
+ }
135
+ function isOptionalString(value) {
136
+ return value === void 0 || typeof value === "string";
137
+ }
138
+ function hasValidPayload(type, payload) {
139
+ switch (type) {
140
+ case "oemeeting:ready" /* READY */:
141
+ return typeof payload.roomSlug === "string";
142
+ case "oemeeting:joined" /* JOINED */:
143
+ return typeof payload.roomSlug === "string" && typeof payload.identity === "string" && isOptionalString(payload.name) && typeof payload.joinedAt === "string";
144
+ case "oemeeting:left" /* LEFT */:
145
+ return typeof payload.roomSlug === "string" && typeof payload.identity === "string" && typeof payload.leftAt === "string" && (payload.durationMs === void 0 || typeof payload.durationMs === "number");
146
+ case "oemeeting:participant-joined" /* PARTICIPANT_JOINED */:
147
+ return typeof payload.identity === "string" && isOptionalString(payload.name) && typeof payload.participantCount === "number";
148
+ case "oemeeting:participant-left" /* PARTICIPANT_LEFT */:
149
+ return typeof payload.identity === "string" && typeof payload.participantCount === "number";
150
+ case "oemeeting:ended" /* ENDED */:
151
+ return typeof payload.roomSlug === "string" && typeof payload.endedAt === "string";
152
+ case "oemeeting:error" /* ERROR */:
153
+ return typeof payload.code === "string" && typeof payload.message === "string";
154
+ }
155
+ }
156
+ function parseEmbedEvent(data) {
157
+ if (!isRecord(data)) return null;
158
+ if (data.source !== EMBED_MESSAGE_SOURCE || data.version !== EMBED_PROTOCOL_VERSION || typeof data.instanceId !== "string" || data.instanceId.length === 0 || typeof data.type !== "string" || !EVENT_TYPES.has(data.type) || !isRecord(data.payload)) {
159
+ return null;
160
+ }
161
+ const type = data.type;
162
+ if (!hasValidPayload(type, data.payload)) return null;
163
+ return data;
164
+ }
165
+
166
+ // src/useOeMeeting.ts
167
+ function postCommand(options) {
168
+ const frameWindow = options.iframe?.contentWindow;
169
+ if (!frameWindow) return;
170
+ const message = {
171
+ source: EMBED_MESSAGE_SOURCE,
172
+ version: EMBED_PROTOCOL_VERSION,
173
+ instanceId: options.instanceId,
174
+ type: "oemeeting:command" /* COMMAND */,
175
+ action: options.action
176
+ };
177
+ frameWindow.postMessage(message, options.targetOrigin);
178
+ }
179
+ function useOeMeeting(options) {
180
+ const iframeRef = (0, import_react.useRef)(null);
181
+ const callbacksRef = (0, import_react.useRef)(options);
182
+ (0, import_react.useEffect)(() => {
183
+ callbacksRef.current = options;
184
+ }, [options]);
185
+ (0, import_react.useEffect)(() => {
186
+ const onMessage = (event) => {
187
+ const frameWindow = iframeRef.current?.contentWindow;
188
+ if (!frameWindow || event.source !== frameWindow || event.origin !== options.targetOrigin) {
189
+ return;
190
+ }
191
+ const embedEvent = parseEmbedEvent(event.data);
192
+ if (!embedEvent || embedEvent.instanceId !== options.instanceId) return;
193
+ const callbacks = callbacksRef.current;
194
+ callbacks.onEvent?.(embedEvent);
195
+ switch (embedEvent.type) {
196
+ case "oemeeting:ready" /* READY */:
197
+ callbacks.onReady?.(embedEvent);
198
+ break;
199
+ case "oemeeting:joined" /* JOINED */:
200
+ callbacks.onJoined?.(embedEvent);
201
+ break;
202
+ case "oemeeting:left" /* LEFT */:
203
+ callbacks.onLeft?.(embedEvent);
204
+ break;
205
+ case "oemeeting:participant-joined" /* PARTICIPANT_JOINED */:
206
+ callbacks.onParticipantJoined?.(embedEvent);
207
+ break;
208
+ case "oemeeting:participant-left" /* PARTICIPANT_LEFT */:
209
+ callbacks.onParticipantLeft?.(embedEvent);
210
+ break;
211
+ case "oemeeting:ended" /* ENDED */:
212
+ callbacks.onEnded?.(embedEvent);
213
+ break;
214
+ case "oemeeting:error" /* ERROR */:
215
+ callbacks.onError?.(embedEvent);
216
+ break;
217
+ }
218
+ };
219
+ window.addEventListener("message", onMessage);
220
+ return () => window.removeEventListener("message", onMessage);
221
+ }, [options.instanceId, options.targetOrigin]);
222
+ const send = (0, import_react.useCallback)(
223
+ (action) => postCommand({
224
+ iframe: iframeRef.current,
225
+ instanceId: options.instanceId,
226
+ action,
227
+ targetOrigin: options.targetOrigin
228
+ }),
229
+ [options.instanceId, options.targetOrigin]
230
+ );
231
+ const leave = (0, import_react.useCallback)(() => send("leave" /* LEAVE */), [send]);
232
+ const toggleMic = (0, import_react.useCallback)(
233
+ () => send("toggleMic" /* TOGGLE_MIC */),
234
+ [send]
235
+ );
236
+ const toggleCamera = (0, import_react.useCallback)(
237
+ () => send("toggleCamera" /* TOGGLE_CAMERA */),
238
+ [send]
239
+ );
240
+ const getIframe = (0, import_react.useCallback)(() => iframeRef.current, []);
241
+ const handle = (0, import_react.useMemo)(
242
+ () => ({ leave, toggleMic, toggleCamera, getIframe }),
243
+ [getIframe, leave, toggleCamera, toggleMic]
244
+ );
245
+ (0, import_react.useImperativeHandle)(options.handleRef, () => handle, [handle]);
246
+ return { iframeRef, handle };
247
+ }
248
+
249
+ // src/OeMeeting.tsx
250
+ var import_jsx_runtime = require("react/jsx-runtime");
251
+ var DEFAULT_IFRAME_ALLOW = "camera; microphone; fullscreen; autoplay; display-capture; clipboard-write; encrypted-media";
252
+ function toCssSize(value) {
253
+ if (value === void 0) return void 0;
254
+ return typeof value === "number" ? `${value}px` : value;
255
+ }
256
+ function getOrigin(value) {
257
+ return new URL(value).origin;
258
+ }
259
+ var OeMeeting = (0, import_react2.forwardRef)(
260
+ function OeMeeting2(props, ref) {
261
+ const {
262
+ roomSlug,
263
+ joinToken,
264
+ userName,
265
+ externalUserId,
266
+ origin = DEFAULT_EMBED_ORIGIN,
267
+ lang,
268
+ autoJoin = true,
269
+ hideLeave,
270
+ title = "OE Meeting room",
271
+ width = "100%",
272
+ height = 640,
273
+ className,
274
+ style,
275
+ iframeClassName,
276
+ iframeStyle,
277
+ allow = DEFAULT_IFRAME_ALLOW,
278
+ targetOrigin,
279
+ onReady,
280
+ onJoined,
281
+ onLeft,
282
+ onParticipantJoined,
283
+ onParticipantLeft,
284
+ onEnded,
285
+ onError,
286
+ onEvent
287
+ } = props;
288
+ const reactId = (0, import_react2.useId)();
289
+ const instanceId = (0, import_react2.useMemo)(
290
+ () => `oem-${reactId.replace(/:/g, "")}`,
291
+ [reactId]
292
+ );
293
+ const embedOrigin = (0, import_react2.useMemo)(() => getOrigin(origin), [origin]);
294
+ const resolvedTargetOrigin = targetOrigin ?? embedOrigin;
295
+ const hostOrigin = typeof window === "undefined" ? void 0 : window.location.origin;
296
+ const src = (0, import_react2.useMemo)(
297
+ () => buildEmbedUrl({
298
+ roomSlug,
299
+ joinToken,
300
+ userName,
301
+ externalUserId,
302
+ origin,
303
+ lang,
304
+ autoJoin,
305
+ hideLeave,
306
+ instanceId,
307
+ hostOrigin
308
+ }),
309
+ [
310
+ autoJoin,
311
+ externalUserId,
312
+ hideLeave,
313
+ hostOrigin,
314
+ instanceId,
315
+ joinToken,
316
+ lang,
317
+ origin,
318
+ roomSlug,
319
+ userName
320
+ ]
321
+ );
322
+ const { iframeRef } = useOeMeeting({
323
+ instanceId,
324
+ targetOrigin: resolvedTargetOrigin,
325
+ handleRef: ref,
326
+ onReady,
327
+ onJoined,
328
+ onLeft,
329
+ onParticipantJoined,
330
+ onParticipantLeft,
331
+ onEnded,
332
+ onError,
333
+ onEvent
334
+ });
335
+ const wrapperStyle = {
336
+ width: toCssSize(width),
337
+ height: toCssSize(height),
338
+ ...style
339
+ };
340
+ const frameStyle = {
341
+ width: "100%",
342
+ height: "100%",
343
+ border: 0,
344
+ display: "block",
345
+ ...iframeStyle
346
+ };
347
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
348
+ "div",
349
+ {
350
+ className,
351
+ style: wrapperStyle,
352
+ "data-oe-meeting-instance": instanceId,
353
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
354
+ "iframe",
355
+ {
356
+ ref: iframeRef,
357
+ className: iframeClassName,
358
+ style: frameStyle,
359
+ src,
360
+ title,
361
+ allow,
362
+ allowFullScreen: true,
363
+ referrerPolicy: "strict-origin-when-cross-origin"
364
+ }
365
+ )
366
+ }
367
+ );
368
+ }
369
+ );
370
+ OeMeeting.displayName = "OeMeeting";
371
+ // Annotate the CommonJS export names for ESM import in node:
372
+ 0 && (module.exports = {
373
+ DEFAULT_EMBED_ORIGIN,
374
+ DEFAULT_IFRAME_ALLOW,
375
+ EMBED_MESSAGE_SOURCE,
376
+ EMBED_PROTOCOL_VERSION,
377
+ EmbedCommandAction,
378
+ EmbedCommandType,
379
+ EmbedErrorCode,
380
+ EmbedEventType,
381
+ EmbedQueryParam,
382
+ OeMeeting,
383
+ buildEmbedUrl,
384
+ parseEmbedEvent,
385
+ useOeMeeting
386
+ });
387
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/OeMeeting.tsx","../src/embed-protocol.ts","../src/buildEmbedUrl.ts","../src/useOeMeeting.ts","../src/parseEmbedEvent.ts"],"sourcesContent":["export { OeMeeting, DEFAULT_IFRAME_ALLOW } from './OeMeeting';\nexport { useOeMeeting } from './useOeMeeting';\nexport { buildEmbedUrl } from './buildEmbedUrl';\nexport { parseEmbedEvent } from './parseEmbedEvent';\nexport {\n DEFAULT_EMBED_ORIGIN,\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EmbedCommandAction,\n EmbedCommandType,\n EmbedErrorCode,\n EmbedEventType,\n EmbedQueryParam,\n} from './embed-protocol';\nexport type {\n BuildEmbedUrlOptions,\n EmbedCommandMessage,\n EmbedEndedPayload,\n EmbedErrorPayload,\n EmbedEvent,\n EmbedEventOf,\n EmbedEventPayloadMap,\n EmbedJoinedPayload,\n EmbedLeftPayload,\n EmbedMessageBase,\n EmbedParticipantJoinedPayload,\n EmbedParticipantLeftPayload,\n EmbedReadyPayload,\n OeMeetingHandle,\n OeMeetingProps,\n} from './types';\n","'use client';\n\nimport {\n forwardRef,\n useId,\n useMemo,\n type CSSProperties,\n} from 'react';\nimport { buildEmbedUrl } from './buildEmbedUrl';\nimport { DEFAULT_EMBED_ORIGIN } from './embed-protocol';\nimport type { OeMeetingHandle, OeMeetingProps } from './types';\nimport { useOeMeeting } from './useOeMeeting';\n\nexport const DEFAULT_IFRAME_ALLOW =\n 'camera; microphone; fullscreen; autoplay; display-capture; clipboard-write; encrypted-media';\n\nfunction toCssSize(value: number | string | undefined): string | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'number' ? `${value}px` : value;\n}\n\nfunction getOrigin(value: string): string {\n return new URL(value).origin;\n}\n\nexport const OeMeeting = forwardRef<OeMeetingHandle, OeMeetingProps>(\n function OeMeeting(props, ref) {\n const {\n roomSlug,\n joinToken,\n userName,\n externalUserId,\n origin = DEFAULT_EMBED_ORIGIN,\n lang,\n autoJoin = true,\n hideLeave,\n title = 'OE Meeting room',\n width = '100%',\n height = 640,\n className,\n style,\n iframeClassName,\n iframeStyle,\n allow = DEFAULT_IFRAME_ALLOW,\n targetOrigin,\n onReady,\n onJoined,\n onLeft,\n onParticipantJoined,\n onParticipantLeft,\n onEnded,\n onError,\n onEvent,\n } = props;\n const reactId = useId();\n const instanceId = useMemo(\n () => `oem-${reactId.replace(/:/g, '')}`,\n [reactId],\n );\n const embedOrigin = useMemo(() => getOrigin(origin), [origin]);\n const resolvedTargetOrigin = targetOrigin ?? embedOrigin;\n const hostOrigin =\n typeof window === 'undefined' ? undefined : window.location.origin;\n const src = useMemo(\n () =>\n buildEmbedUrl({\n roomSlug,\n joinToken,\n userName,\n externalUserId,\n origin,\n lang,\n autoJoin,\n hideLeave,\n instanceId,\n hostOrigin,\n }),\n [\n autoJoin,\n externalUserId,\n hideLeave,\n hostOrigin,\n instanceId,\n joinToken,\n lang,\n origin,\n roomSlug,\n userName,\n ],\n );\n const { iframeRef } = useOeMeeting({\n instanceId,\n targetOrigin: resolvedTargetOrigin,\n handleRef: ref,\n onReady,\n onJoined,\n onLeft,\n onParticipantJoined,\n onParticipantLeft,\n onEnded,\n onError,\n onEvent,\n });\n const wrapperStyle: CSSProperties = {\n width: toCssSize(width),\n height: toCssSize(height),\n ...style,\n };\n const frameStyle: CSSProperties = {\n width: '100%',\n height: '100%',\n border: 0,\n display: 'block',\n ...iframeStyle,\n };\n\n return (\n <div\n className={className}\n style={wrapperStyle}\n data-oe-meeting-instance={instanceId}\n >\n <iframe\n ref={iframeRef}\n className={iframeClassName}\n style={frameStyle}\n src={src}\n title={title}\n allow={allow}\n allowFullScreen\n referrerPolicy=\"strict-origin-when-cross-origin\"\n />\n </div>\n );\n },\n);\n\nOeMeeting.displayName = 'OeMeeting';\n","export const EMBED_PROTOCOL_VERSION = 1 as const;\nexport const EMBED_MESSAGE_SOURCE = 'oemeeting' as const;\nexport const DEFAULT_EMBED_ORIGIN = 'https://meet.oceanedu.org';\n\nexport enum EmbedEventType {\n READY = 'oemeeting:ready',\n JOINED = 'oemeeting:joined',\n LEFT = 'oemeeting:left',\n PARTICIPANT_JOINED = 'oemeeting:participant-joined',\n PARTICIPANT_LEFT = 'oemeeting:participant-left',\n ENDED = 'oemeeting:ended',\n ERROR = 'oemeeting:error',\n}\n\nexport enum EmbedCommandType {\n COMMAND = 'oemeeting:command',\n}\n\nexport enum EmbedCommandAction {\n LEAVE = 'leave',\n TOGGLE_MIC = 'toggleMic',\n TOGGLE_CAMERA = 'toggleCamera',\n}\n\nexport enum EmbedErrorCode {\n TOKEN_INVALID = 'TOKEN_INVALID',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n ROOM_NOT_FOUND = 'ROOM_NOT_FOUND',\n ROOM_ACCESS_DENIED = 'ROOM_ACCESS_DENIED',\n CONNECTION_FAILED = 'CONNECTION_FAILED',\n PROTOCOL_MISMATCH = 'PROTOCOL_MISMATCH',\n UNKNOWN = 'UNKNOWN',\n}\n\nexport enum EmbedQueryParam {\n INSTANCE_ID = 'instanceId',\n HOST_ORIGIN = 'hostOrigin',\n JOIN_TOKEN = 'joinToken',\n USER_NAME = 'userName',\n EXTERNAL_USER_ID = 'externalUserId',\n LANG = 'lang',\n AUTO_JOIN = 'autoJoin',\n HIDE_LEAVE = 'hideLeave',\n}\n","import {\n DEFAULT_EMBED_ORIGIN,\n EmbedQueryParam,\n} from './embed-protocol';\nimport type { BuildEmbedUrlOptions } from './types';\n\nconst QUERY_FLAG_ENABLED = '1';\nconst QUERY_FLAG_DISABLED = '0';\n\nfunction toFlag(value: boolean | undefined): string | undefined {\n if (value === undefined) return undefined;\n return value ? QUERY_FLAG_ENABLED : QUERY_FLAG_DISABLED;\n}\n\nfunction normalizeOrigin(origin: string): string {\n return origin.replace(/\\/+$/, '');\n}\n\nexport function buildEmbedUrl(options: BuildEmbedUrlOptions): string {\n const roomSlug = options.roomSlug.trim();\n const joinToken = options.joinToken.trim();\n if (!roomSlug) {\n throw new Error('OE Meeting embed requires a non-empty roomSlug.');\n }\n if (!joinToken) {\n throw new Error('OE Meeting embed requires a non-empty joinToken.');\n }\n\n const origin = normalizeOrigin(options.origin ?? DEFAULT_EMBED_ORIGIN);\n const url = new URL(`${origin}/embed/${encodeURIComponent(roomSlug)}`);\n const entries: Array<[EmbedQueryParam, string | undefined]> = [\n [EmbedQueryParam.INSTANCE_ID, options.instanceId],\n [EmbedQueryParam.HOST_ORIGIN, options.hostOrigin],\n [EmbedQueryParam.JOIN_TOKEN, joinToken],\n [EmbedQueryParam.USER_NAME, options.userName],\n [EmbedQueryParam.EXTERNAL_USER_ID, options.externalUserId],\n [EmbedQueryParam.LANG, options.lang],\n [EmbedQueryParam.AUTO_JOIN, toFlag(options.autoJoin)],\n [EmbedQueryParam.HIDE_LEAVE, toFlag(options.hideLeave)],\n ];\n\n for (const [key, value] of entries) {\n if (value !== undefined && value !== '') {\n url.searchParams.set(key, value);\n }\n }\n return url.toString();\n}\n","import {\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type Ref,\n} from 'react';\nimport {\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EmbedCommandAction,\n EmbedCommandType,\n EmbedEventType,\n} from './embed-protocol';\nimport { parseEmbedEvent } from './parseEmbedEvent';\nimport type {\n EmbedCommandMessage,\n OeMeetingHandle,\n OeMeetingProps,\n} from './types';\n\ntype UseOeMeetingOptions = Pick<\n OeMeetingProps,\n | 'onReady'\n | 'onJoined'\n | 'onLeft'\n | 'onParticipantJoined'\n | 'onParticipantLeft'\n | 'onEnded'\n | 'onError'\n | 'onEvent'\n> & {\n instanceId: string;\n targetOrigin: string;\n handleRef?: Ref<OeMeetingHandle>;\n};\n\ninterface PostCommandOptions {\n iframe: HTMLIFrameElement | null;\n instanceId: string;\n action: EmbedCommandAction;\n targetOrigin: string;\n}\n\nfunction postCommand(options: PostCommandOptions): void {\n const frameWindow = options.iframe?.contentWindow;\n if (!frameWindow) return;\n const message: EmbedCommandMessage = {\n source: EMBED_MESSAGE_SOURCE,\n version: EMBED_PROTOCOL_VERSION,\n instanceId: options.instanceId,\n type: EmbedCommandType.COMMAND,\n action: options.action,\n };\n frameWindow.postMessage(message, options.targetOrigin);\n}\n\nexport function useOeMeeting(options: UseOeMeetingOptions) {\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const callbacksRef = useRef(options);\n\n useEffect(() => {\n callbacksRef.current = options;\n }, [options]);\n\n useEffect(() => {\n const onMessage = (event: MessageEvent<unknown>) => {\n const frameWindow = iframeRef.current?.contentWindow;\n if (\n !frameWindow ||\n event.source !== frameWindow ||\n event.origin !== options.targetOrigin\n ) {\n return;\n }\n const embedEvent = parseEmbedEvent(event.data);\n if (!embedEvent || embedEvent.instanceId !== options.instanceId) return;\n\n const callbacks = callbacksRef.current;\n callbacks.onEvent?.(embedEvent);\n switch (embedEvent.type) {\n case EmbedEventType.READY:\n callbacks.onReady?.(embedEvent);\n break;\n case EmbedEventType.JOINED:\n callbacks.onJoined?.(embedEvent);\n break;\n case EmbedEventType.LEFT:\n callbacks.onLeft?.(embedEvent);\n break;\n case EmbedEventType.PARTICIPANT_JOINED:\n callbacks.onParticipantJoined?.(embedEvent);\n break;\n case EmbedEventType.PARTICIPANT_LEFT:\n callbacks.onParticipantLeft?.(embedEvent);\n break;\n case EmbedEventType.ENDED:\n callbacks.onEnded?.(embedEvent);\n break;\n case EmbedEventType.ERROR:\n callbacks.onError?.(embedEvent);\n break;\n }\n };\n\n window.addEventListener('message', onMessage);\n return () => window.removeEventListener('message', onMessage);\n }, [options.instanceId, options.targetOrigin]);\n\n const send = useCallback(\n (action: EmbedCommandAction) =>\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action,\n targetOrigin: options.targetOrigin,\n }),\n [options.instanceId, options.targetOrigin],\n );\n const leave = useCallback(() => send(EmbedCommandAction.LEAVE), [send]);\n const toggleMic = useCallback(\n () => send(EmbedCommandAction.TOGGLE_MIC),\n [send],\n );\n const toggleCamera = useCallback(\n () => send(EmbedCommandAction.TOGGLE_CAMERA),\n [send],\n );\n const getIframe = useCallback(() => iframeRef.current, []);\n const handle = useMemo<OeMeetingHandle>(\n () => ({ leave, toggleMic, toggleCamera, getIframe }),\n [getIframe, leave, toggleCamera, toggleMic],\n );\n\n useImperativeHandle(options.handleRef, () => handle, [handle]);\n return { iframeRef, handle };\n}\n","import {\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EmbedEventType,\n} from './embed-protocol';\nimport type { EmbedEvent } from './types';\n\nconst EVENT_TYPES = new Set<string>(Object.values(EmbedEventType));\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isOptionalString(value: unknown): boolean {\n return value === undefined || typeof value === 'string';\n}\n\nfunction hasValidPayload(\n type: EmbedEventType,\n payload: Record<string, unknown>,\n): boolean {\n switch (type) {\n case EmbedEventType.READY:\n return typeof payload.roomSlug === 'string';\n case EmbedEventType.JOINED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.identity === 'string' &&\n isOptionalString(payload.name) &&\n typeof payload.joinedAt === 'string'\n );\n case EmbedEventType.LEFT:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.identity === 'string' &&\n typeof payload.leftAt === 'string' &&\n (payload.durationMs === undefined ||\n typeof payload.durationMs === 'number')\n );\n case EmbedEventType.PARTICIPANT_JOINED:\n return (\n typeof payload.identity === 'string' &&\n isOptionalString(payload.name) &&\n typeof payload.participantCount === 'number'\n );\n case EmbedEventType.PARTICIPANT_LEFT:\n return (\n typeof payload.identity === 'string' &&\n typeof payload.participantCount === 'number'\n );\n case EmbedEventType.ENDED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.endedAt === 'string'\n );\n case EmbedEventType.ERROR:\n return (\n typeof payload.code === 'string' &&\n typeof payload.message === 'string'\n );\n }\n}\n\nexport function parseEmbedEvent(data: unknown): EmbedEvent | null {\n if (!isRecord(data)) return null;\n if (\n data.source !== EMBED_MESSAGE_SOURCE ||\n data.version !== EMBED_PROTOCOL_VERSION ||\n typeof data.instanceId !== 'string' ||\n data.instanceId.length === 0 ||\n typeof data.type !== 'string' ||\n !EVENT_TYPES.has(data.type) ||\n !isRecord(data.payload)\n ) {\n return null;\n }\n\n const type = data.type as EmbedEventType;\n if (!hasValidPayload(type, data.payload)) return null;\n return data as unknown as EmbedEvent;\n}\n"],"mappings":"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","names":["import_react","EmbedEventType","EmbedCommandType","EmbedCommandAction","EmbedErrorCode","EmbedQueryParam","OeMeeting"]}