@wavebirdai/api 0.1.0-beta.6 → 0.1.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -70,7 +70,7 @@ You need a saved Wavebird Test project and placement in Dashboard, a Server Test
70
70
  5. Validate in Test: confirm the saved format and size, consent and GPC behavior, No-Fill, Stop/abort, and late responses. The generation result must continue independently of the ad path. Move to another package version or Live only through your normal project approval process.
71
71
 
72
72
  JavaScript and TypeScript use this same package. Complete Next.js JavaScript and TypeScript examples are included in `node_modules/@wavebirdai/api/examples/next-js` and `node_modules/@wavebirdai/api/examples/next-ts`. For other frameworks, use the explicit `@wavebirdai/api/server` and `@wavebirdai/api/browser` exports described below. The [REST reference](https://wavebird.ai/api/reference/endpoints) covers integrations without this package. The established [`wavebird`](https://www.npmjs.com/package/wavebird) SDK is a separate package.
73
-
73
+
74
74
  ## Server and browser boundary
75
75
 
76
76
  `@wavebirdai/api/server` exports `createPublisherServer`. Configure it with server-only Project ID, Server Test or production Key, preference signing secret, the publisher's real request authorization function, exact `publicOrigins`, and the saved project/placement values shown in the dashboard. The origin list is required. The package compares the browser Origin with a configured public origin whose host matches the route Host, even when Next.js or a proxy gives the route an internal URL. `localhost` and `127.0.0.1` are separate origins, and each port and scheme must match exactly; HTTP loopback is available only with a Server Test Key. A production key also requires its separately approved project and Wavebird server authority. Omit `bidfloor` and `bidfloorCurrency` to inherit saved pricing. An explicit override supplies both a nonnegative amount, including zero, and a three-letter currency; the API validates it against the saved project. It returns authenticated `placementPOST`, `consentGET`, `consentPOST`, and `withGeneration` handlers, plus the server-only `reportGeneration(jobId, event)` method. The helper constructs the existing `/v1/placements`, `/v1/consent`, and `/v1/jobs/{id}/generation/{event}` requests; it does not introduce a new Wavebird protocol. The Wavebird API remains authoritative for saved scope, creative formats, duration, fallback, and financial classification.
package/dist/browser.js CHANGED
@@ -20,8 +20,14 @@ function loadRenderer(url) {
20
20
 
21
21
  export function createPublisherAds(options) {
22
22
  const target = options.target;
23
- if (!target || !options.userId || !options.conversationId) throw new Error("Application container and session are required");
23
+ const requiredText = value => typeof value === "string" && value.trim().length > 0;
24
+ if (!target || !requiredText(options.userId) || !requiredText(options.conversationId))
25
+ throw new Error("Application container and session are required");
24
26
  if (!["chat", "code", "image", "voice", "agent"].includes(options.jobType)) throw new Error("publisher_job_invalid");
27
+ if (typeof options.clientId !== "string" || !options.clientId.startsWith("wbproj_"))
28
+ throw new Error("publisher_project_invalid");
29
+ if (typeof options.surfaceId !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(options.surfaceId))
30
+ throw new Error("publisher_placement_invalid");
25
31
  const turnBinding = options.turnBinding === true;
26
32
  const validId = value => typeof value === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value);
27
33
  if (turnBinding && !validId(options.sessionId)) throw new Error("publisher_trusted_session_required");
@@ -64,7 +70,8 @@ export function createPublisherAds(options) {
64
70
  const prepare = () => { if (!disposed && sponsor) void sponsor.prepare().catch(() => {}); };
65
71
  function setConversation(next) {
66
72
  if (disposed) return;
67
- if (!next.userId || !next.conversationId) throw new Error("Application session is required");
73
+ if (!requiredText(next?.userId) || !requiredText(next?.conversationId))
74
+ throw new Error("Application session is required");
68
75
  if (turnBinding && !validId(next.sessionId)) throw new Error("publisher_trusted_session_required");
69
76
  if (next.userId === binding.userId && next.conversationId === binding.conversationId &&
70
77
  (!turnBinding || next.sessionId === binding.sessionId)) return;
package/dist/server.js CHANGED
@@ -59,8 +59,14 @@ function normalizeOptions(options) {
59
59
  options.blockedCategories.some(value => typeof value !== "string" || !value.trim())))
60
60
  throw new Error("publisher_blocked_categories_invalid");
61
61
  if (!["chat", "code", "image", "voice", "agent"].includes(options.jobType) || typeof options.locale !== "string" || !options.locale) throw new Error("publisher_job_invalid");
62
- const base = String(options.apiBaseUrl || "https://api.wavebird.ai").trim().replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1";
63
- if (!/^https:\/\//.test(base)) throw new Error("publisher_api_https_required");
62
+ const apiBaseUrl = options.apiBaseUrl === undefined ? "https://api.wavebird.ai" : options.apiBaseUrl;
63
+ if (typeof apiBaseUrl !== "string" || !apiBaseUrl.trim() || /[\\?#]/.test(apiBaseUrl)) throw new Error("publisher_api_https_required");
64
+ const endpoint = apiBaseUrl.trim();
65
+ let parsedEndpoint;
66
+ try { parsedEndpoint = new URL(endpoint); } catch { throw new Error("publisher_api_https_required"); }
67
+ if (parsedEndpoint.protocol !== "https:" || !parsedEndpoint.hostname || parsedEndpoint.username || parsedEndpoint.password ||
68
+ parsedEndpoint.search || parsedEndpoint.hash) throw new Error("publisher_api_https_required");
69
+ const base = endpoint.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1";
64
70
  {
65
71
  if (!Array.isArray(options.publicOrigins) || options.publicOrigins.length < 1 || options.publicOrigins.length > 8) throw new Error("publisher_public_origins_invalid");
66
72
  for (const origin of options.publicOrigins) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavebirdai/api",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Integrate Banner, Native, and Clip placements into authenticated chat apps with server-side keys, consent, and generation lifecycle handling",
package/src/browser.js CHANGED
@@ -20,8 +20,14 @@ function loadRenderer(url) {
20
20
 
21
21
  export function createPublisherAds(options) {
22
22
  const target = options.target;
23
- if (!target || !options.userId || !options.conversationId) throw new Error("Application container and session are required");
23
+ const requiredText = value => typeof value === "string" && value.trim().length > 0;
24
+ if (!target || !requiredText(options.userId) || !requiredText(options.conversationId))
25
+ throw new Error("Application container and session are required");
24
26
  if (!["chat", "code", "image", "voice", "agent"].includes(options.jobType)) throw new Error("publisher_job_invalid");
27
+ if (typeof options.clientId !== "string" || !options.clientId.startsWith("wbproj_"))
28
+ throw new Error("publisher_project_invalid");
29
+ if (typeof options.surfaceId !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(options.surfaceId))
30
+ throw new Error("publisher_placement_invalid");
25
31
  const turnBinding = options.turnBinding === true;
26
32
  const validId = value => typeof value === "string" && /^[A-Za-z0-9_-]{1,128}$/.test(value);
27
33
  if (turnBinding && !validId(options.sessionId)) throw new Error("publisher_trusted_session_required");
@@ -64,7 +70,8 @@ export function createPublisherAds(options) {
64
70
  const prepare = () => { if (!disposed && sponsor) void sponsor.prepare().catch(() => {}); };
65
71
  function setConversation(next) {
66
72
  if (disposed) return;
67
- if (!next.userId || !next.conversationId) throw new Error("Application session is required");
73
+ if (!requiredText(next?.userId) || !requiredText(next?.conversationId))
74
+ throw new Error("Application session is required");
68
75
  if (turnBinding && !validId(next.sessionId)) throw new Error("publisher_trusted_session_required");
69
76
  if (next.userId === binding.userId && next.conversationId === binding.conversationId &&
70
77
  (!turnBinding || next.sessionId === binding.sessionId)) return;
package/src/server.js CHANGED
@@ -59,8 +59,14 @@ function normalizeOptions(options) {
59
59
  options.blockedCategories.some(value => typeof value !== "string" || !value.trim())))
60
60
  throw new Error("publisher_blocked_categories_invalid");
61
61
  if (!["chat", "code", "image", "voice", "agent"].includes(options.jobType) || typeof options.locale !== "string" || !options.locale) throw new Error("publisher_job_invalid");
62
- const base = String(options.apiBaseUrl || "https://api.wavebird.ai").trim().replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1";
63
- if (!/^https:\/\//.test(base)) throw new Error("publisher_api_https_required");
62
+ const apiBaseUrl = options.apiBaseUrl === undefined ? "https://api.wavebird.ai" : options.apiBaseUrl;
63
+ if (typeof apiBaseUrl !== "string" || !apiBaseUrl.trim() || /[\\?#]/.test(apiBaseUrl)) throw new Error("publisher_api_https_required");
64
+ const endpoint = apiBaseUrl.trim();
65
+ let parsedEndpoint;
66
+ try { parsedEndpoint = new URL(endpoint); } catch { throw new Error("publisher_api_https_required"); }
67
+ if (parsedEndpoint.protocol !== "https:" || !parsedEndpoint.hostname || parsedEndpoint.username || parsedEndpoint.password ||
68
+ parsedEndpoint.search || parsedEndpoint.hash) throw new Error("publisher_api_https_required");
69
+ const base = endpoint.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1";
64
70
  {
65
71
  if (!Array.isArray(options.publicOrigins) || options.publicOrigins.length < 1 || options.publicOrigins.length > 8) throw new Error("publisher_public_origins_invalid");
66
72
  for (const origin of options.publicOrigins) {