@settlemint/dalp-sdk 3.1.19-main.36458349696 → 3.1.19-main.36466375852
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 +43 -8
- package/dist/auth/client.d.ts +4 -0
- package/dist/auth/native-passkey.d.ts +9 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js +139 -11
- package/dist/{chunk-wp4269cm.js → chunk-4f4kjtt0.js} +33 -5
- package/dist/{chunk-3mxexapc.js → chunk-7a9p5r3x.js} +9 -2
- package/dist/{chunk-te5m0f24.js → chunk-hgz10qvm.js} +1 -1
- package/dist/{chunk-yr7kp37j.js → chunk-szp73yj8.js} +45 -20
- package/dist/effect.js +2 -2
- package/dist/errors/error-codes.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -6
- package/dist/proxy/sse.d.ts +1 -0
- package/dist/proxy/transport.d.ts +3 -1
- package/dist/react-query.js +2 -2
- package/dist/routes/console.js +3 -3
- package/dist/runtime.d.ts +11 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -35,10 +35,13 @@ mechanism, the acting-context headers (`organizationId`, `participant`,
|
|
|
35
35
|
`If-None-Match` reuse, custom fetch injection, the settlement bound, and abort
|
|
36
36
|
propagation.
|
|
37
37
|
|
|
38
|
-
**An API key cannot reach a browser.** `createDalpClient`
|
|
39
|
-
construction when a key is passed with
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
**An API key cannot reach a browser or an app.** `createDalpClient` and
|
|
39
|
+
`createDalpAuthClient` throw at construction when a key is passed with
|
|
40
|
+
`runtime: "browser"` or `runtime: "react-native"`, rather than at the first
|
|
41
|
+
request. A key carries the
|
|
42
|
+
full authority of the identity it belongs to, and anything bundled into a web
|
|
43
|
+
page or an app ships to every user. There the client authenticates with the
|
|
44
|
+
session cookie instead.
|
|
42
45
|
|
|
43
46
|
### Dispatch by route id
|
|
44
47
|
|
|
@@ -151,8 +154,9 @@ import { createDalpAuthClient } from "@settlemint/dalp-sdk/auth";
|
|
|
151
154
|
|
|
152
155
|
The route surface already carries every auth REST route. `./auth` covers what a
|
|
153
156
|
REST call cannot express: the session and cookie state machine, plugin actions
|
|
154
|
-
that must run
|
|
155
|
-
through `navigator.credentials
|
|
157
|
+
that must run on the device (`passkeyClient` drives the WebAuthn ceremony
|
|
158
|
+
through `navigator.credentials`, or through the `passkey` ceremony a React
|
|
159
|
+
Native app passes), and the access-control model that evaluates a
|
|
156
160
|
permission locally instead of round-tripping. It sits off the package root
|
|
157
161
|
because it costs roughly 70 KiB of Better Auth that a consumer who only calls
|
|
158
162
|
routes should not pay.
|
|
@@ -176,6 +180,37 @@ Every other auth route answers a request that carries a key with HTTP 403 and
|
|
|
176
180
|
the code `API_KEY_NOT_SUPPORTED_ON_ROUTE`. Use a session cookie for those
|
|
177
181
|
routes. The API key guide in the DALP API reference has the details.
|
|
178
182
|
|
|
183
|
+
## React Native
|
|
184
|
+
|
|
185
|
+
Supported: Expo SDK 57 (React Native 0.86, React 19.2), with `fetch` from
|
|
186
|
+
`expo/fetch` passed to both clients. The SDK detects React Native from
|
|
187
|
+
`navigator.product` and then keeps the session in its own cookie store, sends
|
|
188
|
+
`credentials: "omit"`, refuses an API key, and sends the platform's origin as
|
|
189
|
+
`Origin` on every call.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { createCookieStoreFetch, createDalpClient, makeCookieStore } from "@settlemint/dalp-sdk";
|
|
193
|
+
import { createDalpAuthClient } from "@settlemint/dalp-sdk/auth";
|
|
194
|
+
import { fetch } from "expo/fetch";
|
|
195
|
+
|
|
196
|
+
const cookieStore = makeCookieStore((await SecureStore.getItemAsync("dalp.session")) ?? "");
|
|
197
|
+
const auth = await createDalpAuthClient({ url, cookieStore, fetch, passkey: ceremony });
|
|
198
|
+
const dalp = createDalpClient({ baseUrl: url, fetch: createCookieStoreFetch(cookieStore, fetch) });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- React Native's built-in `fetch` works for calls but cannot read a stream; a
|
|
202
|
+
stream on it fails with `sdk.stream.UNREADABLE_BODY` before the request is
|
|
203
|
+
sent. Use `expo/fetch`.
|
|
204
|
+
- React Native has no WebAuthn. Pass a `passkey` ceremony (`DalpPasskeyCeremony`:
|
|
205
|
+
`authenticate` and `register`, each taking the platform's options JSON and
|
|
206
|
+
returning the credential JSON) backed by a native passkey module.
|
|
207
|
+
- Persist `cookieStore.header()` in secure storage yourself; the SDK holds it in
|
|
208
|
+
memory only.
|
|
209
|
+
|
|
210
|
+
The SDK's tests run this wiring headless on the fetch and abort polyfills React
|
|
211
|
+
Native ships and a model of `expo/fetch`. They do not run on Hermes, a
|
|
212
|
+
simulator, or a device.
|
|
213
|
+
|
|
179
214
|
## Bundler and paymaster
|
|
180
215
|
|
|
181
216
|
Use JSON-RPC 2.0 over `POST https://dalp.example.com/bundler/{chainId}` for
|
|
@@ -233,5 +268,5 @@ calls routes never pays for auth, TanStack Query, or webhook code.
|
|
|
233
268
|
|
|
234
269
|
The integrator guide lives under Developer reference in the platform docs:
|
|
235
270
|
install and credentials, calling routes, mutations and settlement, streaming,
|
|
236
|
-
TanStack Query, server rendering in TanStack Start and Next.js,
|
|
237
|
-
surface.
|
|
271
|
+
TanStack Query, server rendering in TanStack Start and Next.js, React Native,
|
|
272
|
+
and the Effect surface.
|
package/dist/auth/client.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { DalpAuthSurface } from "../auth-surface.ts";
|
|
|
3
3
|
import type { AuthGetApiKeyListResponse, AuthGetOrganizationListResponse, AuthPostApiKeyCreateData, AuthPostApiKeyCreateResponse, AuthPostApiKeyDeleteData, AuthPostApiKeyDeleteResponse, AuthPostOrganizationCreateData, AuthPostOrganizationCreateResponse, AuthPostOrganizationRemoveMemberData, AuthPostOrganizationRemoveMemberResponse, AuthPostRevokeOtherSessionsResponse, AuthPostRevokeSessionData, AuthPostRevokeSessionResponse, AuthPostTwoFactorDisableData, AuthPostTwoFactorDisableResponse, AuthPostTwoFactorEnableData, AuthPostTwoFactorEnableResponse, AuthPostTwoFactorVerifyBackupCodeData, AuthPostTwoFactorVerifyBackupCodeResponse, AuthPostTwoFactorVerifyTotpData, AuthPostTwoFactorVerifyTotpResponse, CreateOrganizationInvitationData, CreateOrganizationInvitationResponse, GetSessionResponse, ListUserSessionsResponse, Member, SetActiveOrganizationData, SetActiveOrganizationResponse } from "../generated/api-types.d.ts";
|
|
4
4
|
import { Secret } from "../secret.ts";
|
|
5
5
|
import type { CookieStoreShape } from "../transport/cookie-store.ts";
|
|
6
|
+
import type { DalpRuntime } from "../runtime.ts";
|
|
7
|
+
import type { DalpPasskeyCeremony } from "./native-passkey.ts";
|
|
6
8
|
export interface DalpAuthClientConfig {
|
|
7
9
|
readonly url: string;
|
|
8
10
|
readonly apiKey?: string;
|
|
@@ -12,6 +14,8 @@ export interface DalpAuthClientConfig {
|
|
|
12
14
|
readonly authSurface?: DalpAuthSurface;
|
|
13
15
|
readonly headers?: Record<string, string>;
|
|
14
16
|
readonly fetch?: DalpFetch;
|
|
17
|
+
readonly runtime?: DalpRuntime;
|
|
18
|
+
readonly passkey?: DalpPasskeyCeremony;
|
|
15
19
|
}
|
|
16
20
|
export interface DecodedDalpAuthClientConfig extends Omit<DalpAuthClientConfig, "apiKey" | "cookie"> {
|
|
17
21
|
readonly apiKey?: Secret;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { passkeyClient, type AuthenticationResponseJSON, type PublicKeyCredentialCreationOptionsJSON, type PublicKeyCredentialRequestOptionsJSON, type RegistrationResponseJSON } from "@better-auth/passkey/client";
|
|
2
|
+
export interface DalpPasskeyCeremony {
|
|
3
|
+
readonly authenticate: (options: PublicKeyCredentialRequestOptionsJSON) => Promise<AuthenticationResponseJSON>;
|
|
4
|
+
readonly register: (options: PublicKeyCredentialCreationOptionsJSON) => Promise<RegistrationResponseJSON>;
|
|
5
|
+
}
|
|
6
|
+
type PasskeyClientPlugin = ReturnType<typeof passkeyClient>;
|
|
7
|
+
/** @internal */
|
|
8
|
+
export declare function ceremonyPasskeyClient(ceremony: DalpPasskeyCeremony): PasskeyClientPlugin;
|
|
9
|
+
export {};
|
package/dist/auth.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ export type { DalpAuthClient } from "./auth/live.ts";
|
|
|
3
3
|
export { decodeDalpAuthClientConfig } from "./auth/client.ts";
|
|
4
4
|
export type { DalpAuthSurface } from "./auth-surface.ts";
|
|
5
5
|
export type { DalpAuthClientConfig, DalpCliAuthClient, DalpAuthError, DalpAuthOperation, DalpAuthResult, DalpOrganizationInvitation, DalpOrganizationInvitationsQuery, DalpOrganizationMember, DalpOrganizationMembersQuery, DecodedDalpAuthClientConfig, } from "./auth/client.ts";
|
|
6
|
+
export type { DalpPasskeyCeremony } from "./auth/native-passkey.ts";
|
|
7
|
+
export type { DalpRuntime } from "./runtime.ts";
|
|
6
8
|
export type { WalletAuthEndpointPath } from "./auth/wallet-plugins.ts";
|
package/dist/auth.js
CHANGED
|
@@ -6,20 +6,22 @@ import {
|
|
|
6
6
|
secret2,
|
|
7
7
|
unwrapSecret,
|
|
8
8
|
normalizeDalpBaseUrl,
|
|
9
|
+
detectDalpRuntime,
|
|
10
|
+
refuseBundledApiKey,
|
|
9
11
|
STEP_UP_CHALLENGE_HEADER,
|
|
10
12
|
STEP_UP_CHALLENGE_PATH,
|
|
11
13
|
carriesWalletProof,
|
|
12
14
|
stepUpChallengeRequest
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4f4kjtt0.js";
|
|
14
16
|
import {
|
|
15
17
|
makeCookieStore2,
|
|
16
18
|
createCookieStoreFetch2,
|
|
17
19
|
SDK_USER_AGENT2
|
|
18
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-7a9p5r3x.js";
|
|
19
21
|
|
|
20
22
|
// src/auth/live.ts
|
|
21
23
|
import { apiKeyClient } from "@better-auth/api-key/client";
|
|
22
|
-
import { passkeyClient } from "@better-auth/passkey/client";
|
|
24
|
+
import { passkeyClient as passkeyClient2 } from "@better-auth/passkey/client";
|
|
23
25
|
import {
|
|
24
26
|
createAuthClient as createBetterAuthClient
|
|
25
27
|
} from "better-auth/client";
|
|
@@ -101,6 +103,21 @@ function optionalFetch(value) {
|
|
|
101
103
|
}
|
|
102
104
|
return value;
|
|
103
105
|
}
|
|
106
|
+
function optionalRuntime(value) {
|
|
107
|
+
if (value === undefined || value === "browser" || value === "server" || value === "react-native") {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
throw new AuthConfigError("runtime", 'must be "browser", "server", or "react-native"');
|
|
111
|
+
}
|
|
112
|
+
function optionalPasskeyCeremony(value) {
|
|
113
|
+
if (value === undefined) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (typeof value !== "object" || value === null || typeof Reflect.get(value, "authenticate") !== "function" || typeof Reflect.get(value, "register") !== "function") {
|
|
117
|
+
throw new AuthConfigError("passkey", "must expose authenticate and register");
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
104
121
|
function optionalCookieStore(value) {
|
|
105
122
|
if (value === undefined) {
|
|
106
123
|
return;
|
|
@@ -124,6 +141,8 @@ function decodeAuthConfig(input) {
|
|
|
124
141
|
const authSurface = optionalAuthSurface(raw.authSurface);
|
|
125
142
|
const headers = optionalHeaders(raw.headers);
|
|
126
143
|
const configuredFetch = optionalFetch(raw.fetch);
|
|
144
|
+
const runtime = optionalRuntime(raw.runtime);
|
|
145
|
+
const passkey = optionalPasskeyCeremony(raw.passkey);
|
|
127
146
|
return {
|
|
128
147
|
url,
|
|
129
148
|
...apiKey === undefined ? {} : { apiKey: secret2(apiKey) },
|
|
@@ -132,7 +151,9 @@ function decodeAuthConfig(input) {
|
|
|
132
151
|
...organizationId === undefined ? {} : { organizationId },
|
|
133
152
|
...authSurface === undefined ? {} : { authSurface },
|
|
134
153
|
...headers === undefined ? {} : { headers },
|
|
135
|
-
...configuredFetch === undefined ? {} : { fetch: configuredFetch }
|
|
154
|
+
...configuredFetch === undefined ? {} : { fetch: configuredFetch },
|
|
155
|
+
...runtime === undefined ? {} : { runtime },
|
|
156
|
+
...passkey === undefined ? {} : { passkey }
|
|
136
157
|
};
|
|
137
158
|
}
|
|
138
159
|
async function decodeDalpAuthClientConfig(input) {
|
|
@@ -141,8 +162,8 @@ async function decodeDalpAuthClientConfig(input) {
|
|
|
141
162
|
} catch (error) {
|
|
142
163
|
throw createDalpSdkConfigurationError2({
|
|
143
164
|
message: "DALP SDK: auth client configuration is invalid.",
|
|
144
|
-
why: "The auth client received a malformed URL, credential, header map, fetch seam, or
|
|
145
|
-
fix: "Pass one credential source, an HTTP(S) URL, and valid fetch
|
|
165
|
+
why: "The auth client received a malformed URL, credential, header map, runtime, fetch seam, cookie store, or passkey ceremony.",
|
|
166
|
+
fix: "Pass one credential source, an HTTP(S) URL, a known runtime, and valid fetch, cookie-store, and passkey seams.",
|
|
146
167
|
cause: error
|
|
147
168
|
});
|
|
148
169
|
}
|
|
@@ -191,6 +212,106 @@ function withInviteStepUp(fetchImpl, baseUrl) {
|
|
|
191
212
|
};
|
|
192
213
|
}
|
|
193
214
|
|
|
215
|
+
// src/auth/native-passkey.ts
|
|
216
|
+
import {
|
|
217
|
+
passkeyClient
|
|
218
|
+
} from "@better-auth/passkey/client";
|
|
219
|
+
var ceremonyFailed = (code, error) => ({
|
|
220
|
+
data: null,
|
|
221
|
+
error: {
|
|
222
|
+
code,
|
|
223
|
+
message: error instanceof Error ? error.message : "The passkey ceremony did not complete.",
|
|
224
|
+
status: 400,
|
|
225
|
+
statusText: "BAD_REQUEST"
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
function withExtensions(options, extensions) {
|
|
229
|
+
return options.extensions === undefined && extensions === undefined ? options : { ...options, extensions: { ...options.extensions, ...extensions } };
|
|
230
|
+
}
|
|
231
|
+
function ceremonyPasskeyClient(ceremony) {
|
|
232
|
+
const plugin = passkeyClient();
|
|
233
|
+
return {
|
|
234
|
+
...plugin,
|
|
235
|
+
getActions: ($fetch, $store) => {
|
|
236
|
+
const signIn = async (opts, options) => {
|
|
237
|
+
const generated = await $fetch("/passkey/generate-authenticate-options", {
|
|
238
|
+
method: "GET",
|
|
239
|
+
throw: false
|
|
240
|
+
});
|
|
241
|
+
if (generated.data === null) {
|
|
242
|
+
return generated;
|
|
243
|
+
}
|
|
244
|
+
let response;
|
|
245
|
+
try {
|
|
246
|
+
response = await ceremony.authenticate(withExtensions(generated.data, opts?.extensions));
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return ceremonyFailed("AUTH_CANCELLED", error);
|
|
249
|
+
}
|
|
250
|
+
const { clientExtensionResults, ...responseBody } = response;
|
|
251
|
+
try {
|
|
252
|
+
const verified = await $fetch("/passkey/verify-authentication", {
|
|
253
|
+
body: { response: responseBody },
|
|
254
|
+
...opts?.fetchOptions,
|
|
255
|
+
...options,
|
|
256
|
+
method: "POST",
|
|
257
|
+
throw: false
|
|
258
|
+
});
|
|
259
|
+
$store.notify("$listPasskeys");
|
|
260
|
+
$store.notify("$sessionSignal");
|
|
261
|
+
return opts?.returnWebAuthnResponse === true ? { ...verified, webauthn: { response, clientExtensionResults } } : verified;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
return ceremonyFailed("AUTH_CANCELLED", error);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const addPasskey = async (opts, fetchOpts) => {
|
|
267
|
+
const generated = await $fetch("/passkey/generate-register-options", {
|
|
268
|
+
method: "GET",
|
|
269
|
+
query: {
|
|
270
|
+
...opts?.authenticatorAttachment === undefined ? {} : { authenticatorAttachment: opts.authenticatorAttachment },
|
|
271
|
+
...opts?.name === undefined || opts.name === "" ? {} : { name: opts.name },
|
|
272
|
+
...opts?.context === undefined || opts.context === null ? {} : { context: opts.context }
|
|
273
|
+
},
|
|
274
|
+
throw: false
|
|
275
|
+
});
|
|
276
|
+
if (generated.data === null) {
|
|
277
|
+
return generated;
|
|
278
|
+
}
|
|
279
|
+
let response;
|
|
280
|
+
try {
|
|
281
|
+
response = await ceremony.register(withExtensions(generated.data, opts?.extensions));
|
|
282
|
+
} catch (error) {
|
|
283
|
+
return ceremonyFailed("REGISTRATION_CANCELLED", error);
|
|
284
|
+
}
|
|
285
|
+
const { clientExtensionResults, ...responseBody } = response;
|
|
286
|
+
try {
|
|
287
|
+
const verified = await $fetch("/passkey/verify-registration", {
|
|
288
|
+
...opts?.fetchOptions,
|
|
289
|
+
...fetchOpts,
|
|
290
|
+
body: {
|
|
291
|
+
response: responseBody,
|
|
292
|
+
name: opts?.name,
|
|
293
|
+
...opts?.createSession === true ? { createSession: true } : {}
|
|
294
|
+
},
|
|
295
|
+
method: "POST",
|
|
296
|
+
throw: false
|
|
297
|
+
});
|
|
298
|
+
if (verified.data === null) {
|
|
299
|
+
return verified;
|
|
300
|
+
}
|
|
301
|
+
$store.notify("$listPasskeys");
|
|
302
|
+
if (verified.data.session !== undefined) {
|
|
303
|
+
$store.notify("$sessionSignal");
|
|
304
|
+
}
|
|
305
|
+
return opts?.returnWebAuthnResponse === true ? { ...verified, webauthn: { response, clientExtensionResults } } : verified;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
return ceremonyFailed("UNKNOWN_ERROR", error);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
return { signIn: { passkey: signIn }, passkey: { addPasskey }, $Infer: {} };
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
194
315
|
// src/auth/permissions.ts
|
|
195
316
|
import { createAccessControl } from "better-auth/plugins/access";
|
|
196
317
|
import { adminAc, defaultStatements as adminDefaultStatements } from "better-auth/plugins/admin/access";
|
|
@@ -330,11 +451,14 @@ var organizationPluginOptions = {
|
|
|
330
451
|
roles: { owner: ownerRole, member: memberRole }
|
|
331
452
|
};
|
|
332
453
|
var reservedHeaders = new Set(["x-api-key", "authorization", "cookie"]);
|
|
333
|
-
function authHeaders(config) {
|
|
454
|
+
function authHeaders(config, runtime, baseUrl) {
|
|
334
455
|
const headers = { "User-Agent": SDK_USER_AGENT2 };
|
|
456
|
+
if (runtime === "react-native") {
|
|
457
|
+
headers.origin = new URL(baseUrl).origin;
|
|
458
|
+
}
|
|
335
459
|
for (const [name, value] of Object.entries(config.headers ?? {})) {
|
|
336
460
|
if (!reservedHeaders.has(name.toLowerCase())) {
|
|
337
|
-
headers[name] = value;
|
|
461
|
+
headers[name.toLowerCase() === "origin" ? "origin" : name] = value;
|
|
338
462
|
}
|
|
339
463
|
}
|
|
340
464
|
if (config.apiKey !== undefined) {
|
|
@@ -354,7 +478,7 @@ function createConfiguredBetterAuthClient(config, cookieStore) {
|
|
|
354
478
|
inferAdditionalFields(),
|
|
355
479
|
adminClient(adminPluginOptions),
|
|
356
480
|
apiKeyClient(),
|
|
357
|
-
|
|
481
|
+
config.passkey === undefined ? passkeyClient2() : ceremonyPasskeyClient(config.passkey),
|
|
358
482
|
walletPincodeClient(),
|
|
359
483
|
walletTwoFactorClient(),
|
|
360
484
|
walletSecretCodesClient(),
|
|
@@ -363,12 +487,16 @@ function createConfiguredBetterAuthClient(config, cookieStore) {
|
|
|
363
487
|
organizationClient(organizationPluginOptions)
|
|
364
488
|
];
|
|
365
489
|
const baseUrl = normalizeDalpBaseUrl(config.url);
|
|
490
|
+
const runtime = config.runtime ?? detectDalpRuntime();
|
|
491
|
+
if (config.apiKey !== undefined) {
|
|
492
|
+
refuseBundledApiKey(runtime);
|
|
493
|
+
}
|
|
366
494
|
return createBetterAuthClient({
|
|
367
495
|
baseURL: `${baseUrl}/api/auth`,
|
|
368
496
|
fetchOptions: {
|
|
369
|
-
headers: authHeaders(config),
|
|
497
|
+
headers: authHeaders(config, runtime, baseUrl),
|
|
370
498
|
customFetchImpl: withInviteStepUp(createCookieStoreFetch2(cookieStore, config.fetch), baseUrl),
|
|
371
|
-
credentials: "include"
|
|
499
|
+
credentials: runtime === "react-native" ? "omit" : "include"
|
|
372
500
|
},
|
|
373
501
|
plugins
|
|
374
502
|
});
|
|
@@ -63,6 +63,35 @@ function normalizeDalpBaseUrl(url) {
|
|
|
63
63
|
return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// src/runtime.ts
|
|
67
|
+
var REACT_NATIVE_NAVIGATOR_PRODUCT = "ReactNative";
|
|
68
|
+
function detectDalpRuntime() {
|
|
69
|
+
if (Reflect.get(globalThis, "navigator") !== undefined && Reflect.get(navigator, "product") === REACT_NATIVE_NAVIGATOR_PRODUCT) {
|
|
70
|
+
return "react-native";
|
|
71
|
+
}
|
|
72
|
+
return globalThis.window === undefined ? "server" : "browser";
|
|
73
|
+
}
|
|
74
|
+
function runtimeCredentials(runtime) {
|
|
75
|
+
return runtime === "browser" ? "include" : "omit";
|
|
76
|
+
}
|
|
77
|
+
var WHATWG_FETCH_POLYFILL_FLAG = "polyfill";
|
|
78
|
+
function isBufferingFetch(fetchImpl) {
|
|
79
|
+
return typeof fetchImpl === "function" && Reflect.get(fetchImpl, WHATWG_FETCH_POLYFILL_FLAG) === true;
|
|
80
|
+
}
|
|
81
|
+
function keepBufferingMarker(wrapped, wrapper) {
|
|
82
|
+
return isBufferingFetch(wrapped) ? Object.assign(wrapper, { [WHATWG_FETCH_POLYFILL_FLAG]: true }) : wrapper;
|
|
83
|
+
}
|
|
84
|
+
function refuseBundledApiKey(runtime) {
|
|
85
|
+
if (runtime === "server") {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
throw createDalpSdkConfigurationError2({
|
|
89
|
+
message: `DALP SDK: API keys are server/CLI-only credentials and cannot configure a ${runtime} client.`,
|
|
90
|
+
why: "An API key placed in a browser or app bundle ships the credential to every visitor or device.",
|
|
91
|
+
fix: "Keep the API key on the server or CLI process; browser and app clients authenticate with the session cookie."
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
66
95
|
// ../kernel/json/src/step-up-payload-text.ts
|
|
67
96
|
var PROOF_FIELD = "walletVerification";
|
|
68
97
|
var isAbsent = (value) => value === undefined || typeof value === "function" || typeof value === "symbol";
|
|
@@ -99,12 +128,12 @@ var canonicalText = (value, omitted = null) => {
|
|
|
99
128
|
var stepUpPayloadText = (body) => canonicalText(body, PROOF_FIELD);
|
|
100
129
|
|
|
101
130
|
// src/client/step-up-challenge.ts
|
|
131
|
+
import { sha256, stringToBytes } from "viem/utils";
|
|
102
132
|
var STEP_UP_CHALLENGE_HEADER = "X-Step-Up-Challenge";
|
|
103
133
|
var WALLET_VERIFICATION_HEADER = "X-Wallet-Verification";
|
|
104
134
|
var STEP_UP_CHALLENGE_PATH = "/api/v2/step-up-challenges";
|
|
105
135
|
var PROOF_FIELD2 = "walletVerification";
|
|
106
|
-
var
|
|
107
|
-
var HEX_BYTE_WIDTH = 2;
|
|
136
|
+
var HEX_PREFIX = "0x";
|
|
108
137
|
function carriesWalletProof(body) {
|
|
109
138
|
if (body === null || typeof body !== "object" || Array.isArray(body)) {
|
|
110
139
|
return false;
|
|
@@ -113,9 +142,8 @@ function carriesWalletProof(body) {
|
|
|
113
142
|
return proof !== null && proof !== undefined;
|
|
114
143
|
}
|
|
115
144
|
async function stepUpChallengeRequest(operation, target, body) {
|
|
116
|
-
const
|
|
117
|
-
const payloadDigest = Array.from(new Uint8Array(digest), (byte) => byte.toString(HEX_RADIX).padStart(HEX_BYTE_WIDTH, "0")).join("");
|
|
145
|
+
const payloadDigest = sha256(stringToBytes(stepUpPayloadText(body))).slice(HEX_PREFIX.length);
|
|
118
146
|
return { operation, target, payloadDigest };
|
|
119
147
|
}
|
|
120
148
|
|
|
121
|
-
export { DALP_AUTH_SURFACE_HEADER2, SECRET_REDACTION_MARKER2, Secret2, secret2, unwrapSecret, normalizeDalpBaseUrl, STEP_UP_CHALLENGE_HEADER, WALLET_VERIFICATION_HEADER, STEP_UP_CHALLENGE_PATH, carriesWalletProof, stepUpChallengeRequest };
|
|
149
|
+
export { DALP_AUTH_SURFACE_HEADER2, SECRET_REDACTION_MARKER2, Secret2, secret2, unwrapSecret, normalizeDalpBaseUrl, detectDalpRuntime, runtimeCredentials, isBufferingFetch, keepBufferingMarker, refuseBundledApiKey, STEP_UP_CHALLENGE_HEADER, WALLET_VERIFICATION_HEADER, STEP_UP_CHALLENGE_PATH, carriesWalletProof, stepUpChallengeRequest };
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
keepBufferingMarker
|
|
3
|
+
} from "./chunk-4f4kjtt0.js";
|
|
4
|
+
|
|
1
5
|
// src/transport/cookie-store.ts
|
|
2
6
|
var parseCookieHeader = (header) => {
|
|
3
7
|
const cookies = new Map;
|
|
@@ -87,7 +91,7 @@ var splitCombinedSetCookie = (value) => {
|
|
|
87
91
|
return cookies;
|
|
88
92
|
};
|
|
89
93
|
function createCookieStoreFetch2(cookieStore, fetchImpl = globalThis.fetch) {
|
|
90
|
-
|
|
94
|
+
const cookieStoreFetch = async (input, init) => {
|
|
91
95
|
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
92
96
|
if (!cookieStore.isEmpty() && !headers.has("cookie")) {
|
|
93
97
|
headers.set("cookie", cookieStore.header());
|
|
@@ -98,11 +102,12 @@ function createCookieStoreFetch2(cookieStore, fetchImpl = globalThis.fetch) {
|
|
|
98
102
|
}
|
|
99
103
|
return response;
|
|
100
104
|
};
|
|
105
|
+
return keepBufferingMarker(fetchImpl, cookieStoreFetch);
|
|
101
106
|
}
|
|
102
107
|
// package.json
|
|
103
108
|
var package_default = {
|
|
104
109
|
name: "@settlemint/dalp-sdk",
|
|
105
|
-
version: "3.1.19-main.
|
|
110
|
+
version: "3.1.19-main.36466375852",
|
|
106
111
|
private: false,
|
|
107
112
|
description: "Fully typed SDK for the DALP tokenization platform API",
|
|
108
113
|
homepage: "https://settlemint.com",
|
|
@@ -229,10 +234,12 @@ var package_default = {
|
|
|
229
234
|
"@tools/typescript-config": "workspace:*",
|
|
230
235
|
"@tools/vitest-config": "workspace:*",
|
|
231
236
|
"@types/bun": "1.4.2",
|
|
237
|
+
"abort-controller": "3.0.0",
|
|
232
238
|
effect: "4.0.0-rc.117",
|
|
233
239
|
publint: "0.3.24",
|
|
234
240
|
typescript: "7.0.2",
|
|
235
241
|
vitest: "5.0.1",
|
|
242
|
+
"whatwg-fetch": "3.6.20",
|
|
236
243
|
zod: "4.6.5"
|
|
237
244
|
},
|
|
238
245
|
peerDependencies: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
DalpSdkError2
|
|
3
|
-
createDalpSdkConfigurationError2
|
|
2
|
+
DalpSdkError2
|
|
4
3
|
} from "./chunk-k3ntysxx.js";
|
|
5
4
|
import {
|
|
6
5
|
DALP_AUTH_SURFACE_HEADER2,
|
|
@@ -8,12 +7,16 @@ import {
|
|
|
8
7
|
secret2,
|
|
9
8
|
unwrapSecret,
|
|
10
9
|
normalizeDalpBaseUrl,
|
|
10
|
+
detectDalpRuntime,
|
|
11
|
+
runtimeCredentials,
|
|
12
|
+
isBufferingFetch,
|
|
13
|
+
refuseBundledApiKey,
|
|
11
14
|
STEP_UP_CHALLENGE_HEADER,
|
|
12
15
|
WALLET_VERIFICATION_HEADER,
|
|
13
16
|
STEP_UP_CHALLENGE_PATH,
|
|
14
17
|
carriesWalletProof,
|
|
15
18
|
stepUpChallengeRequest
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-4f4kjtt0.js";
|
|
17
20
|
|
|
18
21
|
// src/errors/find-current-error.ts
|
|
19
22
|
var readProperty = (target, key) => Reflect.get(target, key);
|
|
@@ -142,6 +145,7 @@ var SDK_LOCAL_ERROR_CODES = new Set([
|
|
|
142
145
|
"sdk.transport.UNVERIFIED_ERROR_CODE",
|
|
143
146
|
"sdk.response.MALFORMED_SUCCESS",
|
|
144
147
|
"sdk.stream.FAILED",
|
|
148
|
+
"sdk.stream.UNREADABLE_BODY",
|
|
145
149
|
"sdk.scalar.BIG_DECIMAL_INVALID",
|
|
146
150
|
"sdk.scalar.EVM_ADDRESS_INVALID",
|
|
147
151
|
"sdk.scalar.ORGANIZATION_ID_INVALID",
|
|
@@ -519,10 +523,10 @@ async function captureSuccessfulGet(store, key, response) {
|
|
|
519
523
|
store.delete(key);
|
|
520
524
|
return response;
|
|
521
525
|
}
|
|
522
|
-
const body =
|
|
526
|
+
const body = await response.blob();
|
|
523
527
|
const headers = new Headers(response.headers);
|
|
524
528
|
headers.set("etag", etag);
|
|
525
|
-
if (body.
|
|
529
|
+
if (body.size <= CONDITIONAL_RESPONSE_MAX_BODY_BYTES) {
|
|
526
530
|
remember(store, key, { body, etag, headers });
|
|
527
531
|
}
|
|
528
532
|
return new Response(body, {
|
|
@@ -570,7 +574,7 @@ function withConditionalRequests(base) {
|
|
|
570
574
|
const retried = await base(requestWithoutIfNoneMatch(sent));
|
|
571
575
|
return retried.status === 304 ? retried : captureSuccessfulGet(state.store, key, retried);
|
|
572
576
|
}
|
|
573
|
-
return new Response(
|
|
577
|
+
return new Response(entry.body, { status: 200, statusText: "OK", headers: entry.headers });
|
|
574
578
|
};
|
|
575
579
|
return {
|
|
576
580
|
fetch: decorated,
|
|
@@ -696,9 +700,9 @@ function preferDirective(timeoutMs) {
|
|
|
696
700
|
}
|
|
697
701
|
function createTransport(options) {
|
|
698
702
|
const baseUrl = normalizeDalpBaseUrl(String(options.baseUrl));
|
|
699
|
-
const runtime = options.runtime ?? (
|
|
703
|
+
const runtime = options.runtime ?? detectDalpRuntime();
|
|
700
704
|
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
701
|
-
const transportFetch = runtime === "
|
|
705
|
+
const transportFetch = runtime === "server" ? withRequestBodyCompression(baseFetch) : baseFetch;
|
|
702
706
|
const { apiKey: apiKeyOption } = options;
|
|
703
707
|
let apiKey;
|
|
704
708
|
if (apiKeyOption === undefined) {
|
|
@@ -708,12 +712,8 @@ function createTransport(options) {
|
|
|
708
712
|
} else {
|
|
709
713
|
apiKey = secret2(apiKeyOption.trim());
|
|
710
714
|
}
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
message: "DALP SDK: API keys are server/CLI-only credentials and cannot configure a browser client.",
|
|
714
|
-
why: "An API key placed in a browser client ships the credential to every visitor's bundle.",
|
|
715
|
-
fix: "Keep the API key on the server or CLI process; browser clients authenticate with the session cookie."
|
|
716
|
-
});
|
|
715
|
+
if (apiKey !== undefined) {
|
|
716
|
+
refuseBundledApiKey(runtime);
|
|
717
717
|
}
|
|
718
718
|
const sessionFetch = options.ssr === undefined ? transportFetch : createSsrFetch({
|
|
719
719
|
allowedOrigins: options.ssr.allowedOrigins,
|
|
@@ -726,14 +726,18 @@ function createTransport(options) {
|
|
|
726
726
|
if (options.authSurface !== undefined) {
|
|
727
727
|
contextHeaders.set(DALP_AUTH_SURFACE_HEADER2, options.authSurface);
|
|
728
728
|
}
|
|
729
|
+
if (runtime === "react-native") {
|
|
730
|
+
contextHeaders.set("Origin", new URL(baseUrl).origin);
|
|
731
|
+
}
|
|
729
732
|
return {
|
|
730
733
|
baseUrl,
|
|
731
734
|
fetch: conditional.fetch,
|
|
732
735
|
conditionalRequests: conditional.store,
|
|
733
|
-
credentials: runtime
|
|
736
|
+
credentials: runtimeCredentials(runtime),
|
|
734
737
|
apiKey,
|
|
735
738
|
contextHeaders,
|
|
736
|
-
settlementTimeoutMs: options.settlementTimeoutMs
|
|
739
|
+
settlementTimeoutMs: options.settlementTimeoutMs,
|
|
740
|
+
streamsResponseBody: !isBufferingFetch(baseFetch)
|
|
737
741
|
};
|
|
738
742
|
}
|
|
739
743
|
function callHeaders(transport, options) {
|
|
@@ -1332,6 +1336,21 @@ async function loadResponseDecoder() {
|
|
|
1332
1336
|
// src/proxy/sse.ts
|
|
1333
1337
|
var RESERVED_STREAM_FAILURE_EVENT2 = "effect/httpapi/stream/failure";
|
|
1334
1338
|
var STREAM_FAILED_ERROR_CODE2 = "sdk.stream.FAILED";
|
|
1339
|
+
var STREAM_UNREADABLE_BODY_ERROR_CODE2 = "sdk.stream.UNREADABLE_BODY";
|
|
1340
|
+
function isReadableBody(body) {
|
|
1341
|
+
return typeof body === "object" && body !== null && typeof Reflect.get(body, "getReader") === "function";
|
|
1342
|
+
}
|
|
1343
|
+
function unreadableBodyError(routeId) {
|
|
1344
|
+
return makeDalpApiError2(new DalpSdkError2({
|
|
1345
|
+
code: STREAM_UNREADABLE_BODY_ERROR_CODE2,
|
|
1346
|
+
routeId,
|
|
1347
|
+
category: "sdk.configuration",
|
|
1348
|
+
retryable: false,
|
|
1349
|
+
message: "The fetch this client uses cannot stream a response body.",
|
|
1350
|
+
why: "React Native's built-in fetch resolves only after the whole response has arrived and exposes no `response.body`, so a stream never yields a frame.",
|
|
1351
|
+
fix: 'Pass a streaming fetch as `fetch`; on React Native that is `fetch` from "expo/fetch", wrapped with the cookie store.'
|
|
1352
|
+
}));
|
|
1353
|
+
}
|
|
1335
1354
|
function parseFrame(raw) {
|
|
1336
1355
|
try {
|
|
1337
1356
|
return JSON.parse(raw.data);
|
|
@@ -1412,6 +1431,9 @@ async function* decodeEventStream2(body, decodeFrame, routeId) {
|
|
|
1412
1431
|
}
|
|
1413
1432
|
}
|
|
1414
1433
|
async function* openEventStream(transport, route, routeId, input, options, signal, closed) {
|
|
1434
|
+
if (!transport.streamsResponseBody) {
|
|
1435
|
+
throw unreadableBodyError(routeId);
|
|
1436
|
+
}
|
|
1415
1437
|
const prepared = prepareRequest(transport, route, input, options);
|
|
1416
1438
|
prepared.headers.set("Accept", "text/event-stream");
|
|
1417
1439
|
const response = await transport.fetch(prepared.url, {
|
|
@@ -1424,13 +1446,16 @@ async function* openEventStream(transport, route, routeId, input, options, signa
|
|
|
1424
1446
|
if (!response.ok) {
|
|
1425
1447
|
await raiseResponseError(response, routeId);
|
|
1426
1448
|
}
|
|
1427
|
-
|
|
1449
|
+
const { body, status } = response;
|
|
1450
|
+
if (body === null) {
|
|
1428
1451
|
return;
|
|
1429
1452
|
}
|
|
1453
|
+
if (!isReadableBody(body)) {
|
|
1454
|
+
throw unreadableBodyError(routeId);
|
|
1455
|
+
}
|
|
1430
1456
|
const decoder = await loadResponseDecoder();
|
|
1431
|
-
const { status } = response;
|
|
1432
1457
|
try {
|
|
1433
|
-
yield* decodeEventStream2(
|
|
1458
|
+
yield* decodeEventStream2(body, (frame) => decoder.decode(routeId, status, frame), routeId);
|
|
1434
1459
|
} catch (error) {
|
|
1435
1460
|
if (!closed.byConsumer) {
|
|
1436
1461
|
throw error;
|
|
@@ -1564,4 +1589,4 @@ function createTanstackFactories(transport) {
|
|
|
1564
1589
|
};
|
|
1565
1590
|
}
|
|
1566
1591
|
|
|
1567
|
-
export { DalpApiError2, isDalpApiError2, findDalpSdkError2, makeDalpApiError2, isDalpErrorCode2, isDalpSdkLocalErrorCode2, isDalpApiErrorCode2, answeredByRoute, isDalpRouteError2, createDalpContextHeaders2, createTransport, MALFORMED_SUCCESS_ERROR_CODE2, RESERVED_STREAM_FAILURE_EVENT2, STREAM_FAILED_ERROR_CODE2, decodeEventStream2, streamRequest, sendRequest, createFetchExecutor, canonicalize2, queryKeyFor2, UI_SETTLEMENT_TIMEOUT_MS2, createTanstackFactories };
|
|
1592
|
+
export { DalpApiError2, isDalpApiError2, findDalpSdkError2, makeDalpApiError2, isDalpErrorCode2, isDalpSdkLocalErrorCode2, isDalpApiErrorCode2, answeredByRoute, isDalpRouteError2, createDalpContextHeaders2, createTransport, MALFORMED_SUCCESS_ERROR_CODE2, RESERVED_STREAM_FAILURE_EVENT2, STREAM_FAILED_ERROR_CODE2, STREAM_UNREADABLE_BODY_ERROR_CODE2, decodeEventStream2, streamRequest, sendRequest, createFetchExecutor, canonicalize2, queryKeyFor2, UI_SETTLEMENT_TIMEOUT_MS2, createTanstackFactories };
|
package/dist/effect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DalpErrorCode, DalpRouteErrorCode, DalpRouteId } from "../generated/api-types.d.ts";
|
|
2
2
|
import { type DalpApiError } from "./api-error.ts";
|
|
3
|
-
export type DalpSdkLocalErrorCode = "DALP_SDK_ERROR" | "sdk.transport.REQUEST_FAILED" | "sdk.transport.MALFORMED_RESPONSE" | "sdk.transport.UNPUBLISHED_ERROR_CODE" | "sdk.transport.UNVERIFIED_ERROR_CODE" | `sdk.transport.HTTP_${number}` | "sdk.response.MALFORMED_SUCCESS" | "sdk.stream.FAILED" | "sdk.scalar.BIG_DECIMAL_INVALID" | "sdk.scalar.EVM_ADDRESS_INVALID" | "sdk.scalar.ORGANIZATION_ID_INVALID" | "sdk.scalar.TOKEN_DECIMALS_INVALID";
|
|
3
|
+
export type DalpSdkLocalErrorCode = "DALP_SDK_ERROR" | "sdk.transport.REQUEST_FAILED" | "sdk.transport.MALFORMED_RESPONSE" | "sdk.transport.UNPUBLISHED_ERROR_CODE" | "sdk.transport.UNVERIFIED_ERROR_CODE" | `sdk.transport.HTTP_${number}` | "sdk.response.MALFORMED_SUCCESS" | "sdk.stream.FAILED" | "sdk.stream.UNREADABLE_BODY" | "sdk.scalar.BIG_DECIMAL_INVALID" | "sdk.scalar.EVM_ADDRESS_INVALID" | "sdk.scalar.ORGANIZATION_ID_INVALID" | "sdk.scalar.TOKEN_DECIMALS_INVALID";
|
|
4
4
|
export type DalpApiErrorCode = DalpErrorCode | DalpSdkLocalErrorCode;
|
|
5
5
|
export declare const isDalpErrorCode: (code: string) => code is DalpErrorCode;
|
|
6
6
|
export declare const isDalpSdkLocalErrorCode: (code: string) => code is DalpSdkLocalErrorCode;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type { DalpExecuteResult, DalpRequestParts, DalpRequestRouteKey, DalpRout
|
|
|
4
4
|
export type { CompressionRequestInit } from "./client/request-compression.ts";
|
|
5
5
|
export type { CreateDalpClientOptions, DalpClientContext, DalpSsrOptions } from "./client/factory.ts";
|
|
6
6
|
export type { ConditionalRequestStore } from "./client/conditional-request.ts";
|
|
7
|
+
export type { DalpRuntime } from "./runtime.ts";
|
|
7
8
|
export { createCookieStoreFetch, makeCookieStore } from "./transport/cookie-store.ts";
|
|
8
9
|
export type { CookieStoreShape } from "./transport/cookie-store.ts";
|
|
9
10
|
export { SECRET_REDACTION_MARKER, Secret, secret } from "./secret.ts";
|
|
@@ -12,7 +13,7 @@ export type { SseEventDisposition, SseLifecycleFailureReason, SseLifecycleOption
|
|
|
12
13
|
export { WEBHOOK_MANIFEST_VERSION } from "./webhook-events.ts";
|
|
13
14
|
export type { Webhook } from "./webhook-events.ts";
|
|
14
15
|
export type { CallOptions } from "./proxy/builder.ts";
|
|
15
|
-
export { decodeEventStream, RESERVED_STREAM_FAILURE_EVENT, STREAM_FAILED_ERROR_CODE } from "./proxy/sse.ts";
|
|
16
|
+
export { decodeEventStream, RESERVED_STREAM_FAILURE_EVENT, STREAM_FAILED_ERROR_CODE, STREAM_UNREADABLE_BODY_ERROR_CODE, } from "./proxy/sse.ts";
|
|
16
17
|
export { MALFORMED_SUCCESS_ERROR_CODE } from "./proxy/response-decode.ts";
|
|
17
18
|
export { createDalpSdkConfigurationError, DalpSdkError } from "./errors/sdk-error.ts";
|
|
18
19
|
export { DalpApiError, findDalpSdkError, isDalpApiError, makeDalpApiError } from "./errors/api-error.ts";
|