@unboundcx/sdk 4.13.82 → 4.13.85

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/base.js CHANGED
@@ -114,6 +114,20 @@ export class BaseSDK {
114
114
  process.env?.API_BASE_URL || defaultDomain
115
115
  }`;
116
116
  }
117
+ } else if (this._constructorBaseURL && !this.namespace) {
118
+ // Forms v2 P3 fix: a browser-environment instance constructed with
119
+ // ONLY a literal baseURL (no namespace) -- e.g. sdk.forms.public /
120
+ // sdk.webchat.visitor on a page that resolves its tenant server-side
121
+ // from an opaque key, never a namespace subdomain -- must hit that
122
+ // exact baseURL. Before this fix, this branch unconditionally built
123
+ // `https://${namespace}.${baseUrl}` even when namespace was
124
+ // undefined, producing a literal "https://undefined.<host>" request
125
+ // URL (caught via forms-v2 marketing-site integration, P3). The
126
+ // Node branch above already had the equivalent guard
127
+ // (`if (!this._constructorBaseURL)`); this mirrors it for browser.
128
+ // A namespace passed ALONGSIDE baseURL still prefixes it (unchanged,
129
+ // existing behavior other callers rely on).
130
+ this.fullUrl = this._constructorBaseURL;
117
131
  } else {
118
132
  this.fullUrl = `https://${this.namespace}.${this.baseUrl}`;
119
133
  }
package/index.js CHANGED
@@ -26,6 +26,7 @@ import { CobrowseService } from './services/cobrowse.js';
26
26
  import { MessageTemplatesService } from './services/messageTemplates.js';
27
27
  import { ExternalOAuthService } from './services/externalOAuth.js';
28
28
  import { GoogleCalendarService } from './services/googleCalendar.js';
29
+ import { FormsService } from './services/forms.js';
29
30
  import { DriveService } from './services/drive.js';
30
31
  import { EnrollService } from './services/enroll.js';
31
32
  import { PhoneNumbersService } from './services/phoneNumbers.js';
@@ -125,6 +126,7 @@ class UnboundSDK extends BaseSDK {
125
126
  this.messageTemplates = new MessageTemplatesService(this);
126
127
  this.externalOAuth = new ExternalOAuthService(this);
127
128
  this.googleCalendar = new GoogleCalendarService(this);
129
+ this.forms = new FormsService(this);
128
130
  this.drive = new DriveService(this);
129
131
  this.enroll = new EnrollService(this);
130
132
  this.phoneNumbers = new PhoneNumbersService(this);
@@ -326,6 +328,8 @@ export { MessageTemplatesService } from './services/messageTemplates.js';
326
328
  export { WebchatVisitorService } from './services/webchat/VisitorService.js';
327
329
  export { ExternalOAuthService } from './services/externalOAuth.js';
328
330
  export { GoogleCalendarService } from './services/googleCalendar.js';
331
+ export { FormsService } from './services/forms.js';
332
+ export { FormsPublicService } from './services/forms/PublicService.js';
329
333
  export { DriveService } from './services/drive.js';
330
334
  export { EnrollService } from './services/enroll.js';
331
335
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.13.82",
3
+ "version": "4.13.85",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,57 @@
1
+ import { internalRequest } from '../../base.js';
2
+
3
+ // Public, unauthenticated forms surface -- `sdk.forms.public`. Mirrors
4
+ // WebchatVisitorService.js: the sdk instance backing this only needs
5
+ // `namespace` (or a custom `baseURL`) set at construction, never
6
+ // `sdk.token`. POSTs `forceFetch` (HTTP-only, no NATS transport) since
7
+ // these are one-shot fetches from a customer page / marketing site, same
8
+ // as every other visitor-facing call.
9
+ //
10
+ // Wire shape matches `POST /f/:publicKey` (app1-api webhooks service,
11
+ // forms-v2-precheck.md §4.1): plain form fields go at the top level of the
12
+ // body (fieldKey -> value, arrays allowed), and everything the server
13
+ // treats as a *control* field (not a form value) is sent with a leading
14
+ // underscore -- the same convention formSubmit.js/formSubmitPublicKey.js
15
+ // already split on for the legacy `_token` field.
16
+ export class FormsPublicService {
17
+ constructor(sdk) {
18
+ this.sdk = sdk;
19
+ }
20
+
21
+ /**
22
+ * Submit a public form by its publicKey (D1 -- every form has its own
23
+ * publicKey; no tracking-code/_token needed for this path). Legacy
24
+ * hosted-fields forms keep using the `/webhooks/form/:formId` +
25
+ * `_token` path (unchanged, not exposed here).
26
+ * @param {string} publicKey
27
+ * @param {Object} fields - fieldKey -> value (arrays kept as arrays).
28
+ * @param {Object} [options]
29
+ * @param {Object} [options.context] - Client-observed context (utm/referrer/
30
+ * landingUrl/etc, D12) to merge with what the server infers from the
31
+ * request itself -- sent as the `_context` control field.
32
+ * @param {string} [options.captchaToken] - Turnstile/etc response token
33
+ * (D10); sent as `_captchaToken`. Ignored server-side until a captcha
34
+ * provider is configured for the form/account.
35
+ * @param {string} [options.idempotencyKey] - Per-render dedupe token
36
+ * (D10); sent as `_idempotencyKey`.
37
+ * @returns {Promise<{ok:boolean, message?:string}>}
38
+ */
39
+ async submit(publicKey, fields, { context, captchaToken, idempotencyKey } = {}) {
40
+ this.sdk.validateParams(
41
+ { publicKey },
42
+ { publicKey: { type: 'string', required: true } },
43
+ );
44
+ const body = { ...(fields || {}) };
45
+ if (idempotencyKey) body._idempotencyKey = idempotencyKey;
46
+ if (captchaToken) body._captchaToken = captchaToken;
47
+ if (context) body._context = context;
48
+
49
+ return internalRequest(
50
+ this.sdk,
51
+ `/f/${publicKey}`,
52
+ 'POST',
53
+ { body },
54
+ true,
55
+ );
56
+ }
57
+ }
@@ -0,0 +1,46 @@
1
+ import { internalRequest } from '../../base.js';
2
+
3
+ // Agent-authenticated `formsAccountSettings` singleton -- `sdk.forms.settings`
4
+ // (defaultRegion, turnstileSiteKey, turnstileSecretRef; plan §6.5's
5
+ // account-settings page, `/app/setup/forms/account-settings`).
6
+ //
7
+ // GAP (forms-v2 P2, see plans/forms-v2-progress/P2.md): same as
8
+ // SubmissionsService.js -- the server-side singleton get/set controller +
9
+ // route are not built yet (no phase in forms-v2-precheck.md §9 owns this
10
+ // file explicitly). `/forms/settings` is P2's proposed path, chosen to
11
+ // mirror the client route name (`forms/account-settings`) rather than the
12
+ // generic `/object/:objectName` shape, since `formsAccountSettings` is a
13
+ // fixed-id singleton with no `id` the client ever knows (same shape as
14
+ // `chatAccountSettings`/`aiAccountSettings`) -- not a normal CRUD object.
15
+ // Whichever phase builds the backend (P5, alongside the account-settings
16
+ // page per §9) should implement exactly this path/method pair, or bump
17
+ // the SDK with a corrected one.
18
+ export class FormsSettingsService {
19
+ constructor(sdk) {
20
+ this.sdk = sdk;
21
+ }
22
+
23
+ /**
24
+ * @returns {Promise<{defaultRegion?:string, turnstileSiteKey?:string, turnstileSecretRef?:string}>}
25
+ * `turnstileSecretRef` is a reference/handle only -- the raw secret
26
+ * never round-trips to the client.
27
+ */
28
+ async get() {
29
+ return internalRequest(this.sdk, '/forms/settings', 'GET');
30
+ }
31
+
32
+ /**
33
+ * Merge-patch the singleton row.
34
+ * @param {Object} patch
35
+ * @param {string} [patch.defaultRegion]
36
+ * @param {string} [patch.turnstileSiteKey]
37
+ * @param {string} [patch.turnstileSecret] - Plaintext; server stores only
38
+ * `turnstileSecretRef` and never echoes the raw value back.
39
+ * @returns {Promise<Object>}
40
+ */
41
+ async set(patch) {
42
+ return internalRequest(this.sdk, '/forms/settings', 'PUT', {
43
+ body: { ...(patch || {}) },
44
+ });
45
+ }
46
+ }
@@ -0,0 +1,80 @@
1
+ import { internalRequest } from '../../base.js';
2
+
3
+ // Agent-authenticated formSubmissions actions -- `sdk.forms.submissions`.
4
+ // Normal agent-token path (no forceFetch, no authHeaders), same as any
5
+ // other authenticated service method (e.g. WebchatWidgetsService.get()).
6
+ //
7
+ // GAP (forms-v2 P2, see plans/forms-v2-progress/P2.md): the server-side
8
+ // routes these call do NOT exist yet. forms-v2-precheck.md §9 gates the
9
+ // Quarantine/reprocess UI behind P5, but never assigns an owner file for
10
+ // the *backend* controllers -- adding them here to app1-api's
11
+ // `objects/routes.js` (the `/:objectName/merge`-style precedent for a
12
+ // custom per-object action route) is outside P2's file-ownership map, so
13
+ // P2 only forward-declares the wire contract below. Whichever phase wires
14
+ // the server (P5 for reprocess/markNotSpam per plan §8, or earlier if
15
+ // needed) must implement these exact paths/methods, or bump the SDK with
16
+ // a corrected path.
17
+ export class FormsSubmissionsService {
18
+ constructor(sdk) {
19
+ this.sdk = sdk;
20
+ }
21
+
22
+ /**
23
+ * Re-run the pipeline for an existing submission from its stored
24
+ * rawFields (marks the new submission's `reprocessedFromId`).
25
+ * @param {string} id - formSubmissions id.
26
+ * @returns {Promise<Object>}
27
+ */
28
+ async reprocess(id) {
29
+ this.sdk.validateParams(
30
+ { id },
31
+ { id: { type: 'string', required: true } },
32
+ );
33
+ return internalRequest(
34
+ this.sdk,
35
+ `/object/formSubmissions/${id}/reprocess`,
36
+ 'POST',
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Clear a submission's spam status (Quarantine "Not spam" action, D10).
42
+ * @param {string} id - formSubmissions id.
43
+ * @returns {Promise<Object>}
44
+ */
45
+ async markNotSpam(id) {
46
+ this.sdk.validateParams(
47
+ { id },
48
+ { id: { type: 'string', required: true } },
49
+ );
50
+ return internalRequest(
51
+ this.sdk,
52
+ `/object/formSubmissions/${id}/mark-not-spam`,
53
+ 'POST',
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Resolve a submission flagged `identityConflict`/`review` (D16).
59
+ * @param {string} id - formSubmissions id.
60
+ * @param {string} choice - Which candidate record to keep/link; shape is
61
+ * whatever the Review queue UI (P4) settles on -- documented here as a
62
+ * passthrough until that's built.
63
+ * @returns {Promise<Object>}
64
+ */
65
+ async resolveReview(id, choice) {
66
+ this.sdk.validateParams(
67
+ { id, choice },
68
+ {
69
+ id: { type: 'string', required: true },
70
+ choice: { type: 'string', required: true },
71
+ },
72
+ );
73
+ return internalRequest(
74
+ this.sdk,
75
+ `/object/formSubmissions/${id}/resolve-review`,
76
+ 'POST',
77
+ { body: { choice } },
78
+ );
79
+ }
80
+ }
@@ -0,0 +1,15 @@
1
+ import { FormsPublicService } from './forms/PublicService.js';
2
+ import { FormsSubmissionsService } from './forms/SubmissionsService.js';
3
+ import { FormsSettingsService } from './forms/SettingsService.js';
4
+
5
+ // Forms v2 (forms-v2-plan.md §7 / forms-v2-precheck.md §5) -- `sdk.forms`.
6
+ // `public` needs no agent auth (VisitorService pattern, publicKey-scoped);
7
+ // `submissions`/`settings` are normal agent-token calls.
8
+ export class FormsService {
9
+ constructor(sdk) {
10
+ this.sdk = sdk;
11
+ this.public = new FormsPublicService(sdk);
12
+ this.submissions = new FormsSubmissionsService(sdk);
13
+ this.settings = new FormsSettingsService(sdk);
14
+ }
15
+ }