@tinyweb_dev/oe-meeting-react 0.5.0 → 0.7.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/README.md +2 -1
- package/dist/index.cjs +108 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -5
- package/dist/index.d.ts +54 -5
- package/dist/index.js +106 -27
- package/dist/index.js.map +1 -1
- package/docs/BACKEND-INTEGRATION.md +42 -4
- package/docs/REACT-INTEGRATION.md +42 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,6 +81,7 @@ bucket S3/MinIO phải có lifecycle rule cùng số ngày để xóa file thậ
|
|
|
81
81
|
## Imperative API
|
|
82
82
|
|
|
83
83
|
`OeMeetingHandle` cung cấp `leave()`, `toggleMic()`, `toggleCamera()`,
|
|
84
|
-
`openStage()`, `closeStage()` và `getIframe()`.
|
|
84
|
+
`openStage()`, `closeStage()` và `getIframe()`. `toggleCamera()` không bật
|
|
85
|
+
được camera khi prop `disableCamera` đang bật. Host giữ nội dung tool
|
|
85
86
|
(trang PDF, nét vẽ); Meeting chỉ sync URL Stage. Host stamp
|
|
86
87
|
`attributes.stage` (`open` \| `follow`) nếu cần hạn chế origin OPEN/CLOSE.
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
DEFAULT_IFRAME_ALLOW: () => DEFAULT_IFRAME_ALLOW,
|
|
25
25
|
EMBED_MESSAGE_SOURCE: () => EMBED_MESSAGE_SOURCE,
|
|
26
26
|
EMBED_PROTOCOL_VERSION: () => EMBED_PROTOCOL_VERSION,
|
|
27
|
+
EmbedBackgroundType: () => EmbedBackgroundType,
|
|
27
28
|
EmbedCapability: () => EmbedCapability,
|
|
28
29
|
EmbedCaptureOutcome: () => EmbedCaptureOutcome,
|
|
29
30
|
EmbedCommandAction: () => EmbedCommandAction,
|
|
@@ -32,14 +33,17 @@ __export(index_exports, {
|
|
|
32
33
|
EmbedEventType: () => EmbedEventType,
|
|
33
34
|
EmbedQueryParam: () => EmbedQueryParam,
|
|
34
35
|
EmbedRole: () => EmbedRole,
|
|
36
|
+
MAX_BACKGROUND_URL_LENGTH: () => MAX_BACKGROUND_URL_LENGTH,
|
|
35
37
|
MAX_STAGE_TOOL_ID_LENGTH: () => MAX_STAGE_TOOL_ID_LENGTH,
|
|
36
38
|
MAX_STAGE_URL_LENGTH: () => MAX_STAGE_URL_LENGTH,
|
|
37
39
|
OeMeeting: () => OeMeeting,
|
|
38
40
|
buildEmbedUrl: () => buildEmbedUrl,
|
|
39
41
|
isStageToolId: () => isStageToolId,
|
|
40
42
|
isValidStageUrl: () => isValidStageUrl,
|
|
43
|
+
normalizeEmbedBackground: () => normalizeEmbedBackground,
|
|
41
44
|
normalizeOpenStageInput: () => normalizeOpenStageInput,
|
|
42
45
|
parseEmbedEvent: () => parseEmbedEvent,
|
|
46
|
+
serializeEmbedBackground: () => serializeEmbedBackground,
|
|
43
47
|
useOeMeeting: () => useOeMeeting
|
|
44
48
|
});
|
|
45
49
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -75,6 +79,7 @@ var EmbedCommandAction = /* @__PURE__ */ ((EmbedCommandAction2) => {
|
|
|
75
79
|
EmbedCommandAction2["TOGGLE_CAMERA"] = "toggleCamera";
|
|
76
80
|
EmbedCommandAction2["OPEN_STAGE"] = "openStage";
|
|
77
81
|
EmbedCommandAction2["CLOSE_STAGE"] = "closeStage";
|
|
82
|
+
EmbedCommandAction2["SET_BACKGROUND"] = "setBackground";
|
|
78
83
|
return EmbedCommandAction2;
|
|
79
84
|
})(EmbedCommandAction || {});
|
|
80
85
|
var EmbedErrorCode = /* @__PURE__ */ ((EmbedErrorCode2) => {
|
|
@@ -91,6 +96,16 @@ var EmbedErrorCode = /* @__PURE__ */ ((EmbedErrorCode2) => {
|
|
|
91
96
|
EmbedErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
92
97
|
return EmbedErrorCode2;
|
|
93
98
|
})(EmbedErrorCode || {});
|
|
99
|
+
var EmbedBackgroundType = /* @__PURE__ */ ((EmbedBackgroundType2) => {
|
|
100
|
+
EmbedBackgroundType2["BLUR"] = "blur";
|
|
101
|
+
EmbedBackgroundType2["IMAGE"] = "image";
|
|
102
|
+
EmbedBackgroundType2["NONE"] = "none";
|
|
103
|
+
return EmbedBackgroundType2;
|
|
104
|
+
})(EmbedBackgroundType || {});
|
|
105
|
+
var MAX_BACKGROUND_URL_LENGTH = 2048;
|
|
106
|
+
var DEFAULT_BACKGROUND_BLUR_RADIUS = 10;
|
|
107
|
+
var MIN_BACKGROUND_BLUR_RADIUS = 1;
|
|
108
|
+
var MAX_BACKGROUND_BLUR_RADIUS = 50;
|
|
94
109
|
var MAX_STAGE_URL_LENGTH = 2048;
|
|
95
110
|
var MAX_STAGE_TOOL_ID_LENGTH = 32;
|
|
96
111
|
var STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;
|
|
@@ -107,8 +122,10 @@ var EmbedQueryParam = /* @__PURE__ */ ((EmbedQueryParam2) => {
|
|
|
107
122
|
EmbedQueryParam2["AUTO_JOIN"] = "autoJoin";
|
|
108
123
|
EmbedQueryParam2["HIDE_LEAVE"] = "hideLeave";
|
|
109
124
|
EmbedQueryParam2["AUTO_CAMERA_OFF"] = "autoCameraOff";
|
|
125
|
+
EmbedQueryParam2["DISABLE_CAMERA"] = "disableCamera";
|
|
110
126
|
EmbedQueryParam2["AUTO_TRANSCRIBE"] = "autoTranscribe";
|
|
111
127
|
EmbedQueryParam2["AUTO_RECORD"] = "autoRecord";
|
|
128
|
+
EmbedQueryParam2["BACKGROUND"] = "background";
|
|
112
129
|
return EmbedQueryParam2;
|
|
113
130
|
})(EmbedQueryParam || {});
|
|
114
131
|
var EmbedCaptureOutcome = /* @__PURE__ */ ((EmbedCaptureOutcome2) => {
|
|
@@ -128,6 +145,62 @@ var EmbedCapability = /* @__PURE__ */ ((EmbedCapability2) => {
|
|
|
128
145
|
return EmbedCapability2;
|
|
129
146
|
})(EmbedCapability || {});
|
|
130
147
|
|
|
148
|
+
// src/openStage.ts
|
|
149
|
+
function isValidStageUrl(url) {
|
|
150
|
+
if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;
|
|
151
|
+
try {
|
|
152
|
+
const parsed = new URL(url);
|
|
153
|
+
if (parsed.protocol === "https:") return true;
|
|
154
|
+
if (parsed.protocol === "http:") {
|
|
155
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
} catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function normalizeOpenStageInput(input) {
|
|
163
|
+
const url = input.url.trim();
|
|
164
|
+
if (!isValidStageUrl(url)) return null;
|
|
165
|
+
const title = input.title?.trim();
|
|
166
|
+
return {
|
|
167
|
+
url,
|
|
168
|
+
...title ? { title: title.slice(0, 200) } : {},
|
|
169
|
+
...isStageToolId(input.tool) ? { tool: input.tool } : {}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/background.ts
|
|
174
|
+
var isValidBackgroundUrl = (url) => url.length > 0 && url.length <= MAX_BACKGROUND_URL_LENGTH && isValidStageUrl(url);
|
|
175
|
+
var clampBlurRadius = (value) => Number.isFinite(value) ? Math.min(MAX_BACKGROUND_BLUR_RADIUS, Math.max(MIN_BACKGROUND_BLUR_RADIUS, Math.round(value))) : DEFAULT_BACKGROUND_BLUR_RADIUS;
|
|
176
|
+
function normalizeEmbedBackground(input) {
|
|
177
|
+
const locked = input.locked === true ? { locked: true } : {};
|
|
178
|
+
if (input.type === "none" /* NONE */) {
|
|
179
|
+
return { type: "none" /* NONE */, ...locked };
|
|
180
|
+
}
|
|
181
|
+
if (input.type === "blur" /* BLUR */) {
|
|
182
|
+
return {
|
|
183
|
+
type: "blur" /* BLUR */,
|
|
184
|
+
blurRadius: clampBlurRadius(input.blurRadius ?? DEFAULT_BACKGROUND_BLUR_RADIUS),
|
|
185
|
+
...locked
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (input.type === "image" /* IMAGE */) {
|
|
189
|
+
const url = input.url?.trim();
|
|
190
|
+
if (!url || !isValidBackgroundUrl(url)) return null;
|
|
191
|
+
return {
|
|
192
|
+
type: "image" /* IMAGE */,
|
|
193
|
+
url,
|
|
194
|
+
...locked
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
function serializeEmbedBackground(input) {
|
|
200
|
+
const normalized = normalizeEmbedBackground(input);
|
|
201
|
+
return normalized ? JSON.stringify(normalized) : void 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
131
204
|
// src/buildEmbedUrl.ts
|
|
132
205
|
var QUERY_FLAG_ENABLED = "1";
|
|
133
206
|
var QUERY_FLAG_DISABLED = "0";
|
|
@@ -149,6 +222,7 @@ function buildEmbedUrl(options) {
|
|
|
149
222
|
}
|
|
150
223
|
const origin = normalizeOrigin(options.origin ?? DEFAULT_EMBED_ORIGIN);
|
|
151
224
|
const url = new URL(`${origin}/embed/${encodeURIComponent(roomSlug)}`);
|
|
225
|
+
const backgroundParam = options.background ? serializeEmbedBackground(options.background) : void 0;
|
|
152
226
|
const entries = [
|
|
153
227
|
["instanceId" /* INSTANCE_ID */, options.instanceId],
|
|
154
228
|
["hostOrigin" /* HOST_ORIGIN */, options.hostOrigin],
|
|
@@ -159,8 +233,10 @@ function buildEmbedUrl(options) {
|
|
|
159
233
|
["autoJoin" /* AUTO_JOIN */, toFlag(options.autoJoin)],
|
|
160
234
|
["hideLeave" /* HIDE_LEAVE */, toFlag(options.hideLeave)],
|
|
161
235
|
["autoCameraOff" /* AUTO_CAMERA_OFF */, toFlag(options.autoCameraOff)],
|
|
236
|
+
["disableCamera" /* DISABLE_CAMERA */, toFlag(options.disableCamera)],
|
|
162
237
|
["autoTranscribe" /* AUTO_TRANSCRIBE */, toFlag(options.autoTranscribe)],
|
|
163
|
-
["autoRecord" /* AUTO_RECORD */, toFlag(options.autoRecord)]
|
|
238
|
+
["autoRecord" /* AUTO_RECORD */, toFlag(options.autoRecord)],
|
|
239
|
+
["background" /* BACKGROUND */, backgroundParam]
|
|
164
240
|
];
|
|
165
241
|
for (const [key, value] of entries) {
|
|
166
242
|
if (value !== void 0 && value !== "") {
|
|
@@ -173,31 +249,6 @@ function buildEmbedUrl(options) {
|
|
|
173
249
|
// src/useOeMeeting.ts
|
|
174
250
|
var import_react = require("react");
|
|
175
251
|
|
|
176
|
-
// src/openStage.ts
|
|
177
|
-
function isValidStageUrl(url) {
|
|
178
|
-
if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;
|
|
179
|
-
try {
|
|
180
|
-
const parsed = new URL(url);
|
|
181
|
-
if (parsed.protocol === "https:") return true;
|
|
182
|
-
if (parsed.protocol === "http:") {
|
|
183
|
-
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
184
|
-
}
|
|
185
|
-
return false;
|
|
186
|
-
} catch {
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function normalizeOpenStageInput(input) {
|
|
191
|
-
const url = input.url.trim();
|
|
192
|
-
if (!isValidStageUrl(url)) return null;
|
|
193
|
-
const title = input.title?.trim();
|
|
194
|
-
return {
|
|
195
|
-
url,
|
|
196
|
-
...title ? { title: title.slice(0, 200) } : {},
|
|
197
|
-
...isStageToolId(input.tool) ? { tool: input.tool } : {}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
252
|
// src/parseEmbedEvent.ts
|
|
202
253
|
var EVENT_TYPES = new Set(Object.values(EmbedEventType));
|
|
203
254
|
function isRecord(value) {
|
|
@@ -266,6 +317,7 @@ function postCommand(options) {
|
|
|
266
317
|
function useOeMeeting(options) {
|
|
267
318
|
const iframeRef = (0, import_react.useRef)(null);
|
|
268
319
|
const callbacksRef = (0, import_react.useRef)(options);
|
|
320
|
+
const pendingBackgroundRef = (0, import_react.useRef)(null);
|
|
269
321
|
(0, import_react.useEffect)(() => {
|
|
270
322
|
callbacksRef.current = options;
|
|
271
323
|
}, [options]);
|
|
@@ -282,6 +334,15 @@ function useOeMeeting(options) {
|
|
|
282
334
|
switch (embedEvent.type) {
|
|
283
335
|
case "oemeeting:ready" /* READY */:
|
|
284
336
|
callbacks.onReady?.(embedEvent);
|
|
337
|
+
if (pendingBackgroundRef.current) {
|
|
338
|
+
postCommand({
|
|
339
|
+
iframe: iframeRef.current,
|
|
340
|
+
instanceId: options.instanceId,
|
|
341
|
+
action: "setBackground" /* SET_BACKGROUND */,
|
|
342
|
+
targetOrigin: options.targetOrigin,
|
|
343
|
+
payload: pendingBackgroundRef.current
|
|
344
|
+
});
|
|
345
|
+
}
|
|
285
346
|
break;
|
|
286
347
|
case "oemeeting:joined" /* JOINED */:
|
|
287
348
|
callbacks.onJoined?.(embedEvent);
|
|
@@ -349,6 +410,16 @@ function useOeMeeting(options) {
|
|
|
349
410
|
() => send("closeStage" /* CLOSE_STAGE */),
|
|
350
411
|
[send]
|
|
351
412
|
);
|
|
413
|
+
(0, import_react.useEffect)(() => {
|
|
414
|
+
if (!options.background) {
|
|
415
|
+
pendingBackgroundRef.current = null;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const normalized = normalizeEmbedBackground(options.background);
|
|
419
|
+
if (!normalized) return;
|
|
420
|
+
pendingBackgroundRef.current = normalized;
|
|
421
|
+
send("setBackground" /* SET_BACKGROUND */, normalized);
|
|
422
|
+
}, [options.background, send]);
|
|
352
423
|
const getIframe = (0, import_react.useCallback)(() => iframeRef.current, []);
|
|
353
424
|
const handle = (0, import_react.useMemo)(
|
|
354
425
|
() => ({
|
|
@@ -387,8 +458,10 @@ var OeMeeting = (0, import_react2.forwardRef)(
|
|
|
387
458
|
autoJoin = true,
|
|
388
459
|
hideLeave,
|
|
389
460
|
autoCameraOff,
|
|
461
|
+
disableCamera,
|
|
390
462
|
autoTranscribe,
|
|
391
463
|
autoRecord,
|
|
464
|
+
background,
|
|
392
465
|
title = "OE Meeting room",
|
|
393
466
|
width = "100%",
|
|
394
467
|
height = 640,
|
|
@@ -419,6 +492,7 @@ var OeMeeting = (0, import_react2.forwardRef)(
|
|
|
419
492
|
const embedOrigin = (0, import_react2.useMemo)(() => getOrigin(origin), [origin]);
|
|
420
493
|
const resolvedTargetOrigin = targetOrigin ?? embedOrigin;
|
|
421
494
|
const hostOrigin = typeof window === "undefined" ? void 0 : window.location.origin;
|
|
495
|
+
const initialBackgroundRef = (0, import_react2.useRef)(background);
|
|
422
496
|
const src = (0, import_react2.useMemo)(
|
|
423
497
|
() => buildEmbedUrl({
|
|
424
498
|
roomSlug,
|
|
@@ -430,8 +504,10 @@ var OeMeeting = (0, import_react2.forwardRef)(
|
|
|
430
504
|
autoJoin,
|
|
431
505
|
hideLeave,
|
|
432
506
|
autoCameraOff,
|
|
507
|
+
disableCamera,
|
|
433
508
|
autoTranscribe,
|
|
434
509
|
autoRecord,
|
|
510
|
+
background: initialBackgroundRef.current,
|
|
435
511
|
instanceId,
|
|
436
512
|
hostOrigin
|
|
437
513
|
}),
|
|
@@ -448,10 +524,12 @@ var OeMeeting = (0, import_react2.forwardRef)(
|
|
|
448
524
|
origin,
|
|
449
525
|
roomSlug,
|
|
450
526
|
autoCameraOff,
|
|
527
|
+
disableCamera,
|
|
451
528
|
userName
|
|
452
529
|
]
|
|
453
530
|
);
|
|
454
531
|
const { iframeRef } = useOeMeeting({
|
|
532
|
+
background,
|
|
455
533
|
instanceId,
|
|
456
534
|
targetOrigin: resolvedTargetOrigin,
|
|
457
535
|
handleRef: ref,
|
|
@@ -510,6 +588,7 @@ OeMeeting.displayName = "OeMeeting";
|
|
|
510
588
|
DEFAULT_IFRAME_ALLOW,
|
|
511
589
|
EMBED_MESSAGE_SOURCE,
|
|
512
590
|
EMBED_PROTOCOL_VERSION,
|
|
591
|
+
EmbedBackgroundType,
|
|
513
592
|
EmbedCapability,
|
|
514
593
|
EmbedCaptureOutcome,
|
|
515
594
|
EmbedCommandAction,
|
|
@@ -518,14 +597,17 @@ OeMeeting.displayName = "OeMeeting";
|
|
|
518
597
|
EmbedEventType,
|
|
519
598
|
EmbedQueryParam,
|
|
520
599
|
EmbedRole,
|
|
600
|
+
MAX_BACKGROUND_URL_LENGTH,
|
|
521
601
|
MAX_STAGE_TOOL_ID_LENGTH,
|
|
522
602
|
MAX_STAGE_URL_LENGTH,
|
|
523
603
|
OeMeeting,
|
|
524
604
|
buildEmbedUrl,
|
|
525
605
|
isStageToolId,
|
|
526
606
|
isValidStageUrl,
|
|
607
|
+
normalizeEmbedBackground,
|
|
527
608
|
normalizeOpenStageInput,
|
|
528
609
|
parseEmbedEvent,
|
|
610
|
+
serializeEmbedBackground,
|
|
529
611
|
useOeMeeting
|
|
530
612
|
});
|
|
531
613
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/OeMeeting.tsx","../src/embed-protocol.ts","../src/buildEmbedUrl.ts","../src/useOeMeeting.ts","../src/openStage.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 EmbedCapability,\n EmbedCaptureOutcome,\n EmbedCommandAction,\n EmbedRole,\n EmbedCommandType,\n EmbedErrorCode,\n EmbedEventType,\n EmbedQueryParam,\n isStageToolId,\n MAX_STAGE_TOOL_ID_LENGTH,\n MAX_STAGE_URL_LENGTH,\n} from './embed-protocol';\nexport type { EmbedPermissionMap, StageToolId } from './embed-protocol';\nexport { isValidStageUrl, normalizeOpenStageInput } from './openStage';\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 EmbedRecordingSavedPayload,\n EmbedRecordingStartedPayload,\n EmbedStageChangedPayload,\n EmbedTranscriptStartedPayload,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\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 autoCameraOff,\n autoTranscribe,\n autoRecord,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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 autoCameraOff,\n autoTranscribe,\n autoRecord,\n instanceId,\n hostOrigin,\n }),\n [\n autoJoin,\n autoRecord,\n autoTranscribe,\n externalUserId,\n hideLeave,\n hostOrigin,\n instanceId,\n joinToken,\n lang,\n origin,\n roomSlug,\n autoCameraOff,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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.ocean.edu.vn';\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 STAGE_CHANGED = 'oemeeting:stage-changed',\n TRANSCRIPT_STARTED = 'oemeeting:transcript-started',\n RECORDING_STARTED = 'oemeeting:recording-started',\n RECORDING_SAVED = 'oemeeting:recording-saved',\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 OPEN_STAGE = 'openStage',\n CLOSE_STAGE = 'closeStage',\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 STAGE_ORIGIN_DENIED = 'STAGE_ORIGIN_DENIED',\n STAGE_URL_INVALID = 'STAGE_URL_INVALID',\n TRANSCRIPT_START_FAILED = 'TRANSCRIPT_START_FAILED',\n RECORDING_START_FAILED = 'RECORDING_START_FAILED',\n UNKNOWN = 'UNKNOWN',\n}\n\n/** Open host-owned tool id. Meeting does not catalog values. */\nexport type StageToolId = string;\n\nexport const MAX_STAGE_URL_LENGTH = 2048;\nexport const MAX_STAGE_TOOL_ID_LENGTH = 32;\n\nconst STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;\n\nexport function isStageToolId(value: unknown): value is StageToolId {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_STAGE_TOOL_ID_LENGTH &&\n STAGE_TOOL_ID_PATTERN.test(value)\n );\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 AUTO_CAMERA_OFF = 'autoCameraOff',\n AUTO_TRANSCRIBE = 'autoTranscribe',\n AUTO_RECORD = 'autoRecord',\n}\n\n/**\n * What happened to the auto-transcribe request the embed made.\n *\n * `ALREADY_RUNNING` is the normal outcome for every participant after the\n * first, because the transcript is a room-level asset rather than a\n * per-participant one. `DISABLED` means the OE Meeting deployment has\n * transcription turned off.\n */\nexport enum EmbedCaptureOutcome {\n STARTED = 'started',\n ALREADY_RUNNING = 'already_running',\n DISABLED = 'disabled',\n FAILED = 'failed',\n}\n\n/**\n * Role the host app assigns when minting a join token.\n * Keep values aligned with `EmbedRole` in the backend embed-permission enum.\n */\nexport enum EmbedRole {\n OWNER = 'owner',\n MEMBER = 'member',\n}\n\n/**\n * Capability the host app may grant per role. Meeting enforces this on the\n * join token, not on a React prop. `screenShare` is the only value today.\n */\nexport enum EmbedCapability {\n SCREEN_SHARE = 'screenShare',\n}\n\nexport interface EmbedPermissionMap {\n owner?: EmbedCapability[];\n member?: EmbedCapability[];\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 [EmbedQueryParam.AUTO_CAMERA_OFF, toFlag(options.autoCameraOff)],\n [EmbedQueryParam.AUTO_TRANSCRIBE, toFlag(options.autoTranscribe)],\n [EmbedQueryParam.AUTO_RECORD, toFlag(options.autoRecord)],\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 { normalizeOpenStageInput } from './openStage';\nimport { parseEmbedEvent } from './parseEmbedEvent';\nimport type {\n EmbedCommandMessage,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\n} from './types';\n\ntype UseOeMeetingOptions = Pick<\n OeMeetingProps,\n | 'onReady'\n | 'onJoined'\n | 'onLeft'\n | 'onParticipantJoined'\n | 'onParticipantLeft'\n | 'onEnded'\n | 'onError'\n | 'onStageChanged'\n | 'onTranscriptStarted'\n | 'onRecordingStarted'\n | 'onRecordingSaved'\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 payload?: OpenStageInput;\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 ...(options.payload ? { payload: options.payload } : {}),\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 case EmbedEventType.STAGE_CHANGED:\n callbacks.onStageChanged?.(embedEvent);\n break;\n case EmbedEventType.TRANSCRIPT_STARTED:\n callbacks.onTranscriptStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_STARTED:\n callbacks.onRecordingStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_SAVED:\n callbacks.onRecordingSaved?.(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, payload?: OpenStageInput) =>\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action,\n targetOrigin: options.targetOrigin,\n payload,\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 openStage = useCallback(\n (input: OpenStageInput) => {\n const normalized = normalizeOpenStageInput(input);\n if (!normalized) return;\n send(EmbedCommandAction.OPEN_STAGE, normalized);\n },\n [send],\n );\n const closeStage = useCallback(\n () => send(EmbedCommandAction.CLOSE_STAGE),\n [send],\n );\n const getIframe = useCallback(() => iframeRef.current, []);\n const handle = useMemo<OeMeetingHandle>(\n () => ({\n leave,\n toggleMic,\n toggleCamera,\n openStage,\n closeStage,\n getIframe,\n }),\n [closeStage, getIframe, leave, openStage, toggleCamera, toggleMic],\n );\n\n useImperativeHandle(options.handleRef, () => handle, [handle]);\n return { iframeRef, handle };\n}\n","import { isStageToolId, MAX_STAGE_URL_LENGTH } from './embed-protocol';\nimport type { OpenStageInput } from './types';\n\nexport function isValidStageUrl(url: string): boolean {\n if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;\n try {\n const parsed = new URL(url);\n if (parsed.protocol === 'https:') return true;\n if (parsed.protocol === 'http:') {\n return (\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1'\n );\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function normalizeOpenStageInput(\n input: OpenStageInput,\n): OpenStageInput | null {\n const url = input.url.trim();\n if (!isValidStageUrl(url)) return null;\n const title = input.title?.trim();\n return {\n url,\n ...(title ? { title: title.slice(0, 200) } : {}),\n ...(isStageToolId(input.tool) ? { tool: input.tool } : {}),\n };\n}\n","import {\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EmbedCaptureOutcome,\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 isNullableString(value: unknown): boolean {\n return value === null || typeof value === 'string';\n}\n\nconst CAPTURE_OUTCOMES = new Set<string>(Object.values(EmbedCaptureOutcome));\n\nfunction isCaptureOutcome(value: unknown): boolean {\n return typeof value === 'string' && CAPTURE_OUTCOMES.has(value);\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 case EmbedEventType.STAGE_CHANGED:\n return (\n (payload.url === null || typeof payload.url === 'string') &&\n isOptionalString(payload.title) &&\n isOptionalString(payload.tool) &&\n isOptionalString(payload.openedBy)\n );\n case EmbedEventType.TRANSCRIPT_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.transcriptId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.recordingId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_SAVED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.savedAt === '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","EmbedCaptureOutcome","EmbedRole","EmbedCapability","OeMeeting"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/OeMeeting.tsx","../src/embed-protocol.ts","../src/openStage.ts","../src/background.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 EmbedCapability,\n EmbedCaptureOutcome,\n EmbedBackgroundType,\n EmbedCommandAction,\n EmbedRole,\n EmbedCommandType,\n EmbedErrorCode,\n EmbedEventType,\n EmbedQueryParam,\n isStageToolId,\n MAX_BACKGROUND_URL_LENGTH,\n MAX_STAGE_TOOL_ID_LENGTH,\n MAX_STAGE_URL_LENGTH,\n} from './embed-protocol';\nexport type { EmbedBackground, EmbedPermissionMap, StageToolId } from './embed-protocol';\nexport { isValidStageUrl, normalizeOpenStageInput } from './openStage';\nexport { normalizeEmbedBackground, serializeEmbedBackground } from './background';\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 EmbedRecordingSavedPayload,\n EmbedRecordingStartedPayload,\n EmbedStageChangedPayload,\n EmbedTranscriptStartedPayload,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\n} from './types';\n","'use client';\n\nimport {\n forwardRef,\n useId,\n useMemo,\n useRef,\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 autoCameraOff,\n disableCamera,\n autoTranscribe,\n autoRecord,\n background,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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 // Only the mount-time background rides the embed URL. Later prop changes\n // travel as `setBackground` commands (useOeMeeting); rebuilding src would\n // reload the whole meeting mid-session.\n const initialBackgroundRef = useRef(background);\n const src = useMemo(\n () =>\n buildEmbedUrl({\n roomSlug,\n joinToken,\n userName,\n externalUserId,\n origin,\n lang,\n autoJoin,\n hideLeave,\n autoCameraOff,\n disableCamera,\n autoTranscribe,\n autoRecord,\n background: initialBackgroundRef.current,\n instanceId,\n hostOrigin,\n }),\n [\n autoJoin,\n autoRecord,\n autoTranscribe,\n externalUserId,\n hideLeave,\n hostOrigin,\n instanceId,\n joinToken,\n lang,\n origin,\n roomSlug,\n autoCameraOff,\n disableCamera,\n userName,\n ],\n );\n const { iframeRef } = useOeMeeting({\n background,\n instanceId,\n targetOrigin: resolvedTargetOrigin,\n handleRef: ref,\n onReady,\n onJoined,\n onLeft,\n onParticipantJoined,\n onParticipantLeft,\n onEnded,\n onError,\n onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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.ocean.edu.vn';\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 STAGE_CHANGED = 'oemeeting:stage-changed',\n TRANSCRIPT_STARTED = 'oemeeting:transcript-started',\n RECORDING_STARTED = 'oemeeting:recording-started',\n RECORDING_SAVED = 'oemeeting:recording-saved',\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 OPEN_STAGE = 'openStage',\n CLOSE_STAGE = 'closeStage',\n SET_BACKGROUND = 'setBackground',\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 STAGE_ORIGIN_DENIED = 'STAGE_ORIGIN_DENIED',\n STAGE_URL_INVALID = 'STAGE_URL_INVALID',\n TRANSCRIPT_START_FAILED = 'TRANSCRIPT_START_FAILED',\n RECORDING_START_FAILED = 'RECORDING_START_FAILED',\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * Host-controlled camera background applied to this embed's local user only.\n * `none` clears any active background effect. The host app enforces who may\n * set it; the meeting does not interpret roles for this value.\n */\nexport enum EmbedBackgroundType {\n BLUR = 'blur',\n IMAGE = 'image',\n NONE = 'none',\n}\n\nexport const MAX_BACKGROUND_URL_LENGTH = 2048;\nexport const DEFAULT_BACKGROUND_BLUR_RADIUS = 10;\nexport const MIN_BACKGROUND_BLUR_RADIUS = 1;\nexport const MAX_BACKGROUND_BLUR_RADIUS = 50;\n\nexport interface EmbedBackground {\n type: EmbedBackgroundType;\n /** BLUR only. Defaults to `DEFAULT_BACKGROUND_BLUR_RADIUS`, clamped 1-50. */\n blurRadius?: number;\n /** IMAGE only. http(s) URL, max `MAX_BACKGROUND_URL_LENGTH`. */\n url?: string;\n /**\n * @deprecated Ignored by the meeting: a BLUR/IMAGE background always locks\n * the embed user's background and hides the Effects panel; NONE unlocks.\n */\n locked?: boolean;\n}\n\n/** Open host-owned tool id. Meeting does not catalog values. */\nexport type StageToolId = string;\n\nexport const MAX_STAGE_URL_LENGTH = 2048;\nexport const MAX_STAGE_TOOL_ID_LENGTH = 32;\n\nconst STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;\n\nexport function isStageToolId(value: unknown): value is StageToolId {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_STAGE_TOOL_ID_LENGTH &&\n STAGE_TOOL_ID_PATTERN.test(value)\n );\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 AUTO_CAMERA_OFF = 'autoCameraOff',\n DISABLE_CAMERA = 'disableCamera',\n AUTO_TRANSCRIBE = 'autoTranscribe',\n AUTO_RECORD = 'autoRecord',\n BACKGROUND = 'background',\n}\n\n/**\n * What happened to the auto-transcribe request the embed made.\n *\n * `ALREADY_RUNNING` is the normal outcome for every participant after the\n * first, because the transcript is a room-level asset rather than a\n * per-participant one. `DISABLED` means the OE Meeting deployment has\n * transcription turned off.\n */\nexport enum EmbedCaptureOutcome {\n STARTED = 'started',\n ALREADY_RUNNING = 'already_running',\n DISABLED = 'disabled',\n FAILED = 'failed',\n}\n\n/**\n * Role the host app assigns when minting a join token.\n * Keep values aligned with `EmbedRole` in the backend embed-permission enum.\n */\nexport enum EmbedRole {\n OWNER = 'owner',\n MEMBER = 'member',\n}\n\n/**\n * Capability the host app may grant per role. Meeting enforces this on the\n * join token, not on a React prop. `screenShare` is the only value today.\n */\nexport enum EmbedCapability {\n SCREEN_SHARE = 'screenShare',\n}\n\nexport interface EmbedPermissionMap {\n owner?: EmbedCapability[];\n member?: EmbedCapability[];\n}\n","import { isStageToolId, MAX_STAGE_URL_LENGTH } from './embed-protocol';\nimport type { OpenStageInput } from './types';\n\nexport function isValidStageUrl(url: string): boolean {\n if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;\n try {\n const parsed = new URL(url);\n if (parsed.protocol === 'https:') return true;\n if (parsed.protocol === 'http:') {\n return (\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1'\n );\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function normalizeOpenStageInput(\n input: OpenStageInput,\n): OpenStageInput | null {\n const url = input.url.trim();\n if (!isValidStageUrl(url)) return null;\n const title = input.title?.trim();\n return {\n url,\n ...(title ? { title: title.slice(0, 200) } : {}),\n ...(isStageToolId(input.tool) ? { tool: input.tool } : {}),\n };\n}\n","import { isValidStageUrl } from './openStage';\nimport {\n DEFAULT_BACKGROUND_BLUR_RADIUS,\n EmbedBackgroundType,\n MAX_BACKGROUND_BLUR_RADIUS,\n MAX_BACKGROUND_URL_LENGTH,\n MIN_BACKGROUND_BLUR_RADIUS,\n type EmbedBackground,\n} from './embed-protocol';\n\n/** Same http(s) policy as the stage URL, including the localhost exception. */\nconst isValidBackgroundUrl = (url: string): boolean =>\n url.length > 0 &&\n url.length <= MAX_BACKGROUND_URL_LENGTH &&\n isValidStageUrl(url);\n\nconst clampBlurRadius = (value: number): number =>\n Number.isFinite(value)\n ? Math.min(MAX_BACKGROUND_BLUR_RADIUS, Math.max(MIN_BACKGROUND_BLUR_RADIUS, Math.round(value)))\n : DEFAULT_BACKGROUND_BLUR_RADIUS;\n\n/**\n * Validate and normalize a host-provided background. Returns null for invalid\n * input so callers can silently drop it, mirroring `normalizeOpenStageInput`.\n */\nexport function normalizeEmbedBackground(\n input: EmbedBackground,\n): EmbedBackground | null {\n const locked = input.locked === true ? { locked: true } : {};\n\n if (input.type === EmbedBackgroundType.NONE) {\n return { type: EmbedBackgroundType.NONE, ...locked };\n }\n\n if (input.type === EmbedBackgroundType.BLUR) {\n return {\n type: EmbedBackgroundType.BLUR,\n blurRadius: clampBlurRadius(input.blurRadius ?? DEFAULT_BACKGROUND_BLUR_RADIUS),\n ...locked,\n };\n }\n\n if (input.type === EmbedBackgroundType.IMAGE) {\n const url = input.url?.trim();\n if (!url || !isValidBackgroundUrl(url)) return null;\n return {\n type: EmbedBackgroundType.IMAGE,\n url,\n ...locked,\n };\n }\n\n return null;\n}\n\n/** JSON wire format shared by the `background` query param and the command payload. */\nexport function serializeEmbedBackground(\n input: EmbedBackground,\n): string | undefined {\n const normalized = normalizeEmbedBackground(input);\n return normalized ? JSON.stringify(normalized) : undefined;\n}\n","import {\n DEFAULT_EMBED_ORIGIN,\n EmbedQueryParam,\n} from './embed-protocol';\nimport { serializeEmbedBackground } from './background';\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 backgroundParam = options.background\n ? serializeEmbedBackground(options.background)\n : undefined;\n\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 [EmbedQueryParam.AUTO_CAMERA_OFF, toFlag(options.autoCameraOff)],\n [EmbedQueryParam.DISABLE_CAMERA, toFlag(options.disableCamera)],\n [EmbedQueryParam.AUTO_TRANSCRIBE, toFlag(options.autoTranscribe)],\n [EmbedQueryParam.AUTO_RECORD, toFlag(options.autoRecord)],\n [EmbedQueryParam.BACKGROUND, backgroundParam],\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 { normalizeEmbedBackground } from './background';\nimport { normalizeOpenStageInput } from './openStage';\nimport { parseEmbedEvent } from './parseEmbedEvent';\nimport type {\n EmbedCommandMessage,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\n} from './types';\nimport type { EmbedBackground } from './embed-protocol';\n\ntype UseOeMeetingOptions = Pick<\n OeMeetingProps,\n | 'onReady'\n | 'onJoined'\n | 'onLeft'\n | 'onParticipantJoined'\n | 'onParticipantLeft'\n | 'onEnded'\n | 'onError'\n | 'onStageChanged'\n | 'onTranscriptStarted'\n | 'onRecordingStarted'\n | 'onRecordingSaved'\n | 'onEvent'\n | 'background'\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 payload?: OpenStageInput | EmbedBackground;\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 ...(options.payload ? { payload: options.payload } : {}),\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 // Last background the host set. Resent on READY: commands posted before the\n // iframe bridge mounts are dropped by the browser.\n const pendingBackgroundRef = useRef<EmbedBackground | null>(null);\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 if (pendingBackgroundRef.current) {\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action: EmbedCommandAction.SET_BACKGROUND,\n targetOrigin: options.targetOrigin,\n payload: pendingBackgroundRef.current,\n });\n }\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 case EmbedEventType.STAGE_CHANGED:\n callbacks.onStageChanged?.(embedEvent);\n break;\n case EmbedEventType.TRANSCRIPT_STARTED:\n callbacks.onTranscriptStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_STARTED:\n callbacks.onRecordingStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_SAVED:\n callbacks.onRecordingSaved?.(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 (\n action: EmbedCommandAction,\n payload?: OpenStageInput | EmbedBackground\n ) =>\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action,\n targetOrigin: options.targetOrigin,\n payload,\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 openStage = useCallback(\n (input: OpenStageInput) => {\n const normalized = normalizeOpenStageInput(input);\n if (!normalized) return;\n send(EmbedCommandAction.OPEN_STAGE, normalized);\n },\n [send],\n );\n const closeStage = useCallback(\n () => send(EmbedCommandAction.CLOSE_STAGE),\n [send],\n );\n\n useEffect(() => {\n if (!options.background) {\n pendingBackgroundRef.current = null;\n return;\n }\n const normalized = normalizeEmbedBackground(options.background);\n if (!normalized) return;\n pendingBackgroundRef.current = normalized;\n send(EmbedCommandAction.SET_BACKGROUND, normalized);\n }, [options.background, send]);\n const getIframe = useCallback(() => iframeRef.current, []);\n const handle = useMemo<OeMeetingHandle>(\n () => ({\n leave,\n toggleMic,\n toggleCamera,\n openStage,\n closeStage,\n getIframe,\n }),\n [closeStage, getIframe, leave, openStage, 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 EmbedCaptureOutcome,\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 isNullableString(value: unknown): boolean {\n return value === null || typeof value === 'string';\n}\n\nconst CAPTURE_OUTCOMES = new Set<string>(Object.values(EmbedCaptureOutcome));\n\nfunction isCaptureOutcome(value: unknown): boolean {\n return typeof value === 'string' && CAPTURE_OUTCOMES.has(value);\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 case EmbedEventType.STAGE_CHANGED:\n return (\n (payload.url === null || typeof payload.url === 'string') &&\n isOptionalString(payload.title) &&\n isOptionalString(payload.tool) &&\n isOptionalString(payload.openedBy)\n );\n case EmbedEventType.TRANSCRIPT_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.transcriptId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.recordingId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_SAVED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.savedAt === '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","EmbedBackgroundType","EmbedQueryParam","EmbedCaptureOutcome","EmbedRole","EmbedCapability","OeMeeting"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -25,7 +25,8 @@ declare enum EmbedCommandAction {
|
|
|
25
25
|
TOGGLE_MIC = "toggleMic",
|
|
26
26
|
TOGGLE_CAMERA = "toggleCamera",
|
|
27
27
|
OPEN_STAGE = "openStage",
|
|
28
|
-
CLOSE_STAGE = "closeStage"
|
|
28
|
+
CLOSE_STAGE = "closeStage",
|
|
29
|
+
SET_BACKGROUND = "setBackground"
|
|
29
30
|
}
|
|
30
31
|
declare enum EmbedErrorCode {
|
|
31
32
|
TOKEN_INVALID = "TOKEN_INVALID",
|
|
@@ -40,6 +41,29 @@ declare enum EmbedErrorCode {
|
|
|
40
41
|
RECORDING_START_FAILED = "RECORDING_START_FAILED",
|
|
41
42
|
UNKNOWN = "UNKNOWN"
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Host-controlled camera background applied to this embed's local user only.
|
|
46
|
+
* `none` clears any active background effect. The host app enforces who may
|
|
47
|
+
* set it; the meeting does not interpret roles for this value.
|
|
48
|
+
*/
|
|
49
|
+
declare enum EmbedBackgroundType {
|
|
50
|
+
BLUR = "blur",
|
|
51
|
+
IMAGE = "image",
|
|
52
|
+
NONE = "none"
|
|
53
|
+
}
|
|
54
|
+
declare const MAX_BACKGROUND_URL_LENGTH = 2048;
|
|
55
|
+
interface EmbedBackground {
|
|
56
|
+
type: EmbedBackgroundType;
|
|
57
|
+
/** BLUR only. Defaults to `DEFAULT_BACKGROUND_BLUR_RADIUS`, clamped 1-50. */
|
|
58
|
+
blurRadius?: number;
|
|
59
|
+
/** IMAGE only. http(s) URL, max `MAX_BACKGROUND_URL_LENGTH`. */
|
|
60
|
+
url?: string;
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated Ignored by the meeting: a BLUR/IMAGE background always locks
|
|
63
|
+
* the embed user's background and hides the Effects panel; NONE unlocks.
|
|
64
|
+
*/
|
|
65
|
+
locked?: boolean;
|
|
66
|
+
}
|
|
43
67
|
/** Open host-owned tool id. Meeting does not catalog values. */
|
|
44
68
|
type StageToolId = string;
|
|
45
69
|
declare const MAX_STAGE_URL_LENGTH = 2048;
|
|
@@ -55,8 +79,10 @@ declare enum EmbedQueryParam {
|
|
|
55
79
|
AUTO_JOIN = "autoJoin",
|
|
56
80
|
HIDE_LEAVE = "hideLeave",
|
|
57
81
|
AUTO_CAMERA_OFF = "autoCameraOff",
|
|
82
|
+
DISABLE_CAMERA = "disableCamera",
|
|
58
83
|
AUTO_TRANSCRIBE = "autoTranscribe",
|
|
59
|
-
AUTO_RECORD = "autoRecord"
|
|
84
|
+
AUTO_RECORD = "autoRecord",
|
|
85
|
+
BACKGROUND = "background"
|
|
60
86
|
}
|
|
61
87
|
/**
|
|
62
88
|
* What happened to the auto-transcribe request the embed made.
|
|
@@ -197,7 +223,7 @@ type EmbedEventOf<T extends EmbedEventType> = Extract<EmbedEvent, {
|
|
|
197
223
|
type EmbedCommandMessage = EmbedMessageBase & {
|
|
198
224
|
type: 'oemeeting:command';
|
|
199
225
|
action: EmbedCommandAction;
|
|
200
|
-
payload?: OpenStageInput;
|
|
226
|
+
payload?: OpenStageInput | EmbedBackground;
|
|
201
227
|
};
|
|
202
228
|
interface OeMeetingHandle {
|
|
203
229
|
leave: () => void;
|
|
@@ -222,6 +248,11 @@ interface OeMeetingProps {
|
|
|
222
248
|
* still turn the camera on manually.
|
|
223
249
|
*/
|
|
224
250
|
autoCameraOff?: boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Keep the camera off for this embed. The prejoin control, the in-room
|
|
253
|
+
* button, the camera shortcut, and `toggleCamera()` cannot turn it back on.
|
|
254
|
+
*/
|
|
255
|
+
disableCamera?: boolean;
|
|
225
256
|
/**
|
|
226
257
|
* Start the shared transcript as soon as the embed connects. The transcript
|
|
227
258
|
* is a room-level asset: the first participant to connect starts it and it
|
|
@@ -231,6 +262,14 @@ interface OeMeetingProps {
|
|
|
231
262
|
autoTranscribe?: boolean;
|
|
232
263
|
/** Start the shared room recording as soon as the embed connects. */
|
|
233
264
|
autoRecord?: boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Apply a camera background to this embed's local user (blur / image /
|
|
267
|
+
* none). `blur` and `image` also hide the user's Effects panel. Pass the
|
|
268
|
+
* same value for every participant to brand an event. The initial value
|
|
269
|
+
* rides the embed URL; later changes are sent as a `setBackground` command
|
|
270
|
+
* once the embed is ready. Invalid input is ignored.
|
|
271
|
+
*/
|
|
272
|
+
background?: EmbedBackground;
|
|
234
273
|
title?: string;
|
|
235
274
|
width?: number | string;
|
|
236
275
|
height?: number | string;
|
|
@@ -268,8 +307,10 @@ interface BuildEmbedUrlOptions {
|
|
|
268
307
|
autoJoin?: boolean;
|
|
269
308
|
hideLeave?: boolean;
|
|
270
309
|
autoCameraOff?: boolean;
|
|
310
|
+
disableCamera?: boolean;
|
|
271
311
|
autoTranscribe?: boolean;
|
|
272
312
|
autoRecord?: boolean;
|
|
313
|
+
background?: EmbedBackground;
|
|
273
314
|
instanceId?: string;
|
|
274
315
|
hostOrigin?: string;
|
|
275
316
|
}
|
|
@@ -277,7 +318,7 @@ interface BuildEmbedUrlOptions {
|
|
|
277
318
|
declare const DEFAULT_IFRAME_ALLOW = "camera; microphone; fullscreen; autoplay; display-capture; clipboard-write; encrypted-media";
|
|
278
319
|
declare const OeMeeting: react.ForwardRefExoticComponent<OeMeetingProps & react.RefAttributes<OeMeetingHandle>>;
|
|
279
320
|
|
|
280
|
-
type UseOeMeetingOptions = Pick<OeMeetingProps, 'onReady' | 'onJoined' | 'onLeft' | 'onParticipantJoined' | 'onParticipantLeft' | 'onEnded' | 'onError' | 'onStageChanged' | 'onTranscriptStarted' | 'onRecordingStarted' | 'onRecordingSaved' | 'onEvent'> & {
|
|
321
|
+
type UseOeMeetingOptions = Pick<OeMeetingProps, 'onReady' | 'onJoined' | 'onLeft' | 'onParticipantJoined' | 'onParticipantLeft' | 'onEnded' | 'onError' | 'onStageChanged' | 'onTranscriptStarted' | 'onRecordingStarted' | 'onRecordingSaved' | 'onEvent' | 'background'> & {
|
|
281
322
|
instanceId: string;
|
|
282
323
|
targetOrigin: string;
|
|
283
324
|
handleRef?: Ref<OeMeetingHandle>;
|
|
@@ -294,4 +335,12 @@ declare function parseEmbedEvent(data: unknown): EmbedEvent | null;
|
|
|
294
335
|
declare function isValidStageUrl(url: string): boolean;
|
|
295
336
|
declare function normalizeOpenStageInput(input: OpenStageInput): OpenStageInput | null;
|
|
296
337
|
|
|
297
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Validate and normalize a host-provided background. Returns null for invalid
|
|
340
|
+
* input so callers can silently drop it, mirroring `normalizeOpenStageInput`.
|
|
341
|
+
*/
|
|
342
|
+
declare function normalizeEmbedBackground(input: EmbedBackground): EmbedBackground | null;
|
|
343
|
+
/** JSON wire format shared by the `background` query param and the command payload. */
|
|
344
|
+
declare function serializeEmbedBackground(input: EmbedBackground): string | undefined;
|
|
345
|
+
|
|
346
|
+
export { type BuildEmbedUrlOptions, DEFAULT_EMBED_ORIGIN, DEFAULT_IFRAME_ALLOW, EMBED_MESSAGE_SOURCE, EMBED_PROTOCOL_VERSION, type EmbedBackground, EmbedBackgroundType, EmbedCapability, EmbedCaptureOutcome, EmbedCommandAction, type EmbedCommandMessage, EmbedCommandType, type EmbedEndedPayload, EmbedErrorCode, type EmbedErrorPayload, type EmbedEvent, type EmbedEventOf, type EmbedEventPayloadMap, EmbedEventType, type EmbedJoinedPayload, type EmbedLeftPayload, type EmbedMessageBase, type EmbedParticipantJoinedPayload, type EmbedParticipantLeftPayload, type EmbedPermissionMap, EmbedQueryParam, type EmbedReadyPayload, type EmbedRecordingSavedPayload, type EmbedRecordingStartedPayload, EmbedRole, type EmbedStageChangedPayload, type EmbedTranscriptStartedPayload, MAX_BACKGROUND_URL_LENGTH, MAX_STAGE_TOOL_ID_LENGTH, MAX_STAGE_URL_LENGTH, OeMeeting, type OeMeetingHandle, type OeMeetingProps, type OpenStageInput, type StageToolId, buildEmbedUrl, isStageToolId, isValidStageUrl, normalizeEmbedBackground, normalizeOpenStageInput, parseEmbedEvent, serializeEmbedBackground, useOeMeeting };
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ declare enum EmbedCommandAction {
|
|
|
25
25
|
TOGGLE_MIC = "toggleMic",
|
|
26
26
|
TOGGLE_CAMERA = "toggleCamera",
|
|
27
27
|
OPEN_STAGE = "openStage",
|
|
28
|
-
CLOSE_STAGE = "closeStage"
|
|
28
|
+
CLOSE_STAGE = "closeStage",
|
|
29
|
+
SET_BACKGROUND = "setBackground"
|
|
29
30
|
}
|
|
30
31
|
declare enum EmbedErrorCode {
|
|
31
32
|
TOKEN_INVALID = "TOKEN_INVALID",
|
|
@@ -40,6 +41,29 @@ declare enum EmbedErrorCode {
|
|
|
40
41
|
RECORDING_START_FAILED = "RECORDING_START_FAILED",
|
|
41
42
|
UNKNOWN = "UNKNOWN"
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Host-controlled camera background applied to this embed's local user only.
|
|
46
|
+
* `none` clears any active background effect. The host app enforces who may
|
|
47
|
+
* set it; the meeting does not interpret roles for this value.
|
|
48
|
+
*/
|
|
49
|
+
declare enum EmbedBackgroundType {
|
|
50
|
+
BLUR = "blur",
|
|
51
|
+
IMAGE = "image",
|
|
52
|
+
NONE = "none"
|
|
53
|
+
}
|
|
54
|
+
declare const MAX_BACKGROUND_URL_LENGTH = 2048;
|
|
55
|
+
interface EmbedBackground {
|
|
56
|
+
type: EmbedBackgroundType;
|
|
57
|
+
/** BLUR only. Defaults to `DEFAULT_BACKGROUND_BLUR_RADIUS`, clamped 1-50. */
|
|
58
|
+
blurRadius?: number;
|
|
59
|
+
/** IMAGE only. http(s) URL, max `MAX_BACKGROUND_URL_LENGTH`. */
|
|
60
|
+
url?: string;
|
|
61
|
+
/**
|
|
62
|
+
* @deprecated Ignored by the meeting: a BLUR/IMAGE background always locks
|
|
63
|
+
* the embed user's background and hides the Effects panel; NONE unlocks.
|
|
64
|
+
*/
|
|
65
|
+
locked?: boolean;
|
|
66
|
+
}
|
|
43
67
|
/** Open host-owned tool id. Meeting does not catalog values. */
|
|
44
68
|
type StageToolId = string;
|
|
45
69
|
declare const MAX_STAGE_URL_LENGTH = 2048;
|
|
@@ -55,8 +79,10 @@ declare enum EmbedQueryParam {
|
|
|
55
79
|
AUTO_JOIN = "autoJoin",
|
|
56
80
|
HIDE_LEAVE = "hideLeave",
|
|
57
81
|
AUTO_CAMERA_OFF = "autoCameraOff",
|
|
82
|
+
DISABLE_CAMERA = "disableCamera",
|
|
58
83
|
AUTO_TRANSCRIBE = "autoTranscribe",
|
|
59
|
-
AUTO_RECORD = "autoRecord"
|
|
84
|
+
AUTO_RECORD = "autoRecord",
|
|
85
|
+
BACKGROUND = "background"
|
|
60
86
|
}
|
|
61
87
|
/**
|
|
62
88
|
* What happened to the auto-transcribe request the embed made.
|
|
@@ -197,7 +223,7 @@ type EmbedEventOf<T extends EmbedEventType> = Extract<EmbedEvent, {
|
|
|
197
223
|
type EmbedCommandMessage = EmbedMessageBase & {
|
|
198
224
|
type: 'oemeeting:command';
|
|
199
225
|
action: EmbedCommandAction;
|
|
200
|
-
payload?: OpenStageInput;
|
|
226
|
+
payload?: OpenStageInput | EmbedBackground;
|
|
201
227
|
};
|
|
202
228
|
interface OeMeetingHandle {
|
|
203
229
|
leave: () => void;
|
|
@@ -222,6 +248,11 @@ interface OeMeetingProps {
|
|
|
222
248
|
* still turn the camera on manually.
|
|
223
249
|
*/
|
|
224
250
|
autoCameraOff?: boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Keep the camera off for this embed. The prejoin control, the in-room
|
|
253
|
+
* button, the camera shortcut, and `toggleCamera()` cannot turn it back on.
|
|
254
|
+
*/
|
|
255
|
+
disableCamera?: boolean;
|
|
225
256
|
/**
|
|
226
257
|
* Start the shared transcript as soon as the embed connects. The transcript
|
|
227
258
|
* is a room-level asset: the first participant to connect starts it and it
|
|
@@ -231,6 +262,14 @@ interface OeMeetingProps {
|
|
|
231
262
|
autoTranscribe?: boolean;
|
|
232
263
|
/** Start the shared room recording as soon as the embed connects. */
|
|
233
264
|
autoRecord?: boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Apply a camera background to this embed's local user (blur / image /
|
|
267
|
+
* none). `blur` and `image` also hide the user's Effects panel. Pass the
|
|
268
|
+
* same value for every participant to brand an event. The initial value
|
|
269
|
+
* rides the embed URL; later changes are sent as a `setBackground` command
|
|
270
|
+
* once the embed is ready. Invalid input is ignored.
|
|
271
|
+
*/
|
|
272
|
+
background?: EmbedBackground;
|
|
234
273
|
title?: string;
|
|
235
274
|
width?: number | string;
|
|
236
275
|
height?: number | string;
|
|
@@ -268,8 +307,10 @@ interface BuildEmbedUrlOptions {
|
|
|
268
307
|
autoJoin?: boolean;
|
|
269
308
|
hideLeave?: boolean;
|
|
270
309
|
autoCameraOff?: boolean;
|
|
310
|
+
disableCamera?: boolean;
|
|
271
311
|
autoTranscribe?: boolean;
|
|
272
312
|
autoRecord?: boolean;
|
|
313
|
+
background?: EmbedBackground;
|
|
273
314
|
instanceId?: string;
|
|
274
315
|
hostOrigin?: string;
|
|
275
316
|
}
|
|
@@ -277,7 +318,7 @@ interface BuildEmbedUrlOptions {
|
|
|
277
318
|
declare const DEFAULT_IFRAME_ALLOW = "camera; microphone; fullscreen; autoplay; display-capture; clipboard-write; encrypted-media";
|
|
278
319
|
declare const OeMeeting: react.ForwardRefExoticComponent<OeMeetingProps & react.RefAttributes<OeMeetingHandle>>;
|
|
279
320
|
|
|
280
|
-
type UseOeMeetingOptions = Pick<OeMeetingProps, 'onReady' | 'onJoined' | 'onLeft' | 'onParticipantJoined' | 'onParticipantLeft' | 'onEnded' | 'onError' | 'onStageChanged' | 'onTranscriptStarted' | 'onRecordingStarted' | 'onRecordingSaved' | 'onEvent'> & {
|
|
321
|
+
type UseOeMeetingOptions = Pick<OeMeetingProps, 'onReady' | 'onJoined' | 'onLeft' | 'onParticipantJoined' | 'onParticipantLeft' | 'onEnded' | 'onError' | 'onStageChanged' | 'onTranscriptStarted' | 'onRecordingStarted' | 'onRecordingSaved' | 'onEvent' | 'background'> & {
|
|
281
322
|
instanceId: string;
|
|
282
323
|
targetOrigin: string;
|
|
283
324
|
handleRef?: Ref<OeMeetingHandle>;
|
|
@@ -294,4 +335,12 @@ declare function parseEmbedEvent(data: unknown): EmbedEvent | null;
|
|
|
294
335
|
declare function isValidStageUrl(url: string): boolean;
|
|
295
336
|
declare function normalizeOpenStageInput(input: OpenStageInput): OpenStageInput | null;
|
|
296
337
|
|
|
297
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Validate and normalize a host-provided background. Returns null for invalid
|
|
340
|
+
* input so callers can silently drop it, mirroring `normalizeOpenStageInput`.
|
|
341
|
+
*/
|
|
342
|
+
declare function normalizeEmbedBackground(input: EmbedBackground): EmbedBackground | null;
|
|
343
|
+
/** JSON wire format shared by the `background` query param and the command payload. */
|
|
344
|
+
declare function serializeEmbedBackground(input: EmbedBackground): string | undefined;
|
|
345
|
+
|
|
346
|
+
export { type BuildEmbedUrlOptions, DEFAULT_EMBED_ORIGIN, DEFAULT_IFRAME_ALLOW, EMBED_MESSAGE_SOURCE, EMBED_PROTOCOL_VERSION, type EmbedBackground, EmbedBackgroundType, EmbedCapability, EmbedCaptureOutcome, EmbedCommandAction, type EmbedCommandMessage, EmbedCommandType, type EmbedEndedPayload, EmbedErrorCode, type EmbedErrorPayload, type EmbedEvent, type EmbedEventOf, type EmbedEventPayloadMap, EmbedEventType, type EmbedJoinedPayload, type EmbedLeftPayload, type EmbedMessageBase, type EmbedParticipantJoinedPayload, type EmbedParticipantLeftPayload, type EmbedPermissionMap, EmbedQueryParam, type EmbedReadyPayload, type EmbedRecordingSavedPayload, type EmbedRecordingStartedPayload, EmbedRole, type EmbedStageChangedPayload, type EmbedTranscriptStartedPayload, MAX_BACKGROUND_URL_LENGTH, MAX_STAGE_TOOL_ID_LENGTH, MAX_STAGE_URL_LENGTH, OeMeeting, type OeMeetingHandle, type OeMeetingProps, type OpenStageInput, type StageToolId, buildEmbedUrl, isStageToolId, isValidStageUrl, normalizeEmbedBackground, normalizeOpenStageInput, parseEmbedEvent, serializeEmbedBackground, useOeMeeting };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
forwardRef,
|
|
4
4
|
useId,
|
|
5
|
-
useMemo as useMemo2
|
|
5
|
+
useMemo as useMemo2,
|
|
6
|
+
useRef as useRef2
|
|
6
7
|
} from "react";
|
|
7
8
|
|
|
8
9
|
// src/embed-protocol.ts
|
|
@@ -33,6 +34,7 @@ var EmbedCommandAction = /* @__PURE__ */ ((EmbedCommandAction2) => {
|
|
|
33
34
|
EmbedCommandAction2["TOGGLE_CAMERA"] = "toggleCamera";
|
|
34
35
|
EmbedCommandAction2["OPEN_STAGE"] = "openStage";
|
|
35
36
|
EmbedCommandAction2["CLOSE_STAGE"] = "closeStage";
|
|
37
|
+
EmbedCommandAction2["SET_BACKGROUND"] = "setBackground";
|
|
36
38
|
return EmbedCommandAction2;
|
|
37
39
|
})(EmbedCommandAction || {});
|
|
38
40
|
var EmbedErrorCode = /* @__PURE__ */ ((EmbedErrorCode2) => {
|
|
@@ -49,6 +51,16 @@ var EmbedErrorCode = /* @__PURE__ */ ((EmbedErrorCode2) => {
|
|
|
49
51
|
EmbedErrorCode2["UNKNOWN"] = "UNKNOWN";
|
|
50
52
|
return EmbedErrorCode2;
|
|
51
53
|
})(EmbedErrorCode || {});
|
|
54
|
+
var EmbedBackgroundType = /* @__PURE__ */ ((EmbedBackgroundType2) => {
|
|
55
|
+
EmbedBackgroundType2["BLUR"] = "blur";
|
|
56
|
+
EmbedBackgroundType2["IMAGE"] = "image";
|
|
57
|
+
EmbedBackgroundType2["NONE"] = "none";
|
|
58
|
+
return EmbedBackgroundType2;
|
|
59
|
+
})(EmbedBackgroundType || {});
|
|
60
|
+
var MAX_BACKGROUND_URL_LENGTH = 2048;
|
|
61
|
+
var DEFAULT_BACKGROUND_BLUR_RADIUS = 10;
|
|
62
|
+
var MIN_BACKGROUND_BLUR_RADIUS = 1;
|
|
63
|
+
var MAX_BACKGROUND_BLUR_RADIUS = 50;
|
|
52
64
|
var MAX_STAGE_URL_LENGTH = 2048;
|
|
53
65
|
var MAX_STAGE_TOOL_ID_LENGTH = 32;
|
|
54
66
|
var STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;
|
|
@@ -65,8 +77,10 @@ var EmbedQueryParam = /* @__PURE__ */ ((EmbedQueryParam2) => {
|
|
|
65
77
|
EmbedQueryParam2["AUTO_JOIN"] = "autoJoin";
|
|
66
78
|
EmbedQueryParam2["HIDE_LEAVE"] = "hideLeave";
|
|
67
79
|
EmbedQueryParam2["AUTO_CAMERA_OFF"] = "autoCameraOff";
|
|
80
|
+
EmbedQueryParam2["DISABLE_CAMERA"] = "disableCamera";
|
|
68
81
|
EmbedQueryParam2["AUTO_TRANSCRIBE"] = "autoTranscribe";
|
|
69
82
|
EmbedQueryParam2["AUTO_RECORD"] = "autoRecord";
|
|
83
|
+
EmbedQueryParam2["BACKGROUND"] = "background";
|
|
70
84
|
return EmbedQueryParam2;
|
|
71
85
|
})(EmbedQueryParam || {});
|
|
72
86
|
var EmbedCaptureOutcome = /* @__PURE__ */ ((EmbedCaptureOutcome2) => {
|
|
@@ -86,6 +100,62 @@ var EmbedCapability = /* @__PURE__ */ ((EmbedCapability2) => {
|
|
|
86
100
|
return EmbedCapability2;
|
|
87
101
|
})(EmbedCapability || {});
|
|
88
102
|
|
|
103
|
+
// src/openStage.ts
|
|
104
|
+
function isValidStageUrl(url) {
|
|
105
|
+
if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;
|
|
106
|
+
try {
|
|
107
|
+
const parsed = new URL(url);
|
|
108
|
+
if (parsed.protocol === "https:") return true;
|
|
109
|
+
if (parsed.protocol === "http:") {
|
|
110
|
+
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function normalizeOpenStageInput(input) {
|
|
118
|
+
const url = input.url.trim();
|
|
119
|
+
if (!isValidStageUrl(url)) return null;
|
|
120
|
+
const title = input.title?.trim();
|
|
121
|
+
return {
|
|
122
|
+
url,
|
|
123
|
+
...title ? { title: title.slice(0, 200) } : {},
|
|
124
|
+
...isStageToolId(input.tool) ? { tool: input.tool } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/background.ts
|
|
129
|
+
var isValidBackgroundUrl = (url) => url.length > 0 && url.length <= MAX_BACKGROUND_URL_LENGTH && isValidStageUrl(url);
|
|
130
|
+
var clampBlurRadius = (value) => Number.isFinite(value) ? Math.min(MAX_BACKGROUND_BLUR_RADIUS, Math.max(MIN_BACKGROUND_BLUR_RADIUS, Math.round(value))) : DEFAULT_BACKGROUND_BLUR_RADIUS;
|
|
131
|
+
function normalizeEmbedBackground(input) {
|
|
132
|
+
const locked = input.locked === true ? { locked: true } : {};
|
|
133
|
+
if (input.type === "none" /* NONE */) {
|
|
134
|
+
return { type: "none" /* NONE */, ...locked };
|
|
135
|
+
}
|
|
136
|
+
if (input.type === "blur" /* BLUR */) {
|
|
137
|
+
return {
|
|
138
|
+
type: "blur" /* BLUR */,
|
|
139
|
+
blurRadius: clampBlurRadius(input.blurRadius ?? DEFAULT_BACKGROUND_BLUR_RADIUS),
|
|
140
|
+
...locked
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (input.type === "image" /* IMAGE */) {
|
|
144
|
+
const url = input.url?.trim();
|
|
145
|
+
if (!url || !isValidBackgroundUrl(url)) return null;
|
|
146
|
+
return {
|
|
147
|
+
type: "image" /* IMAGE */,
|
|
148
|
+
url,
|
|
149
|
+
...locked
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function serializeEmbedBackground(input) {
|
|
155
|
+
const normalized = normalizeEmbedBackground(input);
|
|
156
|
+
return normalized ? JSON.stringify(normalized) : void 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
89
159
|
// src/buildEmbedUrl.ts
|
|
90
160
|
var QUERY_FLAG_ENABLED = "1";
|
|
91
161
|
var QUERY_FLAG_DISABLED = "0";
|
|
@@ -107,6 +177,7 @@ function buildEmbedUrl(options) {
|
|
|
107
177
|
}
|
|
108
178
|
const origin = normalizeOrigin(options.origin ?? DEFAULT_EMBED_ORIGIN);
|
|
109
179
|
const url = new URL(`${origin}/embed/${encodeURIComponent(roomSlug)}`);
|
|
180
|
+
const backgroundParam = options.background ? serializeEmbedBackground(options.background) : void 0;
|
|
110
181
|
const entries = [
|
|
111
182
|
["instanceId" /* INSTANCE_ID */, options.instanceId],
|
|
112
183
|
["hostOrigin" /* HOST_ORIGIN */, options.hostOrigin],
|
|
@@ -117,8 +188,10 @@ function buildEmbedUrl(options) {
|
|
|
117
188
|
["autoJoin" /* AUTO_JOIN */, toFlag(options.autoJoin)],
|
|
118
189
|
["hideLeave" /* HIDE_LEAVE */, toFlag(options.hideLeave)],
|
|
119
190
|
["autoCameraOff" /* AUTO_CAMERA_OFF */, toFlag(options.autoCameraOff)],
|
|
191
|
+
["disableCamera" /* DISABLE_CAMERA */, toFlag(options.disableCamera)],
|
|
120
192
|
["autoTranscribe" /* AUTO_TRANSCRIBE */, toFlag(options.autoTranscribe)],
|
|
121
|
-
["autoRecord" /* AUTO_RECORD */, toFlag(options.autoRecord)]
|
|
193
|
+
["autoRecord" /* AUTO_RECORD */, toFlag(options.autoRecord)],
|
|
194
|
+
["background" /* BACKGROUND */, backgroundParam]
|
|
122
195
|
];
|
|
123
196
|
for (const [key, value] of entries) {
|
|
124
197
|
if (value !== void 0 && value !== "") {
|
|
@@ -137,31 +210,6 @@ import {
|
|
|
137
210
|
useRef
|
|
138
211
|
} from "react";
|
|
139
212
|
|
|
140
|
-
// src/openStage.ts
|
|
141
|
-
function isValidStageUrl(url) {
|
|
142
|
-
if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;
|
|
143
|
-
try {
|
|
144
|
-
const parsed = new URL(url);
|
|
145
|
-
if (parsed.protocol === "https:") return true;
|
|
146
|
-
if (parsed.protocol === "http:") {
|
|
147
|
-
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
148
|
-
}
|
|
149
|
-
return false;
|
|
150
|
-
} catch {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
function normalizeOpenStageInput(input) {
|
|
155
|
-
const url = input.url.trim();
|
|
156
|
-
if (!isValidStageUrl(url)) return null;
|
|
157
|
-
const title = input.title?.trim();
|
|
158
|
-
return {
|
|
159
|
-
url,
|
|
160
|
-
...title ? { title: title.slice(0, 200) } : {},
|
|
161
|
-
...isStageToolId(input.tool) ? { tool: input.tool } : {}
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
213
|
// src/parseEmbedEvent.ts
|
|
166
214
|
var EVENT_TYPES = new Set(Object.values(EmbedEventType));
|
|
167
215
|
function isRecord(value) {
|
|
@@ -230,6 +278,7 @@ function postCommand(options) {
|
|
|
230
278
|
function useOeMeeting(options) {
|
|
231
279
|
const iframeRef = useRef(null);
|
|
232
280
|
const callbacksRef = useRef(options);
|
|
281
|
+
const pendingBackgroundRef = useRef(null);
|
|
233
282
|
useEffect(() => {
|
|
234
283
|
callbacksRef.current = options;
|
|
235
284
|
}, [options]);
|
|
@@ -246,6 +295,15 @@ function useOeMeeting(options) {
|
|
|
246
295
|
switch (embedEvent.type) {
|
|
247
296
|
case "oemeeting:ready" /* READY */:
|
|
248
297
|
callbacks.onReady?.(embedEvent);
|
|
298
|
+
if (pendingBackgroundRef.current) {
|
|
299
|
+
postCommand({
|
|
300
|
+
iframe: iframeRef.current,
|
|
301
|
+
instanceId: options.instanceId,
|
|
302
|
+
action: "setBackground" /* SET_BACKGROUND */,
|
|
303
|
+
targetOrigin: options.targetOrigin,
|
|
304
|
+
payload: pendingBackgroundRef.current
|
|
305
|
+
});
|
|
306
|
+
}
|
|
249
307
|
break;
|
|
250
308
|
case "oemeeting:joined" /* JOINED */:
|
|
251
309
|
callbacks.onJoined?.(embedEvent);
|
|
@@ -313,6 +371,16 @@ function useOeMeeting(options) {
|
|
|
313
371
|
() => send("closeStage" /* CLOSE_STAGE */),
|
|
314
372
|
[send]
|
|
315
373
|
);
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
if (!options.background) {
|
|
376
|
+
pendingBackgroundRef.current = null;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const normalized = normalizeEmbedBackground(options.background);
|
|
380
|
+
if (!normalized) return;
|
|
381
|
+
pendingBackgroundRef.current = normalized;
|
|
382
|
+
send("setBackground" /* SET_BACKGROUND */, normalized);
|
|
383
|
+
}, [options.background, send]);
|
|
316
384
|
const getIframe = useCallback(() => iframeRef.current, []);
|
|
317
385
|
const handle = useMemo(
|
|
318
386
|
() => ({
|
|
@@ -351,8 +419,10 @@ var OeMeeting = forwardRef(
|
|
|
351
419
|
autoJoin = true,
|
|
352
420
|
hideLeave,
|
|
353
421
|
autoCameraOff,
|
|
422
|
+
disableCamera,
|
|
354
423
|
autoTranscribe,
|
|
355
424
|
autoRecord,
|
|
425
|
+
background,
|
|
356
426
|
title = "OE Meeting room",
|
|
357
427
|
width = "100%",
|
|
358
428
|
height = 640,
|
|
@@ -383,6 +453,7 @@ var OeMeeting = forwardRef(
|
|
|
383
453
|
const embedOrigin = useMemo2(() => getOrigin(origin), [origin]);
|
|
384
454
|
const resolvedTargetOrigin = targetOrigin ?? embedOrigin;
|
|
385
455
|
const hostOrigin = typeof window === "undefined" ? void 0 : window.location.origin;
|
|
456
|
+
const initialBackgroundRef = useRef2(background);
|
|
386
457
|
const src = useMemo2(
|
|
387
458
|
() => buildEmbedUrl({
|
|
388
459
|
roomSlug,
|
|
@@ -394,8 +465,10 @@ var OeMeeting = forwardRef(
|
|
|
394
465
|
autoJoin,
|
|
395
466
|
hideLeave,
|
|
396
467
|
autoCameraOff,
|
|
468
|
+
disableCamera,
|
|
397
469
|
autoTranscribe,
|
|
398
470
|
autoRecord,
|
|
471
|
+
background: initialBackgroundRef.current,
|
|
399
472
|
instanceId,
|
|
400
473
|
hostOrigin
|
|
401
474
|
}),
|
|
@@ -412,10 +485,12 @@ var OeMeeting = forwardRef(
|
|
|
412
485
|
origin,
|
|
413
486
|
roomSlug,
|
|
414
487
|
autoCameraOff,
|
|
488
|
+
disableCamera,
|
|
415
489
|
userName
|
|
416
490
|
]
|
|
417
491
|
);
|
|
418
492
|
const { iframeRef } = useOeMeeting({
|
|
493
|
+
background,
|
|
419
494
|
instanceId,
|
|
420
495
|
targetOrigin: resolvedTargetOrigin,
|
|
421
496
|
handleRef: ref,
|
|
@@ -473,6 +548,7 @@ export {
|
|
|
473
548
|
DEFAULT_IFRAME_ALLOW,
|
|
474
549
|
EMBED_MESSAGE_SOURCE,
|
|
475
550
|
EMBED_PROTOCOL_VERSION,
|
|
551
|
+
EmbedBackgroundType,
|
|
476
552
|
EmbedCapability,
|
|
477
553
|
EmbedCaptureOutcome,
|
|
478
554
|
EmbedCommandAction,
|
|
@@ -481,14 +557,17 @@ export {
|
|
|
481
557
|
EmbedEventType,
|
|
482
558
|
EmbedQueryParam,
|
|
483
559
|
EmbedRole,
|
|
560
|
+
MAX_BACKGROUND_URL_LENGTH,
|
|
484
561
|
MAX_STAGE_TOOL_ID_LENGTH,
|
|
485
562
|
MAX_STAGE_URL_LENGTH,
|
|
486
563
|
OeMeeting,
|
|
487
564
|
buildEmbedUrl,
|
|
488
565
|
isStageToolId,
|
|
489
566
|
isValidStageUrl,
|
|
567
|
+
normalizeEmbedBackground,
|
|
490
568
|
normalizeOpenStageInput,
|
|
491
569
|
parseEmbedEvent,
|
|
570
|
+
serializeEmbedBackground,
|
|
492
571
|
useOeMeeting
|
|
493
572
|
};
|
|
494
573
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/OeMeeting.tsx","../src/embed-protocol.ts","../src/buildEmbedUrl.ts","../src/useOeMeeting.ts","../src/openStage.ts","../src/parseEmbedEvent.ts"],"sourcesContent":["'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 autoCameraOff,\n autoTranscribe,\n autoRecord,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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 autoCameraOff,\n autoTranscribe,\n autoRecord,\n instanceId,\n hostOrigin,\n }),\n [\n autoJoin,\n autoRecord,\n autoTranscribe,\n externalUserId,\n hideLeave,\n hostOrigin,\n instanceId,\n joinToken,\n lang,\n origin,\n roomSlug,\n autoCameraOff,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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.ocean.edu.vn';\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 STAGE_CHANGED = 'oemeeting:stage-changed',\n TRANSCRIPT_STARTED = 'oemeeting:transcript-started',\n RECORDING_STARTED = 'oemeeting:recording-started',\n RECORDING_SAVED = 'oemeeting:recording-saved',\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 OPEN_STAGE = 'openStage',\n CLOSE_STAGE = 'closeStage',\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 STAGE_ORIGIN_DENIED = 'STAGE_ORIGIN_DENIED',\n STAGE_URL_INVALID = 'STAGE_URL_INVALID',\n TRANSCRIPT_START_FAILED = 'TRANSCRIPT_START_FAILED',\n RECORDING_START_FAILED = 'RECORDING_START_FAILED',\n UNKNOWN = 'UNKNOWN',\n}\n\n/** Open host-owned tool id. Meeting does not catalog values. */\nexport type StageToolId = string;\n\nexport const MAX_STAGE_URL_LENGTH = 2048;\nexport const MAX_STAGE_TOOL_ID_LENGTH = 32;\n\nconst STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;\n\nexport function isStageToolId(value: unknown): value is StageToolId {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_STAGE_TOOL_ID_LENGTH &&\n STAGE_TOOL_ID_PATTERN.test(value)\n );\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 AUTO_CAMERA_OFF = 'autoCameraOff',\n AUTO_TRANSCRIBE = 'autoTranscribe',\n AUTO_RECORD = 'autoRecord',\n}\n\n/**\n * What happened to the auto-transcribe request the embed made.\n *\n * `ALREADY_RUNNING` is the normal outcome for every participant after the\n * first, because the transcript is a room-level asset rather than a\n * per-participant one. `DISABLED` means the OE Meeting deployment has\n * transcription turned off.\n */\nexport enum EmbedCaptureOutcome {\n STARTED = 'started',\n ALREADY_RUNNING = 'already_running',\n DISABLED = 'disabled',\n FAILED = 'failed',\n}\n\n/**\n * Role the host app assigns when minting a join token.\n * Keep values aligned with `EmbedRole` in the backend embed-permission enum.\n */\nexport enum EmbedRole {\n OWNER = 'owner',\n MEMBER = 'member',\n}\n\n/**\n * Capability the host app may grant per role. Meeting enforces this on the\n * join token, not on a React prop. `screenShare` is the only value today.\n */\nexport enum EmbedCapability {\n SCREEN_SHARE = 'screenShare',\n}\n\nexport interface EmbedPermissionMap {\n owner?: EmbedCapability[];\n member?: EmbedCapability[];\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 [EmbedQueryParam.AUTO_CAMERA_OFF, toFlag(options.autoCameraOff)],\n [EmbedQueryParam.AUTO_TRANSCRIBE, toFlag(options.autoTranscribe)],\n [EmbedQueryParam.AUTO_RECORD, toFlag(options.autoRecord)],\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 { normalizeOpenStageInput } from './openStage';\nimport { parseEmbedEvent } from './parseEmbedEvent';\nimport type {\n EmbedCommandMessage,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\n} from './types';\n\ntype UseOeMeetingOptions = Pick<\n OeMeetingProps,\n | 'onReady'\n | 'onJoined'\n | 'onLeft'\n | 'onParticipantJoined'\n | 'onParticipantLeft'\n | 'onEnded'\n | 'onError'\n | 'onStageChanged'\n | 'onTranscriptStarted'\n | 'onRecordingStarted'\n | 'onRecordingSaved'\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 payload?: OpenStageInput;\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 ...(options.payload ? { payload: options.payload } : {}),\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 case EmbedEventType.STAGE_CHANGED:\n callbacks.onStageChanged?.(embedEvent);\n break;\n case EmbedEventType.TRANSCRIPT_STARTED:\n callbacks.onTranscriptStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_STARTED:\n callbacks.onRecordingStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_SAVED:\n callbacks.onRecordingSaved?.(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, payload?: OpenStageInput) =>\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action,\n targetOrigin: options.targetOrigin,\n payload,\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 openStage = useCallback(\n (input: OpenStageInput) => {\n const normalized = normalizeOpenStageInput(input);\n if (!normalized) return;\n send(EmbedCommandAction.OPEN_STAGE, normalized);\n },\n [send],\n );\n const closeStage = useCallback(\n () => send(EmbedCommandAction.CLOSE_STAGE),\n [send],\n );\n const getIframe = useCallback(() => iframeRef.current, []);\n const handle = useMemo<OeMeetingHandle>(\n () => ({\n leave,\n toggleMic,\n toggleCamera,\n openStage,\n closeStage,\n getIframe,\n }),\n [closeStage, getIframe, leave, openStage, toggleCamera, toggleMic],\n );\n\n useImperativeHandle(options.handleRef, () => handle, [handle]);\n return { iframeRef, handle };\n}\n","import { isStageToolId, MAX_STAGE_URL_LENGTH } from './embed-protocol';\nimport type { OpenStageInput } from './types';\n\nexport function isValidStageUrl(url: string): boolean {\n if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;\n try {\n const parsed = new URL(url);\n if (parsed.protocol === 'https:') return true;\n if (parsed.protocol === 'http:') {\n return (\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1'\n );\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function normalizeOpenStageInput(\n input: OpenStageInput,\n): OpenStageInput | null {\n const url = input.url.trim();\n if (!isValidStageUrl(url)) return null;\n const title = input.title?.trim();\n return {\n url,\n ...(title ? { title: title.slice(0, 200) } : {}),\n ...(isStageToolId(input.tool) ? { tool: input.tool } : {}),\n };\n}\n","import {\n EMBED_MESSAGE_SOURCE,\n EMBED_PROTOCOL_VERSION,\n EmbedCaptureOutcome,\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 isNullableString(value: unknown): boolean {\n return value === null || typeof value === 'string';\n}\n\nconst CAPTURE_OUTCOMES = new Set<string>(Object.values(EmbedCaptureOutcome));\n\nfunction isCaptureOutcome(value: unknown): boolean {\n return typeof value === 'string' && CAPTURE_OUTCOMES.has(value);\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 case EmbedEventType.STAGE_CHANGED:\n return (\n (payload.url === null || typeof payload.url === 'string') &&\n isOptionalString(payload.title) &&\n isOptionalString(payload.tool) &&\n isOptionalString(payload.openedBy)\n );\n case EmbedEventType.TRANSCRIPT_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.transcriptId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.recordingId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_SAVED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.savedAt === '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":["useMemo","EmbedEventType","EmbedCommandType","EmbedCommandAction","EmbedErrorCode","EmbedQueryParam","EmbedCaptureOutcome","EmbedRole","EmbedCapability","OeMeeting","useMemo"]}
|
|
1
|
+
{"version":3,"sources":["../src/OeMeeting.tsx","../src/embed-protocol.ts","../src/openStage.ts","../src/background.ts","../src/buildEmbedUrl.ts","../src/useOeMeeting.ts","../src/parseEmbedEvent.ts"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useId,\n useMemo,\n useRef,\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 autoCameraOff,\n disableCamera,\n autoTranscribe,\n autoRecord,\n background,\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 onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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 // Only the mount-time background rides the embed URL. Later prop changes\n // travel as `setBackground` commands (useOeMeeting); rebuilding src would\n // reload the whole meeting mid-session.\n const initialBackgroundRef = useRef(background);\n const src = useMemo(\n () =>\n buildEmbedUrl({\n roomSlug,\n joinToken,\n userName,\n externalUserId,\n origin,\n lang,\n autoJoin,\n hideLeave,\n autoCameraOff,\n disableCamera,\n autoTranscribe,\n autoRecord,\n background: initialBackgroundRef.current,\n instanceId,\n hostOrigin,\n }),\n [\n autoJoin,\n autoRecord,\n autoTranscribe,\n externalUserId,\n hideLeave,\n hostOrigin,\n instanceId,\n joinToken,\n lang,\n origin,\n roomSlug,\n autoCameraOff,\n disableCamera,\n userName,\n ],\n );\n const { iframeRef } = useOeMeeting({\n background,\n instanceId,\n targetOrigin: resolvedTargetOrigin,\n handleRef: ref,\n onReady,\n onJoined,\n onLeft,\n onParticipantJoined,\n onParticipantLeft,\n onEnded,\n onError,\n onStageChanged,\n onTranscriptStarted,\n onRecordingStarted,\n onRecordingSaved,\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.ocean.edu.vn';\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 STAGE_CHANGED = 'oemeeting:stage-changed',\n TRANSCRIPT_STARTED = 'oemeeting:transcript-started',\n RECORDING_STARTED = 'oemeeting:recording-started',\n RECORDING_SAVED = 'oemeeting:recording-saved',\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 OPEN_STAGE = 'openStage',\n CLOSE_STAGE = 'closeStage',\n SET_BACKGROUND = 'setBackground',\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 STAGE_ORIGIN_DENIED = 'STAGE_ORIGIN_DENIED',\n STAGE_URL_INVALID = 'STAGE_URL_INVALID',\n TRANSCRIPT_START_FAILED = 'TRANSCRIPT_START_FAILED',\n RECORDING_START_FAILED = 'RECORDING_START_FAILED',\n UNKNOWN = 'UNKNOWN',\n}\n\n/**\n * Host-controlled camera background applied to this embed's local user only.\n * `none` clears any active background effect. The host app enforces who may\n * set it; the meeting does not interpret roles for this value.\n */\nexport enum EmbedBackgroundType {\n BLUR = 'blur',\n IMAGE = 'image',\n NONE = 'none',\n}\n\nexport const MAX_BACKGROUND_URL_LENGTH = 2048;\nexport const DEFAULT_BACKGROUND_BLUR_RADIUS = 10;\nexport const MIN_BACKGROUND_BLUR_RADIUS = 1;\nexport const MAX_BACKGROUND_BLUR_RADIUS = 50;\n\nexport interface EmbedBackground {\n type: EmbedBackgroundType;\n /** BLUR only. Defaults to `DEFAULT_BACKGROUND_BLUR_RADIUS`, clamped 1-50. */\n blurRadius?: number;\n /** IMAGE only. http(s) URL, max `MAX_BACKGROUND_URL_LENGTH`. */\n url?: string;\n /**\n * @deprecated Ignored by the meeting: a BLUR/IMAGE background always locks\n * the embed user's background and hides the Effects panel; NONE unlocks.\n */\n locked?: boolean;\n}\n\n/** Open host-owned tool id. Meeting does not catalog values. */\nexport type StageToolId = string;\n\nexport const MAX_STAGE_URL_LENGTH = 2048;\nexport const MAX_STAGE_TOOL_ID_LENGTH = 32;\n\nconst STAGE_TOOL_ID_PATTERN = /^[a-z][a-z0-9._-]{0,31}$/;\n\nexport function isStageToolId(value: unknown): value is StageToolId {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= MAX_STAGE_TOOL_ID_LENGTH &&\n STAGE_TOOL_ID_PATTERN.test(value)\n );\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 AUTO_CAMERA_OFF = 'autoCameraOff',\n DISABLE_CAMERA = 'disableCamera',\n AUTO_TRANSCRIBE = 'autoTranscribe',\n AUTO_RECORD = 'autoRecord',\n BACKGROUND = 'background',\n}\n\n/**\n * What happened to the auto-transcribe request the embed made.\n *\n * `ALREADY_RUNNING` is the normal outcome for every participant after the\n * first, because the transcript is a room-level asset rather than a\n * per-participant one. `DISABLED` means the OE Meeting deployment has\n * transcription turned off.\n */\nexport enum EmbedCaptureOutcome {\n STARTED = 'started',\n ALREADY_RUNNING = 'already_running',\n DISABLED = 'disabled',\n FAILED = 'failed',\n}\n\n/**\n * Role the host app assigns when minting a join token.\n * Keep values aligned with `EmbedRole` in the backend embed-permission enum.\n */\nexport enum EmbedRole {\n OWNER = 'owner',\n MEMBER = 'member',\n}\n\n/**\n * Capability the host app may grant per role. Meeting enforces this on the\n * join token, not on a React prop. `screenShare` is the only value today.\n */\nexport enum EmbedCapability {\n SCREEN_SHARE = 'screenShare',\n}\n\nexport interface EmbedPermissionMap {\n owner?: EmbedCapability[];\n member?: EmbedCapability[];\n}\n","import { isStageToolId, MAX_STAGE_URL_LENGTH } from './embed-protocol';\nimport type { OpenStageInput } from './types';\n\nexport function isValidStageUrl(url: string): boolean {\n if (url.length === 0 || url.length > MAX_STAGE_URL_LENGTH) return false;\n try {\n const parsed = new URL(url);\n if (parsed.protocol === 'https:') return true;\n if (parsed.protocol === 'http:') {\n return (\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1'\n );\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function normalizeOpenStageInput(\n input: OpenStageInput,\n): OpenStageInput | null {\n const url = input.url.trim();\n if (!isValidStageUrl(url)) return null;\n const title = input.title?.trim();\n return {\n url,\n ...(title ? { title: title.slice(0, 200) } : {}),\n ...(isStageToolId(input.tool) ? { tool: input.tool } : {}),\n };\n}\n","import { isValidStageUrl } from './openStage';\nimport {\n DEFAULT_BACKGROUND_BLUR_RADIUS,\n EmbedBackgroundType,\n MAX_BACKGROUND_BLUR_RADIUS,\n MAX_BACKGROUND_URL_LENGTH,\n MIN_BACKGROUND_BLUR_RADIUS,\n type EmbedBackground,\n} from './embed-protocol';\n\n/** Same http(s) policy as the stage URL, including the localhost exception. */\nconst isValidBackgroundUrl = (url: string): boolean =>\n url.length > 0 &&\n url.length <= MAX_BACKGROUND_URL_LENGTH &&\n isValidStageUrl(url);\n\nconst clampBlurRadius = (value: number): number =>\n Number.isFinite(value)\n ? Math.min(MAX_BACKGROUND_BLUR_RADIUS, Math.max(MIN_BACKGROUND_BLUR_RADIUS, Math.round(value)))\n : DEFAULT_BACKGROUND_BLUR_RADIUS;\n\n/**\n * Validate and normalize a host-provided background. Returns null for invalid\n * input so callers can silently drop it, mirroring `normalizeOpenStageInput`.\n */\nexport function normalizeEmbedBackground(\n input: EmbedBackground,\n): EmbedBackground | null {\n const locked = input.locked === true ? { locked: true } : {};\n\n if (input.type === EmbedBackgroundType.NONE) {\n return { type: EmbedBackgroundType.NONE, ...locked };\n }\n\n if (input.type === EmbedBackgroundType.BLUR) {\n return {\n type: EmbedBackgroundType.BLUR,\n blurRadius: clampBlurRadius(input.blurRadius ?? DEFAULT_BACKGROUND_BLUR_RADIUS),\n ...locked,\n };\n }\n\n if (input.type === EmbedBackgroundType.IMAGE) {\n const url = input.url?.trim();\n if (!url || !isValidBackgroundUrl(url)) return null;\n return {\n type: EmbedBackgroundType.IMAGE,\n url,\n ...locked,\n };\n }\n\n return null;\n}\n\n/** JSON wire format shared by the `background` query param and the command payload. */\nexport function serializeEmbedBackground(\n input: EmbedBackground,\n): string | undefined {\n const normalized = normalizeEmbedBackground(input);\n return normalized ? JSON.stringify(normalized) : undefined;\n}\n","import {\n DEFAULT_EMBED_ORIGIN,\n EmbedQueryParam,\n} from './embed-protocol';\nimport { serializeEmbedBackground } from './background';\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 backgroundParam = options.background\n ? serializeEmbedBackground(options.background)\n : undefined;\n\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 [EmbedQueryParam.AUTO_CAMERA_OFF, toFlag(options.autoCameraOff)],\n [EmbedQueryParam.DISABLE_CAMERA, toFlag(options.disableCamera)],\n [EmbedQueryParam.AUTO_TRANSCRIBE, toFlag(options.autoTranscribe)],\n [EmbedQueryParam.AUTO_RECORD, toFlag(options.autoRecord)],\n [EmbedQueryParam.BACKGROUND, backgroundParam],\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 { normalizeEmbedBackground } from './background';\nimport { normalizeOpenStageInput } from './openStage';\nimport { parseEmbedEvent } from './parseEmbedEvent';\nimport type {\n EmbedCommandMessage,\n OeMeetingHandle,\n OeMeetingProps,\n OpenStageInput,\n} from './types';\nimport type { EmbedBackground } from './embed-protocol';\n\ntype UseOeMeetingOptions = Pick<\n OeMeetingProps,\n | 'onReady'\n | 'onJoined'\n | 'onLeft'\n | 'onParticipantJoined'\n | 'onParticipantLeft'\n | 'onEnded'\n | 'onError'\n | 'onStageChanged'\n | 'onTranscriptStarted'\n | 'onRecordingStarted'\n | 'onRecordingSaved'\n | 'onEvent'\n | 'background'\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 payload?: OpenStageInput | EmbedBackground;\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 ...(options.payload ? { payload: options.payload } : {}),\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 // Last background the host set. Resent on READY: commands posted before the\n // iframe bridge mounts are dropped by the browser.\n const pendingBackgroundRef = useRef<EmbedBackground | null>(null);\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 if (pendingBackgroundRef.current) {\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action: EmbedCommandAction.SET_BACKGROUND,\n targetOrigin: options.targetOrigin,\n payload: pendingBackgroundRef.current,\n });\n }\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 case EmbedEventType.STAGE_CHANGED:\n callbacks.onStageChanged?.(embedEvent);\n break;\n case EmbedEventType.TRANSCRIPT_STARTED:\n callbacks.onTranscriptStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_STARTED:\n callbacks.onRecordingStarted?.(embedEvent);\n break;\n case EmbedEventType.RECORDING_SAVED:\n callbacks.onRecordingSaved?.(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 (\n action: EmbedCommandAction,\n payload?: OpenStageInput | EmbedBackground\n ) =>\n postCommand({\n iframe: iframeRef.current,\n instanceId: options.instanceId,\n action,\n targetOrigin: options.targetOrigin,\n payload,\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 openStage = useCallback(\n (input: OpenStageInput) => {\n const normalized = normalizeOpenStageInput(input);\n if (!normalized) return;\n send(EmbedCommandAction.OPEN_STAGE, normalized);\n },\n [send],\n );\n const closeStage = useCallback(\n () => send(EmbedCommandAction.CLOSE_STAGE),\n [send],\n );\n\n useEffect(() => {\n if (!options.background) {\n pendingBackgroundRef.current = null;\n return;\n }\n const normalized = normalizeEmbedBackground(options.background);\n if (!normalized) return;\n pendingBackgroundRef.current = normalized;\n send(EmbedCommandAction.SET_BACKGROUND, normalized);\n }, [options.background, send]);\n const getIframe = useCallback(() => iframeRef.current, []);\n const handle = useMemo<OeMeetingHandle>(\n () => ({\n leave,\n toggleMic,\n toggleCamera,\n openStage,\n closeStage,\n getIframe,\n }),\n [closeStage, getIframe, leave, openStage, 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 EmbedCaptureOutcome,\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 isNullableString(value: unknown): boolean {\n return value === null || typeof value === 'string';\n}\n\nconst CAPTURE_OUTCOMES = new Set<string>(Object.values(EmbedCaptureOutcome));\n\nfunction isCaptureOutcome(value: unknown): boolean {\n return typeof value === 'string' && CAPTURE_OUTCOMES.has(value);\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 case EmbedEventType.STAGE_CHANGED:\n return (\n (payload.url === null || typeof payload.url === 'string') &&\n isOptionalString(payload.title) &&\n isOptionalString(payload.tool) &&\n isOptionalString(payload.openedBy)\n );\n case EmbedEventType.TRANSCRIPT_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.transcriptId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_STARTED:\n return (\n typeof payload.roomSlug === 'string' &&\n isCaptureOutcome(payload.outcome) &&\n isNullableString(payload.recordingId) &&\n isNullableString(payload.status)\n );\n case EmbedEventType.RECORDING_SAVED:\n return (\n typeof payload.roomSlug === 'string' &&\n typeof payload.savedAt === '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":["useMemo","useRef","EmbedEventType","EmbedCommandType","EmbedCommandAction","EmbedErrorCode","EmbedBackgroundType","EmbedQueryParam","EmbedCaptureOutcome","EmbedRole","EmbedCapability","OeMeeting","useMemo","useRef"]}
|
|
@@ -53,7 +53,9 @@ Request:
|
|
|
53
53
|
"name": "Lesson 98765 - 2026-08-17",
|
|
54
54
|
"slug": "oe-school-lesson-98765",
|
|
55
55
|
"access_level": "restricted",
|
|
56
|
-
"configuration": {
|
|
56
|
+
"configuration": {
|
|
57
|
+
"speakerDiarization": true
|
|
58
|
+
}
|
|
57
59
|
}
|
|
58
60
|
```
|
|
59
61
|
|
|
@@ -62,8 +64,7 @@ Request:
|
|
|
62
64
|
| `name` | Có | Tên hiển thị, tối đa 500 ký tự. |
|
|
63
65
|
| `slug` | Không | Khóa ổn định của phòng, tối đa 100 ký tự. Nếu bỏ trống, server sinh từ `name`. |
|
|
64
66
|
| `access_level` | Không | Nên luôn dùng `restricted`; external API mặc định là `restricted`. |
|
|
65
|
-
| `configuration` | Không | Cấu hình mở rộng
|
|
66
|
-
|
|
67
|
+
| `configuration` | Không | Cấu hình mở rộng. `speakerDiarization: true` bảo agent tách người nói. Mặc định tắt. Gửi trên lần tạo phòng, hoặc lần tạo lại cùng slug. `background` và `backgroundExemptions` sai định dạng trả 400. |
|
|
67
68
|
Response mẫu:
|
|
68
69
|
|
|
69
70
|
```json
|
|
@@ -211,7 +212,7 @@ Response mẫu:
|
|
|
211
212
|
| Field | Mô tả |
|
|
212
213
|
|---|---|
|
|
213
214
|
| `status` | `active` khi buổi học đang diễn ra, `paused` khi tạm dừng, `completed` sau khi phòng kết thúc. |
|
|
214
|
-
| `segments` | Các câu nói đã chốt, sắp xếp tăng dần theo `spokenAt`. |
|
|
215
|
+
| `segments` | Các câu nói đã chốt, sắp xếp tăng dần theo `spokenAt`. Client chưa bật `speakerDiarization` nhận đúng shape cũ, không có `speakerId`. |
|
|
215
216
|
| `startedBy` | `null` với phiên do embed tự bật (người tham gia là khách của OE.School, không có tài khoản OE Meeting). |
|
|
216
217
|
|
|
217
218
|
Trả `404` khi phòng chưa từng có transcript nào. Transcript là tài sản cấp
|
|
@@ -219,6 +220,38 @@ phòng: nếu OE.School dùng lại một room cho nhiều buổi, endpoint tr
|
|
|
219
220
|
nhất và các segment của phiên đó. Dùng một room cho mỗi buổi học (theo quy ước
|
|
220
221
|
slug ở 2.1) để tránh nhập nhằng.
|
|
221
222
|
|
|
223
|
+
Với phòng đã tạo với `speakerDiarization: true`, mỗi segment có thêm field
|
|
224
|
+
`speakerId` — nhãn giọng nói Soniox gán trong phạm vi một audio track (mic
|
|
225
|
+
chung, tab share). Hai giọng trên một mic sẽ ra `"1"`, `"2"` xen kẽ:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
"segments": [
|
|
229
|
+
{
|
|
230
|
+
"id": "0b0f9d54-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
|
|
231
|
+
"participantIdentity": "oe-school:user:12345",
|
|
232
|
+
"participantName": "Nguyen Van A",
|
|
233
|
+
"speakerId": "1",
|
|
234
|
+
"text": "Chào cả lớp, hôm nay chúng ta học bài 3.",
|
|
235
|
+
"language": "vi",
|
|
236
|
+
"spokenAt": "2026-08-17T02:00:45.000Z"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "1c2d3e4f-...",
|
|
240
|
+
"participantIdentity": "oe-school:user:12345",
|
|
241
|
+
"participantName": "Nguyen Van A",
|
|
242
|
+
"speakerId": "2",
|
|
243
|
+
"text": "Khoan, cho em hỏi bài 3 ạ.",
|
|
244
|
+
"language": "vi",
|
|
245
|
+
"spokenAt": "2026-08-17T02:00:52.000Z"
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`speakerId` là nhãn giọng, không phải tên người: mọi segment vẫn mang
|
|
251
|
+
`participantName` của chủ track. Muốn tên thật cho từng giọng phải tự map
|
|
252
|
+
ngoài hệ thống. Nhãn chỉ phân biệt trong phạm vi một track, không phải toàn
|
|
253
|
+
phòng, và không có trên live caption trong lúc họp.
|
|
254
|
+
|
|
222
255
|
Gọi endpoint này sau event `ENDED` (`onEnded` trên React SDK). Agent transcriber
|
|
223
256
|
chốt phiên (`status: "completed"`) khi phòng LiveKit đóng, nên gọi quá sớm sẽ
|
|
224
257
|
nhận được transcript còn `active` và thiếu các câu cuối. Poll tới khi
|
|
@@ -247,6 +280,10 @@ Gọi endpoint này từ backend OE.School khi nhận `ENDED`. Egress có thể
|
|
|
247
280
|
| `PATCH` | `/external-api/v1.0/rooms/:idOrSlug` | `rooms:update` |
|
|
248
281
|
| `DELETE` | `/external-api/v1.0/rooms/:idOrSlug` | `rooms:delete` |
|
|
249
282
|
|
|
283
|
+
`PATCH` merge `configuration` vào cấu hình đã lưu: key không gửi thì giữ
|
|
284
|
+
nguyên, key gửi `null` thì bị xóa. Ví dụ `{ "configuration": { "background": null } }`
|
|
285
|
+
chỉ xóa nền phòng, không đụng `speakerDiarization`.
|
|
286
|
+
|
|
250
287
|
## 3. Service mẫu ở backend OE.School
|
|
251
288
|
|
|
252
289
|
Ví dụ dưới đây dùng `fetch` tích hợp sẵn trong Node.js 18+.
|
|
@@ -271,6 +308,7 @@ interface TranscriptSegment {
|
|
|
271
308
|
id: string;
|
|
272
309
|
participantIdentity: string | null;
|
|
273
310
|
participantName: string;
|
|
311
|
+
speakerId?: string;
|
|
274
312
|
text: string;
|
|
275
313
|
language: string | null;
|
|
276
314
|
spokenAt: string;
|
|
@@ -128,8 +128,10 @@ này ở production. Khi chạy local, truyền URL frontend OE Meeting local, v
|
|
|
128
128
|
| `autoJoin` | `boolean` | `true` | `false` hiển thị màn preview (prejoin) trong iframe: kiểm tra camera/mic, chọn thiết bị rồi bấm vào mới vào phòng. Khi bật prejoin, `onReady`/`onJoined` chỉ kích hoạt sau khi người dùng vào phòng. |
|
|
129
129
|
| `hideLeave` | `boolean` | `false` | Ẩn nút rời phòng trong iframe. |
|
|
130
130
|
| `autoCameraOff` | `boolean` | `false` | Vào phòng với camera tắt: màn prejoin khởi tạo camera đang tắt và phòng không phát video lúc kết nối. Người dùng vẫn có thể bật lại thủ công. |
|
|
131
|
+
| `disableCamera` | `boolean` | `false` | Giữ camera tắt suốt phiên embed. Nút camera ở prejoin và trong phòng, phím tắt, và `toggleCamera()` đều không bật lại được. |
|
|
131
132
|
| `autoTranscribe` | `boolean` | `false` | Tự bật transcript dùng chung khi embed kết nối. |
|
|
132
133
|
| `autoRecord` | `boolean` | `false` | Tự ghi toàn bộ phòng khi embed kết nối. |
|
|
134
|
+
| `background` | `EmbedBackground` | — | Áp background camera (blur / ảnh / none) cho user của iframe này, kèm tuỳ chọn khóa lựa chọn nền của người dùng. Dùng để branding sự kiện: host app truyền cùng một giá trị cho mọi người tham gia. Giá trị ban đầu đi theo embed URL; đổi prop sau mount được gửi vào iframe dưới dạng command `setBackground` (buffer đến khi embed ready). Input không hợp lệ bị bỏ qua. |
|
|
133
135
|
| `userName` | `string` | — | Thông tin tùy chọn truyền vào embed URL. |
|
|
134
136
|
| `externalUserId` | `string` | — | External identity tùy chọn. |
|
|
135
137
|
| `lang` | `string` | — | Ngôn ngữ giao diện. |
|
|
@@ -157,6 +159,45 @@ này ở production. Khi chạy local, truyền URL frontend OE Meeting local, v
|
|
|
157
159
|
Không đặt `targetOrigin="*"`. SDK và iframe đều kiểm tra origin, source và
|
|
158
160
|
`instanceId` trước khi xử lý message.
|
|
159
161
|
|
|
162
|
+
### 3.0.1. Background chung cho sự kiện (`background`)
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { EmbedBackgroundType } from '@tinyweb_dev/oe-meeting-react'
|
|
166
|
+
|
|
167
|
+
// Ảnh branding cho mọi người tham gia, người dùng không đổi được
|
|
168
|
+
<OeMeeting
|
|
169
|
+
roomSlug="event-room"
|
|
170
|
+
joinToken={token}
|
|
171
|
+
background={{
|
|
172
|
+
type: EmbedBackgroundType.IMAGE,
|
|
173
|
+
url: 'https://cdn.example.com/event-brand.jpg',
|
|
174
|
+
}}
|
|
175
|
+
/>
|
|
176
|
+
|
|
177
|
+
// Blur nhẹ, cũng bị khóa
|
|
178
|
+
background={{ type: EmbedBackgroundType.BLUR, blurRadius: 5 }}
|
|
179
|
+
|
|
180
|
+
// Bỏ background đang áp và trả quyền chọn lại cho người dùng
|
|
181
|
+
background={{ type: EmbedBackgroundType.NONE }}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Quy tắc:
|
|
185
|
+
|
|
186
|
+
- `type: 'blur'` | `'image'` | `'none'`. `blurRadius` mặc định 10, clamp 1–50;
|
|
187
|
+
`url` phải là http(s) (http chỉ cho localhost) và ≤ 2048 ký tự.
|
|
188
|
+
- **Ảnh cross-origin tự đi qua proxy của meeting backend** (`GET /api/v1.0/embed/background-image`): host app truyền URL ảnh công khai bất kỳ, **không cần CORS/ACAO** trên host ảnh. Proxy bảo vệ cho production: join token, DNS pin chặn rebinding, redirect validate lại từng hop (tối đa 3), chặn IP private/loopback/link-local (IPv4+IPv6), chỉ `image/*` ≤ 10MB, rate limit theo identity (mặc định 30/phút), cache 10 phút (budget 32MB). Ops: `EMBED_BACKGROUND_PROXY_ENABLED` (kill switch), `EMBED_BACKGROUND_PROXY_ALLOWED_HOSTS` (allowlist hostname, tùy chọn), `EMBED_BACKGROUND_PROXY_RATE_LIMIT`. URL same-origin với meeting tải trực tiếp. URL không tải được bị bỏ qua an toàn (nền không áp, khóa mở, camera vẫn chạy).
|
|
189
|
+
- Truyền `blur`/`image` là khóa luôn: panel Effects và mọi nút mở nó bị ẩn
|
|
190
|
+
(cả prejoin lẫn trong phòng), người dùng không tự đổi background được.
|
|
191
|
+
`none` mở lại. Field `locked` đã deprecated, meeting bỏ qua.
|
|
192
|
+
- Meeting không kiểm tra role: chỉ tác động lên iframe của chính người đó,
|
|
193
|
+
nên host app tự enforce "ai được set" (vd chỉ dashboard của owner).
|
|
194
|
+
- Đồng bộ tới mọi người tham gia là việc host app: hãy render cùng một giá trị
|
|
195
|
+
`background` trên mọi phiên embed và đẩy update qua kênh realtime của bạn
|
|
196
|
+
khi owner đổi giữa buổi. Người vào phòng trực tiếp (không qua embed) không
|
|
197
|
+
nhận được background này.
|
|
198
|
+
- Command `setBackground` gửi trong lúc prejoin (`autoJoin={false}`, chưa bấm
|
|
199
|
+
Join) bị mất — giá trị ban đầu qua URL vẫn đúng.
|
|
200
|
+
|
|
160
201
|
## 3.1. Phân quyền theo role
|
|
161
202
|
|
|
162
203
|
`<OeMeeting>` không có prop quyền. Quyền nằm trong `joinToken`.
|
|
@@ -358,7 +399,7 @@ export function MeetingWithControls(props: {
|
|
|
358
399
|
|
|
359
400
|
- `leave()`.
|
|
360
401
|
- `toggleMic()`.
|
|
361
|
-
- `toggleCamera()`.
|
|
402
|
+
- `toggleCamera()`. Bị bỏ qua khi `disableCamera` đang bật.
|
|
362
403
|
- `openStage({ url, title?, tool? })` — ignore nếu `attributes.stage=follow`.
|
|
363
404
|
- `closeStage()` — opener, `stage=open`, hoặc admin room.
|
|
364
405
|
- `getIframe()`.
|