@dataships/sdk 0.0.0-stage → 2.0.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.
@@ -0,0 +1,233 @@
1
+ type Channel = "EMAIL" | "SMS" | "WHATSAPP";
2
+ /** Consent type of a v2 consent entry. `PENDING_IDENTITY` and `NO_WIDGET_DISPLAYED` never reach the client. */
3
+ type ConsentType = "OPT_IN" | "OPT_OUT" | "NO_OPT_IN" | "NO_OPT_OUT" | "NONE" | "OPT_IN_PRE_TICKED" | "PENDING_IDENTITY" | "NO_WIDGET_DISPLAYED";
4
+ declare const SUPPORTED_LOCALES: readonly ["en", "es", "de", "pt", "ro", "hr", "da", "fi", "fr", "hu", "it", "nb", "pl", "sk", "sv", "nl", "el", "cs", "zh", "ja", "is"];
5
+ type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
6
+ /**
7
+ * Which contact the consent-session envelope carries at the top
8
+ * level. `EMAIL` ⇒ email address, `PHONE` ⇒ phone number.
9
+ */
10
+ type IdentifierType = "EMAIL" | "PHONE";
11
+ type Environment = "production" | "staging" | "local";
12
+ /** The consent-session handle `saveConsent` and `startHeartbeat` operate on. */
13
+ interface SessionIds {
14
+ consentSessionId: string | null;
15
+ }
16
+ /** Resolved top-level identifier for the consent-session envelope. */
17
+ interface ConsentSessionIdentifier {
18
+ identifier: string | null;
19
+ identifierType: IdentifierType;
20
+ }
21
+ /**
22
+ * Browser credentials for the consent-session protocol: the merchant's public
23
+ * key (`X-Api-Key`) and the JWT their server minted for this page, sent
24
+ * verbatim as `Authorization: Bearer`. The SDK never decodes or refreshes it;
25
+ * rotate it with {@link DatashipsClient.setToken}, or pass a
26
+ * {@link TokenProviderAuth} instead when the platform hands out short-lived
27
+ * tokens on demand.
28
+ */
29
+ interface PublicKeyAuth {
30
+ publicKey: string;
31
+ token: string;
32
+ getToken?: never;
33
+ }
34
+ /**
35
+ * Browser credentials whose token is fetched before every request, retries and
36
+ * heartbeats included, for platforms that mint short-lived tokens on demand
37
+ * (Shopify's `shopify.sessionToken.get()`). The SDK never caches the result.
38
+ * Exclusive with {@link PublicKeyAuth.token}; `setToken()` does not apply.
39
+ */
40
+ interface TokenProviderAuth {
41
+ publicKey: string;
42
+ getToken: () => string | Promise<string>;
43
+ token?: never;
44
+ }
45
+ /**
46
+ * Server-to-server credentials: the merchant's server key alone (`X-Api-Key`,
47
+ * no token). Only accepted by `DatashipsServerClient` from `@dataships/sdk/server`.
48
+ */
49
+ interface ServerKeyAuth {
50
+ serverKey: string;
51
+ }
52
+ type CustomerApiAuth = PublicKeyAuth | ServerKeyAuth;
53
+ /**
54
+ * UNSAFE escape hatch for a merchant with no server to mint tokens: the server
55
+ * key itself in the page, sent alone as `X-Api-Key`. Anyone who views source
56
+ * can then open sessions, save consent and submit orders as that merchant
57
+ * until the key is revoked; revocation is the only remedy. The SDK warns once
58
+ * at construction and tags every request so the exposure is never silent.
59
+ * Prefer `PublicKeyAuth` wherever a token can be minted.
60
+ */
61
+ interface UnsafeServerKeyAuth {
62
+ unsafeServerKey: string;
63
+ }
64
+ /** Credentials the browser client accepts: the safe pair (fixed or provided token), or the explicit unsafe escape hatch. */
65
+ type BrowserAuth = PublicKeyAuth | TokenProviderAuth | UnsafeServerKeyAuth;
66
+ interface UpsertSessionInput {
67
+ /** The only channel-related input: PHONE covers SMS and WhatsApp, the server picks the channels. */
68
+ identifierType: IdentifierType;
69
+ /** Contact for that type. Omit (or pass null) to let the adapter resolve the server-known value. */
70
+ identifier?: string | null;
71
+ /** Trusted as the geolocation source when present; otherwise the connection IP is used. */
72
+ userIp?: string;
73
+ /**
74
+ * How the copy comes back: `json` (the API default) leaves plain strings to
75
+ * format client-side, `html` applies links and emphasis to `text` and
76
+ * `consentDescription`.
77
+ */
78
+ format?: ConsentCopyFormat;
79
+ }
80
+ type ConsentCopyFormat = "json" | "html";
81
+ /**
82
+ * Server-side upsert input: the connection IP is the merchant's server, so the
83
+ * user's IP must always be supplied for geolocation.
84
+ */
85
+ interface ServerUpsertSessionInput extends UpsertSessionInput {
86
+ userIp: string;
87
+ }
88
+ /** Widget copy for one channel. Every field may be null; render what is present. */
89
+ interface ConsentWidgetConfig {
90
+ text: string | null;
91
+ consentDescription: string | null;
92
+ marketingInformation: string | null;
93
+ policyURL: string | null;
94
+ mobileTermsOfServiceURL: string | null;
95
+ privacyEmail: string | null;
96
+ /**
97
+ * What the copy's placeholders resolve to; null when it uses none. In `json` copy,
98
+ * render `{{MARKETING_PREFERENCES}}` as the marketing modal link showing
99
+ * `MARKETING_PREFERENCES` as is. `html` copy has it rendered already.
100
+ */
101
+ placeholderValues?: WidgetCopyPlaceholderValues | null;
102
+ }
103
+ interface WidgetCopyPlaceholderValues {
104
+ MARKETING_PREFERENCES?: string;
105
+ }
106
+ /** One channel that renders a widget: its consent record plus the copy. */
107
+ interface ConsentItem {
108
+ channel: Channel;
109
+ consentType: ConsentType;
110
+ checked: boolean;
111
+ widgetConfig: ConsentWidgetConfig;
112
+ }
113
+ /**
114
+ * `POST /v1/consent-session` response. An empty `consents` means there is
115
+ * nothing to render; `consentSessionId` is null only in that normalised
116
+ * nothing-to-render case (see `upsertSession`).
117
+ */
118
+ interface UpsertSessionResponse {
119
+ consentSessionId: string | null;
120
+ consents: ConsentItem[];
121
+ /** Phone sessions only: whether the user must verify with a one-time code. Null for EMAIL. */
122
+ userVerificationRequired: boolean | null;
123
+ lastHeartbeat: string | null;
124
+ /**
125
+ * The identifier the session holds for the requested type, so a widget that
126
+ * lost what the user typed (a reload, a remount) can refill its field. Null
127
+ * while the session has none; absent from a core-api that predates it.
128
+ */
129
+ identifier?: string | null;
130
+ }
131
+ /**
132
+ * Platform adapter for the `/v1/consent-session` protocol. It declares the
133
+ * `integration.type` discriminator, resolves the top-level identifier and
134
+ * returns the integration-specific fields from `getSessionContext()` (the
135
+ * client injects `type` under `integration` and `locale` at the top level; the
136
+ * merchant comes from the `X-Api-Key` header, never from the body).
137
+ */
138
+ interface ConsentSessionAdapter<TContext = unknown> {
139
+ /**
140
+ * Adapter identifier reported via `X-Dataships-App` (e.g. "SalesforceAdapter").
141
+ * Used to distinguish which SDK adapter sourced the request.
142
+ */
143
+ readonly name: string;
144
+ /**
145
+ * Always `"consent-session"`, the only protocol the SDK speaks. Kept as a
146
+ * constant marker because integrations check it to recognise an SDK adapter.
147
+ */
148
+ readonly sessionApi: "consent-session";
149
+ /** ISO 639-1 locale code from the SupportedLocale union. */
150
+ getLocale(): SupportedLocale;
151
+ /** The integration-specific fields merged under `integration` on every upsert. */
152
+ getSessionContext(): Promise<TContext>;
153
+ /**
154
+ * When true, the client serves synthetic responses instead of making live
155
+ * network calls: `upsertSession` returns
156
+ * {@link getPreviewUpsertResponse}; `saveConsent`, the heartbeat, the OTP
157
+ * calls and `submitSession` resolve without a request. No credentials are
158
+ * needed. Used to style a widget in an editor or sandbox without hitting the
159
+ * API. Defaults to live behaviour when absent.
160
+ */
161
+ previewMode?: boolean;
162
+ /** Synthetic upsert response served when {@link previewMode} is true, for the requested identifier type. */
163
+ getPreviewUpsertResponse?(identifierType: IdentifierType): UpsertSessionResponse;
164
+ /** `integration.type` discriminator, e.g. "SALESFORCE_SNIPPET". */
165
+ readonly integrationType: string;
166
+ /**
167
+ * Resolves the top-level `identifier` / `identifierType` for `upsertSession`
168
+ * from the adapter's own data. An explicit `upsertSession({ identifier })`
169
+ * takes precedence, e.g. an email the buyer types mid-checkout.
170
+ */
171
+ getIdentifier(identifierType: IdentifierType): ConsentSessionIdentifier | Promise<ConsentSessionIdentifier>;
172
+ /**
173
+ * The `datashipsSessionId` beacon the adapter sent on its upserts, when it
174
+ * owns one. `submitSession` uses it as the default id to complete, so a
175
+ * browser integration never has to carry the beacon itself. Adapters that
176
+ * have no beacon (server-side, Salesforce) leave this undefined and callers
177
+ * pass `datashipsSessionId` explicitly.
178
+ */
179
+ getDatashipsSessionId?(): string;
180
+ /**
181
+ * Discard the beacon after a completed submit so the next collection on this
182
+ * browser opens a new session instead of touching the completed one.
183
+ */
184
+ resetDatashipsSessionId?(): void;
185
+ }
186
+ /**
187
+ * The shopper as the merchant knows them at completion. Both names are
188
+ * required and non-blank (core-api validates `NotBlank`). Contact details never
189
+ * travel here: the identifier is what the widget collected on the upsert.
190
+ */
191
+ interface SubmitConsentSessionUser {
192
+ firstName: string;
193
+ lastName: string;
194
+ }
195
+ /**
196
+ * `integration` details of a `CUSTOM_PLATFORM_CHECKOUT` submit: the order the
197
+ * shopper placed. Amounts are in the merchant integration's currency.
198
+ */
199
+ interface CustomPlatformCheckoutSubmitDetails {
200
+ /** Merchant order id (max 255). */
201
+ id: string;
202
+ /** Order total as a number, zero allowed. */
203
+ total: number;
204
+ /** Billing country, ISO 3166-1 alpha-2; drives the legal basis. Case-insensitive here, sent uppercased. */
205
+ billingCountryCode: string;
206
+ /** When the order was placed; defaults server-side to the time the submit arrives. */
207
+ submittedAt?: string | Date;
208
+ }
209
+ /**
210
+ * Input to `submitSession`: the completion signal for every session opened
211
+ * under one beacon. `details` is the flow-specific payload merged under
212
+ * `integration` next to the adapter's `type`; for a checkout that is the
213
+ * order ({@link CustomPlatformCheckoutSubmitDetails}).
214
+ */
215
+ interface SubmitSessionInput<TDetails = CustomPlatformCheckoutSubmitDetails> {
216
+ /**
217
+ * The beacon sent on the upserts. Browser clients default to the adapter's
218
+ * own beacon; the server client requires it, since a server holds none.
219
+ */
220
+ datashipsSessionId?: string;
221
+ /** The shopper's names; required by core-api. */
222
+ user: SubmitConsentSessionUser;
223
+ details: TDetails;
224
+ /**
225
+ * Discard the adapter's beacon once the submit is accepted, so the next
226
+ * collection on this browser starts a new session. Defaults to true; has no
227
+ * effect when `datashipsSessionId` was passed explicitly.
228
+ */
229
+ resetBeacon?: boolean;
230
+ }
231
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
232
+
233
+ export { type BrowserAuth as B, type ConsentSessionAdapter as C, type Environment as E, type FetchLike as F, type IdentifierType as I, type PublicKeyAuth as P, type SupportedLocale as S, type TokenProviderAuth as T, type UpsertSessionResponse as U, type WidgetCopyPlaceholderValues as W, type ConsentSessionIdentifier as a, type UpsertSessionInput as b, type Channel as c, type SessionIds as d, type SubmitSessionInput as e, type CustomerApiAuth as f, type UnsafeServerKeyAuth as g, type ConsentCopyFormat as h, type ConsentItem as i, type ConsentType as j, type ConsentWidgetConfig as k, type CustomPlatformCheckoutSubmitDetails as l, SUPPORTED_LOCALES as m, type ServerKeyAuth as n, type SubmitConsentSessionUser as o, type ServerUpsertSessionInput as p };
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "@dataships/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.0",
4
+ "description": "Dataships client SDK — transport and consent-session lifecycle for the Dataships consent-session API. Platform-agnostic core with adapters per integration (Shopify checkout, Salesforce, custom platform, …).",
5
+ "private": false,
6
+ "license": "Apache-2.0",
7
+ "author": "Dataships",
8
+ "homepage": "https://dataships.io",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://gitlab.com/dataships/platform/dataships-sdk.git"
12
+ },
13
+ "type": "module",
14
+ "sideEffects": false,
15
+ "files": [
16
+ "dist",
17
+ "LICENSE",
18
+ "README.md"
19
+ ],
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "unpkg": "./dist/dataships.min.js",
23
+ "jsdelivr": "./dist/dataships.min.js",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ },
29
+ "./server": {
30
+ "types": "./dist/server.d.ts",
31
+ "import": "./dist/server.js"
32
+ },
33
+ "./adapters/shopify-checkout": {
34
+ "types": "./dist/adapters/shopify-checkout/index.d.ts",
35
+ "import": "./dist/adapters/shopify-checkout/index.js"
36
+ },
37
+ "./adapters/salesforce": {
38
+ "types": "./dist/adapters/salesforce/index.d.ts",
39
+ "import": "./dist/adapters/salesforce/index.js"
40
+ },
41
+ "./adapters/custom-platform-checkout": {
42
+ "types": "./dist/adapters/custom-platform-checkout/index.d.ts",
43
+ "import": "./dist/adapters/custom-platform-checkout/index.js"
44
+ },
45
+ "./adapters/custom-platform-signup": {
46
+ "types": "./dist/adapters/custom-platform-signup/index.d.ts",
47
+ "import": "./dist/adapters/custom-platform-signup/index.js"
48
+ },
49
+ "./package.json": "./package.json"
50
+ },
51
+ "scripts": {
52
+ "build": "rm -rf dist && tsup && node scripts/integrity.mjs",
53
+ "dev": "tsup --watch",
54
+ "typecheck": "tsc --noEmit",
55
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
56
+ "test": "vitest",
57
+ "test:once": "vitest run",
58
+ "test:ci": "vitest run --coverage.enabled --reporter=junit --outputFile=./junit.xml",
59
+ "prepublishOnly": "yarn build",
60
+ "check:package": "sh scripts/check-package.sh",
61
+ "smoke:cdn": "node scripts/smoke-cdn.mjs",
62
+ "verify:npm": "sh scripts/verify-npm.sh"
63
+ },
64
+ "devDependencies": {
65
+ "@arethetypeswrong/cli": "^0.18.5",
66
+ "@types/node": "^20.12.11",
67
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
68
+ "@typescript-eslint/parser": "^7.0.0",
69
+ "@vitest/coverage-v8": "^3.2.6",
70
+ "eslint": "^8.57.0",
71
+ "jsdom": "^24.0.0",
72
+ "msw": "^2.4.0",
73
+ "publint": "^0.3.25",
74
+ "tsup": "^8.3.0",
75
+ "typescript": "^5.4.0",
76
+ "vitest": "^3.2.6"
77
+ },
78
+ "packageManager": "yarn@1.22.22",
79
+ "engines": {
80
+ "node": ">=20"
81
+ },
82
+ "publishConfig": {
83
+ "access": "public"
84
+ }
85
+ }