@callembed/sdk 0.1.0 → 0.1.2

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/dist/core/api.js CHANGED
@@ -1,5 +1,16 @@
1
1
  import { CallEmbedError } from "./errors.js";
2
2
  import { assertValidMetadata } from "./metadata.js";
3
+ /** Publishable request headers; includes parent origin for hosted iframe (B109). */
4
+ function publishableHeaders(cfg, extra) {
5
+ const headers = {
6
+ "X-Publishable-Key": cfg.publishableKey,
7
+ ...extra,
8
+ };
9
+ if (cfg.parentOrigin) {
10
+ headers["X-CallEmbed-Parent-Origin"] = cfg.parentOrigin;
11
+ }
12
+ return headers;
13
+ }
3
14
  /**
4
15
  * Create a call via `POST /v1/calls` using the publishable key only.
5
16
  * Response includes `endToken` (memory-only — never persist or log).
@@ -9,10 +20,7 @@ export async function apiCreateCall(cfg, opts) {
9
20
  const base = cfg.apiBaseUrl.replace(/\/$/, "");
10
21
  const res = await fetch(`${base}/v1/calls`, {
11
22
  method: "POST",
12
- headers: {
13
- "Content-Type": "application/json",
14
- "X-Publishable-Key": cfg.publishableKey,
15
- },
23
+ headers: publishableHeaders(cfg, { "Content-Type": "application/json" }),
16
24
  body: JSON.stringify({
17
25
  queue: opts.queue,
18
26
  ...(opts.metadata !== undefined ? { metadata: opts.metadata } : {}),
@@ -92,10 +100,7 @@ const STATUS_MAX_ATTEMPTS = 3;
92
100
  export async function apiEndCall(cfg, callId, endToken) {
93
101
  const url = endCallUrl(cfg, callId);
94
102
  const body = endBody(endToken);
95
- const headers = {
96
- "Content-Type": "application/json",
97
- "X-Publishable-Key": cfg.publishableKey,
98
- };
103
+ const headers = publishableHeaders(cfg, { "Content-Type": "application/json" });
99
104
  for (let attempt = 0; attempt < END_MAX_ATTEMPTS; attempt++) {
100
105
  try {
101
106
  const res = await fetch(url, { method: "POST", headers, body });
@@ -123,10 +128,7 @@ export function apiEndCallKeepalive(cfg, callId, endToken) {
123
128
  try {
124
129
  void fetch(endCallUrl(cfg, callId), {
125
130
  method: "POST",
126
- headers: {
127
- "Content-Type": "application/json",
128
- "X-Publishable-Key": cfg.publishableKey,
129
- },
131
+ headers: publishableHeaders(cfg, { "Content-Type": "application/json" }),
130
132
  body: endBody(endToken),
131
133
  keepalive: true,
132
134
  });
@@ -141,9 +143,7 @@ export function apiEndCallKeepalive(cfg, callId, endToken) {
141
143
  * Retries briefly on 429 (Retry-After + jitter) so ring polls survive key/IP spikes.
142
144
  */
143
145
  export async function apiGetCallStatus(cfg, callId, endToken) {
144
- const headers = {
145
- "X-Publishable-Key": cfg.publishableKey,
146
- };
146
+ const headers = publishableHeaders(cfg);
147
147
  if (endToken)
148
148
  headers["X-Call-End-Token"] = endToken;
149
149
  const url = callStatusUrl(cfg, callId);
@@ -86,6 +86,22 @@ export const CallEmbed = {
86
86
  if (options.apiBaseUrl === undefined || options.apiBaseUrl === null) {
87
87
  throw new CallEmbedError("invalid_config", 'apiBaseUrl is required (use "" for same-origin).');
88
88
  }
89
+ const parentRaw = typeof options.parentOrigin === "string" ? options.parentOrigin.trim() : "";
90
+ let parentOrigin;
91
+ if (parentRaw) {
92
+ try {
93
+ const u = new URL(parentRaw);
94
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
95
+ throw new CallEmbedError("invalid_config", "parentOrigin must be a valid http(s) origin.");
96
+ }
97
+ parentOrigin = u.origin;
98
+ }
99
+ catch (err) {
100
+ if (err instanceof CallEmbedError)
101
+ throw err;
102
+ throw new CallEmbedError("invalid_config", "parentOrigin must be a valid http(s) origin.");
103
+ }
104
+ }
89
105
  runtime = {
90
106
  publishableKey: options.publishableKey.trim(),
91
107
  apiBaseUrl: String(options.apiBaseUrl).trim().replace(/\/$/, ""),
@@ -94,6 +110,7 @@ export const CallEmbed = {
94
110
  copyOverrides: options.copy ?? {},
95
111
  branding: options.branding !== false,
96
112
  debug: options.debug === true,
113
+ ...(parentOrigin ? { parentOrigin } : {}),
97
114
  };
98
115
  },
99
116
  /**
@@ -2,7 +2,7 @@
2
2
  * Error codes and {@link CallEmbedError} for the public SDK surface.
3
3
  */
4
4
  /** Well-known SDK / API error codes (English, machine-readable). */
5
- export type ErrorCode = "not_initialized" | "invalid_config" | "invalid_queue" | "invalid_metadata" | "invalid_display_name" | "invalid_container" | "invalid_selector" | "call_in_progress" | "mic_permission_denied" | "no_microphone" | "connection_failed" | "call_failed" | "no_agent_online" | "no_answer" | "agent_busy" | "call_already_answered" | "invalid_end_token" | "quota_exceeded" | "origin_not_allowed" | "invalid_publishable_key" | "rate_limited" | "livekit_not_configured" | "unknown_queue";
5
+ export type ErrorCode = "not_initialized" | "invalid_config" | "invalid_queue" | "invalid_metadata" | "invalid_display_name" | "invalid_container" | "invalid_selector" | "call_in_progress" | "mic_permission_denied" | "no_microphone" | "connection_failed" | "call_failed" | "no_agent_online" | "no_answer" | "agent_busy" | "call_already_answered" | "invalid_end_token" | "quota_exceeded" | "origin_not_allowed" | "parent_origin_required" | "parent_origin_not_allowed" | "invalid_publishable_key" | "rate_limited" | "livekit_not_configured" | "unknown_queue";
6
6
  /**
7
7
  * Thrown by CallEmbed SDK methods and surfaced via the `error` event.
8
8
  */
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Hosted iframe embed defaults (B135).
3
+ *
4
+ * Production iframe lives on its own origin so callembed-web can keep a global
5
+ * `X-Frame-Options: DENY`. Override the URL when self-hosting the embed page.
6
+ *
7
+ * Legacy `https://app.callembed.sealby.me/embed.html` (and the Render
8
+ * `callembed-web` hostname) remain parseable as iframe `src` values but are
9
+ * blocked by the app site's permanent DENY — prefer the URL below.
10
+ */
11
+ /** Origin that serves the hosted `/embed.html` page. */
12
+ export declare const DEFAULT_HOSTED_EMBED_ORIGIN = "https://callembed-embed.onrender.com";
13
+ /** Default hosted iframe document URL (configurable by overriding iframe `src`). */
14
+ export declare const DEFAULT_HOSTED_EMBED_URL = "https://callembed-embed.onrender.com/embed.html";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Hosted iframe embed defaults (B135).
3
+ *
4
+ * Production iframe lives on its own origin so callembed-web can keep a global
5
+ * `X-Frame-Options: DENY`. Override the URL when self-hosting the embed page.
6
+ *
7
+ * Legacy `https://app.callembed.sealby.me/embed.html` (and the Render
8
+ * `callembed-web` hostname) remain parseable as iframe `src` values but are
9
+ * blocked by the app site's permanent DENY — prefer the URL below.
10
+ */
11
+ /** Origin that serves the hosted `/embed.html` page. */
12
+ export const DEFAULT_HOSTED_EMBED_ORIGIN = "https://callembed-embed.onrender.com";
13
+ /** Default hosted iframe document URL (configurable by overriding iframe `src`). */
14
+ export const DEFAULT_HOSTED_EMBED_URL = `${DEFAULT_HOSTED_EMBED_ORIGIN}/embed.html`;
@@ -43,6 +43,13 @@ export interface InitOptions {
43
43
  * Default: `false`.
44
44
  */
45
45
  debug?: boolean;
46
+ /**
47
+ * Embedding parent page origin for the hosted iframe (`embed.html`).
48
+ * Sent as `X-CallEmbed-Parent-Origin` on publishable API calls. Required by
49
+ * the API when the browser `Origin` is the hosted embed host (B109).
50
+ * Ordinary first-party SDK usage should omit this.
51
+ */
52
+ parentOrigin?: string;
46
53
  }
47
54
  /**
48
55
  * Options for {@link CallEmbed.startCall}.
@@ -172,4 +179,6 @@ export interface RuntimeConfig {
172
179
  copyOverrides: Partial<Record<string, string>>;
173
180
  branding: boolean;
174
181
  debug: boolean;
182
+ /** See {@link InitOptions.parentOrigin}. */
183
+ parentOrigin?: string;
175
184
  }
package/dist/index.d.ts CHANGED
@@ -7,5 +7,6 @@
7
7
  export { CallEmbed } from "./core/client.js";
8
8
  export { CallEmbedError, type ErrorCode } from "./core/errors.js";
9
9
  export { METADATA_MAX_BYTES, assertValidMetadata } from "./core/metadata.js";
10
+ export { DEFAULT_HOSTED_EMBED_ORIGIN, DEFAULT_HOSTED_EMBED_URL, } from "./core/hosted-embed.js";
10
11
  export type { ActiveCall, ApiErrorBody, CallEventHandler, CallEventMap, CallEventName, CallSession, CallStatus, InitOptions, Locale, RuntimeConfig, StartCallEventHandlers, StartCallOptions, } from "./core/types.js";
11
12
  export { CallEmbed as default } from "./core/client.js";
package/dist/index.js CHANGED
@@ -7,4 +7,5 @@
7
7
  export { CallEmbed } from "./core/client.js";
8
8
  export { CallEmbedError } from "./core/errors.js";
9
9
  export { METADATA_MAX_BYTES, assertValidMetadata } from "./core/metadata.js";
10
+ export { DEFAULT_HOSTED_EMBED_ORIGIN, DEFAULT_HOSTED_EMBED_URL, } from "./core/hosted-embed.js";
10
11
  export { CallEmbed as default } from "./core/client.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callembed/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "CallEmbed embeddable web SDK — headless core + optional UI",
5
5
  "license": "MIT",
6
6
  "author": "Sealby",
@@ -42,6 +42,14 @@
42
42
  "pack:verify:workflow": "node ./scripts/pack-verify-workflow-smoke.mjs"
43
43
  },
44
44
  "homepage": "https://callembed.sealby.me",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/TonyFeng1203/CallEmbed.git",
48
+ "directory": "packages/sdk"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/TonyFeng1203/CallEmbed/issues"
52
+ },
45
53
  "keywords": [
46
54
  "callembed",
47
55
  "voice",