@unboundcx/sdk 4.13.92 → 4.13.94
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 +25 -10
- package/package.json +1 -1
- package/services/forms/PublicService.js +11 -2
- package/services/forms/README.md +160 -0
- package/services/forms/SettingsService.js +9 -16
- package/services/forms/SubmissionsService.js +12 -24
- package/services/storage.js +11 -12
- package/services/webchat/VisitorService.js +33 -9
package/base.js
CHANGED
|
@@ -16,6 +16,17 @@
|
|
|
16
16
|
const requestBySdk = new WeakMap();
|
|
17
17
|
const SDK_REQUEST = Symbol.for('unbound.sdk.request');
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Safely read a process.env var. `process` is an undeclared global in a
|
|
21
|
+
* browser/Vite bundle -- `process?.env?.X` does NOT protect against that
|
|
22
|
+
* (optional chaining only guards property access, not a bare identifier
|
|
23
|
+
* reference), so a browser bundle throws `ReferenceError: process is not
|
|
24
|
+
* defined` the moment `process` is referenced at all. Gate on
|
|
25
|
+
* `typeof process !== 'undefined'` first.
|
|
26
|
+
*/
|
|
27
|
+
export const env = (key) =>
|
|
28
|
+
typeof process !== 'undefined' && process.env ? process.env[key] : undefined;
|
|
29
|
+
|
|
19
30
|
/**
|
|
20
31
|
* Service-internal request. Not part of the public SDK surface.
|
|
21
32
|
* Named SDK methods call this; app code must not. Optional transports
|
|
@@ -43,14 +54,14 @@ export class BaseSDK {
|
|
|
43
54
|
// Support both object and legacy positional parameters for backwards compatibility
|
|
44
55
|
if (typeof options === 'string') {
|
|
45
56
|
// Legacy positional parameters: (namespace, callId, token, fwRequestId)
|
|
46
|
-
this.namespace = options ||
|
|
57
|
+
this.namespace = options || env('namespace');
|
|
47
58
|
this.callId = arguments[1];
|
|
48
59
|
this.token = arguments[2];
|
|
49
60
|
this.fwRequestId = arguments[3];
|
|
50
61
|
} else {
|
|
51
62
|
// New object-based parameters
|
|
52
63
|
const { namespace, callId, token, fwRequestId, baseURL } = options;
|
|
53
|
-
this.namespace = namespace ||
|
|
64
|
+
this.namespace = namespace || env('namespace');
|
|
54
65
|
this.callId = callId;
|
|
55
66
|
this.token = token;
|
|
56
67
|
this.fwRequestId = fwRequestId;
|
|
@@ -78,7 +89,7 @@ export class BaseSDK {
|
|
|
78
89
|
// Server-side (Node.js)
|
|
79
90
|
this.environment = 'node';
|
|
80
91
|
this.baseURL = this._constructorBaseURL || `https://${this.namespace ? this.namespace : 'login'}.${
|
|
81
|
-
|
|
92
|
+
env('API_BASE_URL') || defaultDomain
|
|
82
93
|
}`;
|
|
83
94
|
} else {
|
|
84
95
|
// Client-side (browser)
|
|
@@ -91,7 +102,7 @@ export class BaseSDK {
|
|
|
91
102
|
this.baseUrl = url.hostname.replace(/^[^.]+\./, '');
|
|
92
103
|
} else {
|
|
93
104
|
this.baseUrl =
|
|
94
|
-
this.baseUrl ||
|
|
105
|
+
this.baseUrl || env('API_BASE_URL') || defaultDomain;
|
|
95
106
|
if (this.baseUrl && !this.baseUrl.startsWith('api.')) {
|
|
96
107
|
this.baseUrl = `api.${this.baseUrl}`;
|
|
97
108
|
}
|
|
@@ -111,7 +122,7 @@ export class BaseSDK {
|
|
|
111
122
|
if (this.environment === 'node') {
|
|
112
123
|
if (!this._constructorBaseURL) {
|
|
113
124
|
this.baseURL = `https://${this.namespace ? this.namespace : 'login'}.${
|
|
114
|
-
|
|
125
|
+
env('API_BASE_URL') || defaultDomain
|
|
115
126
|
}`;
|
|
116
127
|
}
|
|
117
128
|
} else if (this._constructorBaseURL && !this.namespace) {
|
|
@@ -296,9 +307,9 @@ export class BaseSDK {
|
|
|
296
307
|
}
|
|
297
308
|
} else {
|
|
298
309
|
// No transport available, fallback to HTTP
|
|
299
|
-
if (forceFetch &&
|
|
310
|
+
if (forceFetch && env('AUTH_V3_TOKEN_TYPE_OVERRIDE')) {
|
|
300
311
|
params.headers['x-token-type-override'] =
|
|
301
|
-
|
|
312
|
+
env('AUTH_V3_TOKEN_TYPE_OVERRIDE');
|
|
302
313
|
}
|
|
303
314
|
return this._httpRequest(
|
|
304
315
|
endpoint,
|
|
@@ -372,7 +383,7 @@ export class BaseSDK {
|
|
|
372
383
|
returnRawResponse = false,
|
|
373
384
|
startTime = Date.now(),
|
|
374
385
|
) {
|
|
375
|
-
const { body, query, headers = {} } = params;
|
|
386
|
+
const { body, query, headers = {}, credentials } = params;
|
|
376
387
|
|
|
377
388
|
const options = {
|
|
378
389
|
method,
|
|
@@ -387,9 +398,13 @@ export class BaseSDK {
|
|
|
387
398
|
},
|
|
388
399
|
};
|
|
389
400
|
|
|
390
|
-
// Set credentials for browser environment
|
|
401
|
+
// Set credentials for browser environment. Public/unauthenticated
|
|
402
|
+
// endpoints called from third-party origins (forms, webchat loader) opt
|
|
403
|
+
// out via params.credentials = 'omit' — app1-api's CORS only sends
|
|
404
|
+
// access-control-allow-credentials for trusted app1 base domains, so a
|
|
405
|
+
// browser rejects the response for `include` requests from other origins.
|
|
391
406
|
if (this.environment === 'browser') {
|
|
392
|
-
options.credentials = 'include';
|
|
407
|
+
options.credentials = credentials === 'omit' ? 'omit' : 'include';
|
|
393
408
|
}
|
|
394
409
|
|
|
395
410
|
let url;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.94",
|
|
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",
|
|
@@ -7,6 +7,15 @@ import { internalRequest } from '../../base.js';
|
|
|
7
7
|
// these are one-shot fetches from a customer page / marketing site, same
|
|
8
8
|
// as every other visitor-facing call.
|
|
9
9
|
//
|
|
10
|
+
// Every call passes `credentials: 'omit'` (base.js `_httpRequest` opt-in) --
|
|
11
|
+
// this surface is hit from third-party origins (a marketing site embedding
|
|
12
|
+
// a form, not an app1 base domain), and app1-api's CORS
|
|
13
|
+
// (`src/index.js` allowlist) only sends `access-control-allow-credentials`
|
|
14
|
+
// for trusted app1 base domains. A browser rejects the whole response for
|
|
15
|
+
// a `credentials:'include'` request when that header is missing, even
|
|
16
|
+
// though these endpoints don't use cookies for auth -- so third-party
|
|
17
|
+
// callers must opt out explicitly.
|
|
18
|
+
//
|
|
10
19
|
// Wire shape matches `POST /f/:publicKey` (app1-api webhooks service,
|
|
11
20
|
// forms-v2-precheck.md §4.1): plain form fields go at the top level of the
|
|
12
21
|
// body (fieldKey -> value, arrays allowed), and everything the server
|
|
@@ -56,7 +65,7 @@ export class FormsPublicService {
|
|
|
56
65
|
this.sdk,
|
|
57
66
|
`/f/${publicKey}`,
|
|
58
67
|
'POST',
|
|
59
|
-
{ body },
|
|
68
|
+
{ body, credentials: 'omit' },
|
|
60
69
|
true,
|
|
61
70
|
);
|
|
62
71
|
}
|
|
@@ -94,7 +103,7 @@ export class FormsPublicService {
|
|
|
94
103
|
this.sdk,
|
|
95
104
|
`/f/${publicKey}/upload`,
|
|
96
105
|
'POST',
|
|
97
|
-
{ body },
|
|
106
|
+
{ body, credentials: 'omit' },
|
|
98
107
|
true,
|
|
99
108
|
);
|
|
100
109
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Forms v2 SDK module (`sdk.forms`)
|
|
2
|
+
|
|
3
|
+
Internal reference for `services/forms.js` + `services/forms/*.js` as they exist in code
|
|
4
|
+
today on branch `f-forms-v2` (sdk version `4.13.92`). Not the plan — every claim below is
|
|
5
|
+
cited to a source line. See `/workspace/code/app1/plans/forms-v2-plan.md` /
|
|
6
|
+
`forms-v2-progress.md` / `forms-v2-precheck.md` for design history; §"Deviations from
|
|
7
|
+
plan" at the bottom lists where progress-doc claims and code disagree.
|
|
8
|
+
|
|
9
|
+
## Files
|
|
10
|
+
|
|
11
|
+
| File | Exports |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `services/forms.js` | `FormsService` (registers as `sdk.forms`) |
|
|
14
|
+
| `services/forms/PublicService.js` | `FormsPublicService` → `sdk.forms.public` |
|
|
15
|
+
| `services/forms/SubmissionsService.js` | `FormsSubmissionsService` → `sdk.forms.submissions` |
|
|
16
|
+
| `services/forms/SettingsService.js` | `FormsSettingsService` → `sdk.forms.settings` |
|
|
17
|
+
| `services/forms/HealthService.js` | `FormsHealthService` → `sdk.forms.health` |
|
|
18
|
+
|
|
19
|
+
Registered in `index.js:29` (import), `index.js:129` (`this.forms = new FormsService(this)`),
|
|
20
|
+
exported at `index.js:331-333`.
|
|
21
|
+
|
|
22
|
+
## Methods
|
|
23
|
+
|
|
24
|
+
| Method | Signature | Returns | Purpose |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `sdk.forms.public.submit` | `submit(publicKey: string, fields: Object, { context, captchaToken, idempotencyKey }: Object = {})` | `Promise<{ok: boolean, message?: string}>` | Submit a public form by `publicKey`; `POST /f/:publicKey` |
|
|
27
|
+
| `sdk.forms.public.upload` | `upload(publicKey: string, fieldKey: string, file: File\|Blob)` | `Promise<{fileId, token, fileName, fileType, fileSize}>` | Browser-only file upload ahead of `submit()`, for a `file`-type field; `POST /f/:publicKey/upload` |
|
|
28
|
+
| `sdk.forms.submissions.reprocess` | `reprocess(id: string)` | `Promise<Object>` | Re-run the pipeline for a stored submission; `POST /object/formSubmissions/:id/reprocess` |
|
|
29
|
+
| `sdk.forms.submissions.markNotSpam` | `markNotSpam(id: string)` | `Promise<Object>` | Clear a submission's spam status; `POST /object/formSubmissions/:id/mark-not-spam` |
|
|
30
|
+
| `sdk.forms.submissions.resolveReview` | `resolveReview(id: string, choice: string)` | `Promise<Object>` | Resolve an `identityConflict`/`review`-flagged submission; `POST /object/formSubmissions/:id/resolve-review`, body `{choice}` |
|
|
31
|
+
| `sdk.forms.settings.get` | `get()` | `Promise<{defaultRegion?, turnstileSiteKey?, turnstileSecretRef?}>` | Read the account-level `formsAccountSettings` singleton; `GET /forms/settings` |
|
|
32
|
+
| `sdk.forms.settings.set` | `set(patch: {defaultRegion?, turnstileSiteKey?, turnstileSecret?})` | `Promise<Object>` | Merge-patch the singleton; `turnstileSecret` is plaintext in, never echoed back; `PUT /forms/settings` |
|
|
33
|
+
| `sdk.forms.settings.setForm` | `setForm(formId: string, patch: {captchaProvider?, turnstileSiteKey?, turnstileSecret?})` | `Promise<Object>` | Per-form Turnstile override; `PUT /forms/:formId/settings` |
|
|
34
|
+
| `sdk.forms.health.get` | `get(formId: string)` | `Promise<{tiles, sparkline, fields, driftAlerts, windowDays}>` | Health tab data (tiles, 30-day sparkline, per-field fill rate, drift alerts); `GET /forms/:formId/health` |
|
|
35
|
+
| `sdk.forms.regeneratePublicKey` | `regeneratePublicKey(formId: string)` | `Promise<{publicKey: string}>` | Mint a new `publicKey`, invalidating every embed using the old one (destructive); `POST /forms/:formId/regenerate-key` |
|
|
36
|
+
| `sdk.forms.previewToken` | `previewToken(formId: string)` | `Promise<{token: string, expiresIn: number}>` | Mint a signed 15-min draft-preview token; `POST /forms/:formId/preview-token` |
|
|
37
|
+
|
|
38
|
+
Sources: `PublicService.js:46-100`, `SubmissionsService.js:28-79`, `SettingsService.js:28-72`,
|
|
39
|
+
`HealthService.js:28-34`, `forms.js:20-63`.
|
|
40
|
+
|
|
41
|
+
## Auth model
|
|
42
|
+
|
|
43
|
+
| Group | Methods | Session/JWT required? | Mechanism |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| Public | `public.submit`, `public.upload` | No | `internalRequest(..., forceFetch=true)` — `PublicService.js:46-62,77-100` explicitly pass `true` as the 5th arg. No `sdk.token` involved; auth is `publicKey` + server-side origin/domainAllowlist check, not a session (comment `PublicService.js:1-8`, mirrors `WebchatVisitorService`/`VisitorService` pattern). |
|
|
46
|
+
| Agent-authenticated | `submissions.*`, `settings.*`, `health.get`, `regeneratePublicKey`, `previewToken` | Yes | `internalRequest(sdk, endpoint, method, {...})` with no `forceFetch`/`httpOnly` flag — routed through the SDK's normal transport (socket/HTTP) using `sdk.token`, same as any other authenticated service call (e.g. `WebchatWidgetsService.get()`). |
|
|
47
|
+
|
|
48
|
+
`internalRequest`'s `forceFetch` param (`base.js:22-38`) skips optional transports and goes
|
|
49
|
+
HTTP-only — used for the two public methods because they're one-shot calls from a visitor
|
|
50
|
+
page with no socket session, same convention as file upload/download elsewhere in the SDK.
|
|
51
|
+
|
|
52
|
+
Both `public.submit` and `public.upload` also pass `credentials: 'omit'` in the request
|
|
53
|
+
params (`PublicService.js:46-62,77-100`; consumed by `base.js` `_httpRequest`, which
|
|
54
|
+
otherwise defaults browser fetches to `credentials:'include'`). This surface is called
|
|
55
|
+
from third-party origins (a marketing site, not an app1 base domain), and app1-api's CORS
|
|
56
|
+
only sends `access-control-allow-credentials` for trusted app1 base domains — a browser
|
|
57
|
+
rejects the entire response for a `credentials:'include'` fetch when that header is
|
|
58
|
+
missing, even though these endpoints don't use cookies for auth. `sdk.webchat.visitor.*`
|
|
59
|
+
(`services/webchat/VisitorService.js`) has the same third-party-origin exposure and sets
|
|
60
|
+
the same option for the same reason.
|
|
61
|
+
|
|
62
|
+
**Known SDK-surface gap (not a code bug, documented in-file):** `submissions.*` and
|
|
63
|
+
`settings.get/set` call server routes that, per their own doc comments, were not yet built
|
|
64
|
+
as of P2 (`SubmissionsService.js:7-16`, `SettingsService.js:7-17` — "GAP (forms-v2 P2) ...
|
|
65
|
+
the server-side routes these call do NOT exist yet"). `forms-v2-progress.md` marks P5 done
|
|
66
|
+
and lists "forms account settings API" and "reprocess/mark-not-spam/review actions" as
|
|
67
|
+
shipped, so these comments are likely stale relative to the API side — the SDK client code
|
|
68
|
+
itself was not re-verified against `app1-api` route files in this pass (out of scope: this
|
|
69
|
+
README covers the SDK repo only). Flagged in Deviations below.
|
|
70
|
+
|
|
71
|
+
## Example — browser handler mode (public form submission)
|
|
72
|
+
|
|
73
|
+
Realistic embed-adjacent usage: a page with its own HTML/`<form>` calling the SDK directly
|
|
74
|
+
(the `forms.public` surface backs both the generated embed script and any hand-rolled
|
|
75
|
+
integration like this).
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
import { BaseSDK, internalRequest } from '@unboundcx/sdk/base.js';
|
|
79
|
+
import { FormsPublicService } from '@unboundcx/sdk/services/forms/PublicService.js';
|
|
80
|
+
|
|
81
|
+
// Minimal public-only instance — namespace or a custom baseURL, NEVER sdk.token.
|
|
82
|
+
// Importing only base.js + PublicService.js avoids pulling in the full SDK's
|
|
83
|
+
// service tree (some of which have bundler-unfriendly static imports that broke
|
|
84
|
+
// marketing_unbound_cx's browser build — see CHANGELOG 4.13.90).
|
|
85
|
+
const sdk = new BaseSDK({ namespace: 'masterc' });
|
|
86
|
+
const forms = new FormsPublicService(sdk);
|
|
87
|
+
|
|
88
|
+
document.querySelector('#lead-form').addEventListener('submit', async (e) => {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
const data = new FormData(e.target);
|
|
91
|
+
|
|
92
|
+
await forms.submit(
|
|
93
|
+
'PUBLIC_KEY_HERE',
|
|
94
|
+
{
|
|
95
|
+
email: data.get('email'),
|
|
96
|
+
name: data.get('name'),
|
|
97
|
+
interests: data.getAll('interests'), // arrays kept as arrays
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
context: {
|
|
101
|
+
utm_source: new URLSearchParams(location.search).get('utm_source'),
|
|
102
|
+
referrer: document.referrer,
|
|
103
|
+
landingUrl: location.href,
|
|
104
|
+
},
|
|
105
|
+
idempotencyKey: crypto.randomUUID(),
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`context` is spread onto the top level of the request body alongside `fields` (not nested
|
|
112
|
+
under a `_context` key — that was a bug fixed in 4.13.89, `PublicService.js:10-21` +
|
|
113
|
+
CHANGELOG). Control fields (`_idempotencyKey`, `_captchaToken`) are sent with a leading
|
|
114
|
+
underscore so the server can distinguish them from real form values (`PublicService.js:51-53`).
|
|
115
|
+
|
|
116
|
+
## Example — server-side submit (Node)
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
import UnboundSDK from '@unboundcx/sdk';
|
|
120
|
+
|
|
121
|
+
// GOTCHA: in Node, UnboundSDK's constructor does NOT use a literal `baseURL` you pass
|
|
122
|
+
// unless you pass it explicitly as `options.baseURL` — and even then only via the
|
|
123
|
+
// object-form constructor. If you only pass `namespace` (the common case), Node builds
|
|
124
|
+
// the URL as `https://<namespace||'login'>.${process.env.API_BASE_URL || 'api.unbound.cx'}`
|
|
125
|
+
// (base.js:79-82). So:
|
|
126
|
+
// - No API_BASE_URL env var set -> requests go to https://masterc.api.unbound.cx (WRONG
|
|
127
|
+
// for dev — hits the real production-shaped host, not dev-d01).
|
|
128
|
+
// - API_BASE_URL is treated as a DOMAIN SUFFIX appended after the namespace, not a full
|
|
129
|
+
// URL override — set it to e.g. `dev-d01.app1svc.com`, not `https://api.dev-d01...`.
|
|
130
|
+
process.env.API_BASE_URL = 'dev-d01.app1svc.com';
|
|
131
|
+
|
|
132
|
+
const sdk = new UnboundSDK({ namespace: 'masterc', token: process.env.AGENT_JWT });
|
|
133
|
+
|
|
134
|
+
const health = await sdk.forms.health.get('formIdHere');
|
|
135
|
+
console.log(health.tiles);
|
|
136
|
+
|
|
137
|
+
// Public submit also works from Node (no browser needed) since forms.public.submit()
|
|
138
|
+
// only requires FormData for upload(), not submit():
|
|
139
|
+
await sdk.forms.public.submit('PUBLIC_KEY_HERE', { email: 'test@example.com' });
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Verified against `base.js:56-82` (`_initializeEnvironment`, Node branch) and
|
|
143
|
+
`base.js:107-134` (`setNamespace`, Node branch uses the same `!this._constructorBaseURL`
|
|
144
|
+
guard) — this gotcha is still true for this SDK's client construction as of `4.13.92`.
|
|
145
|
+
Matches the repo memory note "SDK Node baseURL gotcha."
|
|
146
|
+
|
|
147
|
+
## Deviations from plan / progress docs
|
|
148
|
+
|
|
149
|
+
1. **Health service exists but isn't in the precheck's SDK contract.** `forms-v2-precheck.md`
|
|
150
|
+
§5 only specifies `public`, `submissions`, `settings` on `FormsService`; `health` (P8,
|
|
151
|
+
`HealthService.js`) and the two gap-closure methods `regeneratePublicKey`/`previewToken`
|
|
152
|
+
(added directly on `FormsService`, not a sub-service) were added later and aren't in the
|
|
153
|
+
precheck's code sample — expected drift for a precheck doc frozen at P0, not a bug.
|
|
154
|
+
2. **`submissions.*` / `settings.get`/`settings.set` carry "route may not exist yet" comments
|
|
155
|
+
still in the source** (`SubmissionsService.js:7-16`, `SettingsService.js:7-17`), even
|
|
156
|
+
though `forms-v2-progress.md` marks P5 ("Captcha gate ... forms account settings API,
|
|
157
|
+
submission reprocess/mark-not-spam/review actions") as done. This README does not resolve
|
|
158
|
+
the discrepancy — it was not in scope to audit `app1-api`'s route files — but flags it as
|
|
159
|
+
a live contradiction between the sdk source comments and the progress ledger. Reported
|
|
160
|
+
back to Cameron per task instructions.
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import { internalRequest } from
|
|
1
|
+
import { internalRequest } from "../../base.js";
|
|
2
2
|
|
|
3
3
|
// Agent-authenticated `formsAccountSettings` singleton -- `sdk.forms.settings`
|
|
4
4
|
// (defaultRegion, turnstileSiteKey, turnstileSecretRef; plan §6.5's
|
|
5
5
|
// account-settings page, `/app/setup/forms/account-settings`).
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
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.
|
|
7
|
+
// Methods call:
|
|
8
|
+
// - get() → GET /forms/settings (services/forms/routes.js)
|
|
9
|
+
// - set() → PUT /forms/settings (services/forms/routes.js)
|
|
10
|
+
// - setForm() → PUT /forms/:id/settings (services/forms/routes.js)
|
|
18
11
|
export class FormsSettingsService {
|
|
19
12
|
constructor(sdk) {
|
|
20
13
|
this.sdk = sdk;
|
|
@@ -26,7 +19,7 @@ export class FormsSettingsService {
|
|
|
26
19
|
* never round-trips to the client.
|
|
27
20
|
*/
|
|
28
21
|
async get() {
|
|
29
|
-
return internalRequest(this.sdk,
|
|
22
|
+
return internalRequest(this.sdk, "/forms/settings", "GET");
|
|
30
23
|
}
|
|
31
24
|
|
|
32
25
|
/**
|
|
@@ -39,7 +32,7 @@ export class FormsSettingsService {
|
|
|
39
32
|
* @returns {Promise<Object>}
|
|
40
33
|
*/
|
|
41
34
|
async set(patch) {
|
|
42
|
-
return internalRequest(this.sdk,
|
|
35
|
+
return internalRequest(this.sdk, "/forms/settings", "PUT", {
|
|
43
36
|
body: { ...(patch || {}) },
|
|
44
37
|
});
|
|
45
38
|
}
|
|
@@ -61,12 +54,12 @@ export class FormsSettingsService {
|
|
|
61
54
|
async setForm(formId, patch) {
|
|
62
55
|
this.sdk.validateParams(
|
|
63
56
|
{ formId },
|
|
64
|
-
{ formId: { type:
|
|
57
|
+
{ formId: { type: "string", required: true } },
|
|
65
58
|
);
|
|
66
59
|
return internalRequest(
|
|
67
60
|
this.sdk,
|
|
68
61
|
`/forms/${encodeURIComponent(formId)}/settings`,
|
|
69
|
-
|
|
62
|
+
"PUT",
|
|
70
63
|
{ body: { ...(patch || {}) } },
|
|
71
64
|
);
|
|
72
65
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { internalRequest } from
|
|
1
|
+
import { internalRequest } from "../../base.js";
|
|
2
2
|
|
|
3
3
|
// Agent-authenticated formSubmissions actions -- `sdk.forms.submissions`.
|
|
4
4
|
// Normal agent-token path (no forceFetch, no authHeaders), same as any
|
|
5
5
|
// other authenticated service method (e.g. WebchatWidgetsService.get()).
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
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.
|
|
7
|
+
// Methods call:
|
|
8
|
+
// - reprocess() → POST /object/formSubmissions/:id/reprocess (api objects/routes.js)
|
|
9
|
+
// - markNotSpam() → POST /object/formSubmissions/:id/mark-not-spam (api objects/routes.js)
|
|
10
|
+
// - resolveReview() → POST /object/formSubmissions/:id/resolve-review (api objects/routes.js)
|
|
17
11
|
export class FormsSubmissionsService {
|
|
18
12
|
constructor(sdk) {
|
|
19
13
|
this.sdk = sdk;
|
|
@@ -26,14 +20,11 @@ export class FormsSubmissionsService {
|
|
|
26
20
|
* @returns {Promise<Object>}
|
|
27
21
|
*/
|
|
28
22
|
async reprocess(id) {
|
|
29
|
-
this.sdk.validateParams(
|
|
30
|
-
{ id },
|
|
31
|
-
{ id: { type: 'string', required: true } },
|
|
32
|
-
);
|
|
23
|
+
this.sdk.validateParams({ id }, { id: { type: "string", required: true } });
|
|
33
24
|
return internalRequest(
|
|
34
25
|
this.sdk,
|
|
35
26
|
`/object/formSubmissions/${id}/reprocess`,
|
|
36
|
-
|
|
27
|
+
"POST",
|
|
37
28
|
);
|
|
38
29
|
}
|
|
39
30
|
|
|
@@ -43,14 +34,11 @@ export class FormsSubmissionsService {
|
|
|
43
34
|
* @returns {Promise<Object>}
|
|
44
35
|
*/
|
|
45
36
|
async markNotSpam(id) {
|
|
46
|
-
this.sdk.validateParams(
|
|
47
|
-
{ id },
|
|
48
|
-
{ id: { type: 'string', required: true } },
|
|
49
|
-
);
|
|
37
|
+
this.sdk.validateParams({ id }, { id: { type: "string", required: true } });
|
|
50
38
|
return internalRequest(
|
|
51
39
|
this.sdk,
|
|
52
40
|
`/object/formSubmissions/${id}/mark-not-spam`,
|
|
53
|
-
|
|
41
|
+
"POST",
|
|
54
42
|
);
|
|
55
43
|
}
|
|
56
44
|
|
|
@@ -66,14 +54,14 @@ export class FormsSubmissionsService {
|
|
|
66
54
|
this.sdk.validateParams(
|
|
67
55
|
{ id, choice },
|
|
68
56
|
{
|
|
69
|
-
id: { type:
|
|
70
|
-
choice: { type:
|
|
57
|
+
id: { type: "string", required: true },
|
|
58
|
+
choice: { type: "string", required: true },
|
|
71
59
|
},
|
|
72
60
|
);
|
|
73
61
|
return internalRequest(
|
|
74
62
|
this.sdk,
|
|
75
63
|
`/object/formSubmissions/${id}/resolve-review`,
|
|
76
|
-
|
|
64
|
+
"POST",
|
|
77
65
|
{ body: { choice } },
|
|
78
66
|
);
|
|
79
67
|
}
|
package/services/storage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { internalRequest } from '../base.js';
|
|
1
|
+
import { internalRequest, env } from '../base.js';
|
|
2
2
|
export class StorageService {
|
|
3
3
|
constructor(sdk) {
|
|
4
4
|
this.sdk = sdk;
|
|
@@ -314,13 +314,12 @@ export class StorageService {
|
|
|
314
314
|
headers = result.headers;
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
if (
|
|
318
|
-
headers['x-token-type-override'] =
|
|
319
|
-
process.env.AUTH_V3_TOKEN_TYPE_OVERRIDE;
|
|
317
|
+
if (env('AUTH_V3_TOKEN_TYPE_OVERRIDE')) {
|
|
318
|
+
headers['x-token-type-override'] = env('AUTH_V3_TOKEN_TYPE_OVERRIDE');
|
|
320
319
|
}
|
|
321
320
|
|
|
322
|
-
if (skipClamscan &&
|
|
323
|
-
headers['x-clamscan-override-key'] =
|
|
321
|
+
if (skipClamscan && env('CLAMSCAN_OVERRIDE_KEY')) {
|
|
322
|
+
headers['x-clamscan-override-key'] = env('CLAMSCAN_OVERRIDE_KEY');
|
|
324
323
|
}
|
|
325
324
|
|
|
326
325
|
const params = {
|
|
@@ -408,16 +407,16 @@ export class StorageService {
|
|
|
408
407
|
}
|
|
409
408
|
|
|
410
409
|
// Add environment variable override headers
|
|
411
|
-
if (
|
|
410
|
+
if (env('AUTH_V3_TOKEN_TYPE_OVERRIDE')) {
|
|
412
411
|
xhr.setRequestHeader(
|
|
413
412
|
'x-token-type-override',
|
|
414
|
-
|
|
413
|
+
env('AUTH_V3_TOKEN_TYPE_OVERRIDE'),
|
|
415
414
|
);
|
|
416
415
|
}
|
|
417
|
-
if (skipClamscan &&
|
|
416
|
+
if (skipClamscan && env('CLAMSCAN_OVERRIDE_KEY')) {
|
|
418
417
|
xhr.setRequestHeader(
|
|
419
418
|
'x-clamscan-override-key',
|
|
420
|
-
|
|
419
|
+
env('CLAMSCAN_OVERRIDE_KEY'),
|
|
421
420
|
);
|
|
422
421
|
}
|
|
423
422
|
|
|
@@ -656,8 +655,8 @@ Response:
|
|
|
656
655
|
if (metadata) formData.append('metadata', JSON.stringify(metadata));
|
|
657
656
|
}
|
|
658
657
|
|
|
659
|
-
if (_options?.skipScan &&
|
|
660
|
-
headers['x-clamscan-override-key'] =
|
|
658
|
+
if (_options?.skipScan && env('CLAMSCAN_OVERRIDE_KEY')) {
|
|
659
|
+
headers['x-clamscan-override-key'] = env('CLAMSCAN_OVERRIDE_KEY');
|
|
661
660
|
}
|
|
662
661
|
|
|
663
662
|
const params = {
|
|
@@ -14,6 +14,16 @@ import { internalRequest } from '../../base.js';
|
|
|
14
14
|
// method forces HTTP (esign.public precedent, base.js `forceFetch`) since
|
|
15
15
|
// these are one-shot fetches from a customer page or a custom UI, never
|
|
16
16
|
// NATS-transport traffic.
|
|
17
|
+
//
|
|
18
|
+
// Every call also passes `credentials: 'omit'` (base.js `_httpRequest`
|
|
19
|
+
// opt-in), matching app1-webchat-embed's own webchatApi.js fetch wrapper
|
|
20
|
+
// (which already sets `credentials:'omit'` for the same reason). This
|
|
21
|
+
// surface is meant for a custom UI on a third-party origin calling the API
|
|
22
|
+
// directly (see `grant()` below), not just the embed iframe -- and
|
|
23
|
+
// app1-api's CORS only sends `access-control-allow-credentials` for
|
|
24
|
+
// trusted app1 base domains, so a browser rejects the response for a
|
|
25
|
+
// `credentials:'include'` request from any other origin even though auth
|
|
26
|
+
// here is Authorization-header/body-token based, not cookie based.
|
|
17
27
|
function authHeaders(token) {
|
|
18
28
|
return token ? { headers: { Authorization: `Bearer ${token}` } } : {};
|
|
19
29
|
}
|
|
@@ -64,6 +74,7 @@ class WebchatVisitorSessionService {
|
|
|
64
74
|
trackingId,
|
|
65
75
|
initialMessage,
|
|
66
76
|
},
|
|
77
|
+
credentials: 'omit',
|
|
67
78
|
},
|
|
68
79
|
true,
|
|
69
80
|
);
|
|
@@ -89,7 +100,7 @@ class WebchatVisitorSessionService {
|
|
|
89
100
|
this.sdk,
|
|
90
101
|
`/webchat/${widgetId}/visitor-profile`,
|
|
91
102
|
'POST',
|
|
92
|
-
{ body: { embedGrant, trackingId } },
|
|
103
|
+
{ body: { embedGrant, trackingId }, credentials: 'omit' },
|
|
93
104
|
true,
|
|
94
105
|
);
|
|
95
106
|
}
|
|
@@ -115,7 +126,7 @@ class WebchatVisitorSessionService {
|
|
|
115
126
|
this.sdk,
|
|
116
127
|
`/webchat/${widgetId}/session`,
|
|
117
128
|
'POST',
|
|
118
|
-
{ body: { resumeToken } },
|
|
129
|
+
{ body: { resumeToken }, credentials: 'omit' },
|
|
119
130
|
true,
|
|
120
131
|
);
|
|
121
132
|
}
|
|
@@ -140,7 +151,7 @@ class WebchatVisitorSessionService {
|
|
|
140
151
|
this.sdk,
|
|
141
152
|
`/webchat/${widgetId}/session/end`,
|
|
142
153
|
'POST',
|
|
143
|
-
{ ...authHeaders(token) },
|
|
154
|
+
{ ...authHeaders(token), credentials: 'omit' },
|
|
144
155
|
true,
|
|
145
156
|
);
|
|
146
157
|
}
|
|
@@ -179,6 +190,7 @@ class WebchatVisitorMessagesService {
|
|
|
179
190
|
...(limit !== undefined && limit !== null ? { limit } : {}),
|
|
180
191
|
},
|
|
181
192
|
...authHeaders(token),
|
|
193
|
+
credentials: 'omit',
|
|
182
194
|
},
|
|
183
195
|
true,
|
|
184
196
|
);
|
|
@@ -205,7 +217,7 @@ class WebchatVisitorMessagesService {
|
|
|
205
217
|
this.sdk,
|
|
206
218
|
`/webchat/${widgetId}/messages`,
|
|
207
219
|
'POST',
|
|
208
|
-
{ body: { message, media }, ...authHeaders(token) },
|
|
220
|
+
{ body: { message, media }, ...authHeaders(token), credentials: 'omit' },
|
|
209
221
|
true,
|
|
210
222
|
);
|
|
211
223
|
}
|
|
@@ -248,7 +260,7 @@ class WebchatVisitorFilesService {
|
|
|
248
260
|
this.sdk,
|
|
249
261
|
`/webchat/${widgetId}/files`,
|
|
250
262
|
'POST',
|
|
251
|
-
{ body, ...authHeaders(token) },
|
|
263
|
+
{ body, ...authHeaders(token), credentials: 'omit' },
|
|
252
264
|
true,
|
|
253
265
|
);
|
|
254
266
|
}
|
|
@@ -297,7 +309,13 @@ export class WebchatVisitorService {
|
|
|
297
309
|
{ widgetId },
|
|
298
310
|
{ widgetId: { type: 'string', required: true } },
|
|
299
311
|
);
|
|
300
|
-
return internalRequest(
|
|
312
|
+
return internalRequest(
|
|
313
|
+
this.sdk,
|
|
314
|
+
`/webchat/${widgetId}/grant`,
|
|
315
|
+
'GET',
|
|
316
|
+
{ credentials: 'omit' },
|
|
317
|
+
true,
|
|
318
|
+
);
|
|
301
319
|
}
|
|
302
320
|
|
|
303
321
|
/**
|
|
@@ -310,7 +328,13 @@ export class WebchatVisitorService {
|
|
|
310
328
|
{ widgetId },
|
|
311
329
|
{ widgetId: { type: 'string', required: true } },
|
|
312
330
|
);
|
|
313
|
-
return internalRequest(
|
|
331
|
+
return internalRequest(
|
|
332
|
+
this.sdk,
|
|
333
|
+
`/webchat/${widgetId}/status`,
|
|
334
|
+
'GET',
|
|
335
|
+
{ credentials: 'omit' },
|
|
336
|
+
true,
|
|
337
|
+
);
|
|
314
338
|
}
|
|
315
339
|
|
|
316
340
|
/**
|
|
@@ -336,7 +360,7 @@ export class WebchatVisitorService {
|
|
|
336
360
|
this.sdk,
|
|
337
361
|
`/webchat/${widgetId}/transcript`,
|
|
338
362
|
'POST',
|
|
339
|
-
{ body: { optIn, email }, ...authHeaders(token) },
|
|
363
|
+
{ body: { optIn, email }, ...authHeaders(token), credentials: 'omit' },
|
|
340
364
|
true,
|
|
341
365
|
);
|
|
342
366
|
}
|
|
@@ -372,7 +396,7 @@ export class WebchatVisitorService {
|
|
|
372
396
|
this.sdk,
|
|
373
397
|
`/webchat/${widgetId}/identify`,
|
|
374
398
|
'POST',
|
|
375
|
-
{ body: { ...fields, hash }, ...authHeaders(token) },
|
|
399
|
+
{ body: { ...fields, hash }, ...authHeaders(token), credentials: 'omit' },
|
|
376
400
|
true,
|
|
377
401
|
);
|
|
378
402
|
}
|