@capxul/sdk 4.20.0-beta.4 → 4.20.0-beta.40
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 +282 -32
- package/dist/access-6VLRaqRP.mjs +26274 -0
- package/dist/index-DTc8HIBm.d.mts +40284 -0
- package/dist/index.d.mts +2 -653
- package/dist/index.mjs +121 -268
- package/dist/node/index.d.mts +9 -9
- package/dist/node/index.mjs +29 -38
- package/dist/organizations-DItJHTNM.mjs +12693 -0
- package/dist/permission-approvals-C2oLa3jf.mjs +138 -0
- package/dist/production-Dy8n4ZBY.mjs +5488 -0
- package/dist/testing/index.d.mts +32 -15
- package/dist/testing/index.mjs +315 -152
- package/package.json +51 -18
- package/dist/OAuthBearerAuthClient-C-ip-z8M.d.mts +0 -628
- package/dist/OAuthBearerAuthClient-DWkofxdZ.mjs +0 -2488
- package/dist/production-BMfmzwHq.mjs +0 -17131
- package/dist/production-CJwxgI8u.d.mts +0 -3141
package/README.md
CHANGED
|
@@ -19,51 +19,154 @@ const client = created.value;
|
|
|
19
19
|
Every public operation resolves a `Promise<CapxulResult<T>>`. Domain failures
|
|
20
20
|
are values, not thrown exceptions.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
After the Person and personal Account are ready, begin Organization setup and
|
|
23
|
+
store its exact ID before signer interaction:
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
```ts
|
|
26
|
+
const begun = await client.onboarding.beginOrganization({
|
|
27
|
+
name: "Analytical Engines",
|
|
28
|
+
handle: "analytical-engines",
|
|
29
|
+
country: "GH",
|
|
30
|
+
});
|
|
31
|
+
if (!begun.ok) throw begun.error;
|
|
32
|
+
|
|
33
|
+
const { orgId } = begun.value;
|
|
34
|
+
if (begun.value.setup.status === "awaitingAuthorization") {
|
|
35
|
+
const authorized = await client.org(orgId).setup.authorize();
|
|
36
|
+
if (!authorized.ok) throw authorized.error;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const observed = await client.org(orgId).setup.subscribe((snapshot) => {
|
|
40
|
+
if (snapshot.status === "value") console.log(snapshot.value.status);
|
|
41
|
+
});
|
|
42
|
+
if (!observed.ok) throw observed.error;
|
|
43
|
+
// Call observed.value() when the screen no longer needs updates.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`setup.resume()` requests a backend check for the same Organization. It does
|
|
47
|
+
not ask the signer to submit again. `onboarding.completeOrganization()` runs
|
|
48
|
+
begin, authorization when needed, and observation through `ready`.
|
|
49
|
+
|
|
50
|
+
An authenticated client can find unfinished founder setups without a local
|
|
51
|
+
recovery file. Read pages until `isDone` is true; omit `cursor` on the first
|
|
52
|
+
request:
|
|
26
53
|
|
|
27
54
|
```ts
|
|
28
|
-
|
|
55
|
+
let cursor: string | undefined;
|
|
56
|
+
do {
|
|
57
|
+
const listed = await client.onboarding.listOrganizationSetups(
|
|
58
|
+
cursor === undefined ? {} : { cursor },
|
|
59
|
+
);
|
|
60
|
+
if (!listed.ok) throw listed.error;
|
|
61
|
+
for (const setup of listed.value.page) console.log(setup.orgId, setup.status);
|
|
62
|
+
if (listed.value.isDone) break;
|
|
63
|
+
if (listed.value.cursor === null) throw new Error("Missing setup cursor");
|
|
64
|
+
cursor = listed.value.cursor;
|
|
65
|
+
} while (true);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`listOrganizationSetups` is a read. It does not authorize, resume, or sign.
|
|
69
|
+
|
|
70
|
+
## Local Effect DevTools
|
|
29
71
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
72
|
+
Start the repository collector with `vp run devtools:serve`, then create a
|
|
73
|
+
development client:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const created = await createCapxulClient({
|
|
77
|
+
publishableKey,
|
|
78
|
+
devToolsUrl: "ws://127.0.0.1:34439",
|
|
33
79
|
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Run an operation and read its spans and metrics with `vp run devtools:snapshot`.
|
|
83
|
+
For the editor view, start its Effect DevTools server and run the collector with
|
|
84
|
+
`vp run devtools:serve --vscode`. Browser and
|
|
85
|
+
Node hosts use the same option. Omit it to disable the connection. Call
|
|
86
|
+
`client.close()` when the host no longer needs the client; close also releases
|
|
87
|
+
DevTools. The URL must use `ws` or `wss` on `localhost`, `127.0.0.1`, `[::1]`, or
|
|
88
|
+
`host.docker.internal`, without credentials, a query, or a fragment.
|
|
89
|
+
This local connection carries native debug detail. It does not change remote
|
|
90
|
+
telemetry configuration. See the [development workflow](../../docs/canon/development-and-verification.md)
|
|
91
|
+
for agent access and the shared local collector.
|
|
92
|
+
|
|
93
|
+
## Invitation updates
|
|
94
|
+
|
|
95
|
+
Use `client.org(orgId).invitations.subscribe({ invitationId }, listener, { signal })`
|
|
96
|
+
to observe one offer. The listener receives `loading`, then validated `value`
|
|
97
|
+
snapshots or a terminal typed `error`. The successful result holds an idempotent
|
|
98
|
+
unsubscribe function. Abort, sign-out, account replacement, and client close
|
|
99
|
+
stop delivery. A late registration handle is released when it arrives.
|
|
100
|
+
This read never accepts an offer or retries a write. Bound a wait through the
|
|
101
|
+
caller’s AbortSignal; per-call timeout options do not define stream lifetime.
|
|
102
|
+
|
|
103
|
+
## Asset amount input and display
|
|
104
|
+
|
|
105
|
+
Use the asset helpers at Payment form and display boundaries. They keep the
|
|
106
|
+
exact AssetId and do not convert a value to a JavaScript number.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { formatAssetAmount, parseAssetAmount, requireAsset, TOKENS } from "@capxul/sdk";
|
|
110
|
+
|
|
111
|
+
const amount = parseAssetAmount("1,000.25", requireAsset(TOKENS.BASE_SEPOLIA.CAPXUL_TEST_USDC));
|
|
34
112
|
|
|
35
113
|
if ("kind" in amount) {
|
|
36
114
|
console.error(amount.reason); // closed code — the app owns the sentence (ADR-0023)
|
|
37
115
|
} else {
|
|
38
|
-
console.log(
|
|
39
|
-
console.log(formatMoney(amount, { grammar: "code" })); // 1,000.25 USD
|
|
116
|
+
console.log(formatAssetAmount(amount)); // 1,000.25 USDC
|
|
40
117
|
}
|
|
41
118
|
```
|
|
42
119
|
|
|
43
|
-
`
|
|
44
|
-
|
|
120
|
+
`parseAssetAmount` returns validation errors as values. Payment, request,
|
|
121
|
+
Budget, Payroll, and activity amounts require AssetAmount. Fiat Money remains
|
|
122
|
+
separate valuation data.
|
|
45
123
|
|
|
46
|
-
##
|
|
124
|
+
## Value all asset positions
|
|
47
125
|
|
|
48
|
-
`holdings.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
stands for no supported currency is skipped, because it cannot be rendered.
|
|
126
|
+
Use `holdings.current` for raw positions. Use `valueIn` to value exact asset
|
|
127
|
+
quantities in a display currency. A token symbol does not identify an asset.
|
|
128
|
+
Quantities from different assets cannot be compared as a headline balance.
|
|
52
129
|
|
|
53
130
|
```ts
|
|
54
|
-
import { formatMoney,
|
|
131
|
+
import { formatMoney, valueIn } from "@capxul/sdk";
|
|
55
132
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const holding: Holding = result.value;
|
|
60
|
-
console.log(holding.symbol); // "USDX" — the token symbol the chain reported
|
|
61
|
-
console.log(formatMoney(holding.available, { grammar: "code" })); // e.g. "1,250.50 USD"
|
|
133
|
+
const valuation = valueIn(amounts, displayCurrency, rates);
|
|
134
|
+
if (valuation.total !== null) {
|
|
135
|
+
console.log(formatMoney(valuation.total));
|
|
62
136
|
}
|
|
63
137
|
```
|
|
64
138
|
|
|
65
|
-
`
|
|
66
|
-
|
|
139
|
+
Keep `valuation.unrated` visible beside the known subtotal. A missing rate
|
|
140
|
+
does not mean a zero balance.
|
|
141
|
+
|
|
142
|
+
## Approvals
|
|
143
|
+
|
|
144
|
+
Every signature a person makes goes through one approval. A caller with no
|
|
145
|
+
key, such as the CLI or an agent, prepares the Payment and hands the person
|
|
146
|
+
the link. Nothing is signed or sent until the person approves it there.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const prepared = await client.approvals.prepare({
|
|
150
|
+
to: { kind: "email", email: "ada@example.com" },
|
|
151
|
+
amount: { asset: usdc.assetId, value: "25" },
|
|
152
|
+
});
|
|
153
|
+
if (!prepared.ok) throw prepared.error;
|
|
154
|
+
console.log(prepared.value.url, prepared.value.summary);
|
|
155
|
+
|
|
156
|
+
// Sends the approved operation once, then waits until its Payment settles.
|
|
157
|
+
const outcome = await client.approvals.wait(prepared.value.approvalId, { signal });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The approval page runs as the same person, with their own signer:
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
await client.approvals.review(approvalId); // the first open is recorded
|
|
164
|
+
const approved = await client.approvals.approve(approvalId); // or reject(approvalId)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`approve` signs only the digest the approval's operation describes, and only
|
|
168
|
+
with the approval's signer. The backend checks the signature and never signs.
|
|
169
|
+
A rejected or expired approval ends `wait` with that state.
|
|
67
170
|
|
|
68
171
|
## Observability
|
|
69
172
|
|
|
@@ -105,6 +208,11 @@ The backend remains the authority for reserved names and global uniqueness.
|
|
|
105
208
|
The method returns only after the client requirement reaches a ready Account.
|
|
106
209
|
The React frontend does not use this journey yet.
|
|
107
210
|
|
|
211
|
+
A Node host that keeps login outside the browser can pass `openfortSignerFactory`.
|
|
212
|
+
The factory receives only the validated Openfort and auth bootstrap fields. Its
|
|
213
|
+
browser page can use `createOpenfortBrowserSigner` with host-owned access-token
|
|
214
|
+
and encryption-session callbacks. It does not need a second login or private SDK import.
|
|
215
|
+
|
|
108
216
|
## Published entry points
|
|
109
217
|
|
|
110
218
|
The packed npm package exposes only:
|
|
@@ -125,7 +233,10 @@ also contains them.
|
|
|
125
233
|
import { createCapxulTestClient } from "@capxul/sdk/testing";
|
|
126
234
|
|
|
127
235
|
const testing = createCapxulTestClient();
|
|
128
|
-
await testing.seedIdentity({
|
|
236
|
+
await testing.seedIdentity({
|
|
237
|
+
authUserId: "auth_seeded_test",
|
|
238
|
+
email: "test@example.test",
|
|
239
|
+
});
|
|
129
240
|
await testing.clock.advance(1_000);
|
|
130
241
|
await testing.client.auth.signIn({ email: "test@example.test" });
|
|
131
242
|
const otp = testing.peekOtp("test@example.test");
|
|
@@ -165,11 +276,150 @@ vp run --filter @capxul/sdk build
|
|
|
165
276
|
vp run --filter @capxul/sdk proofs:live
|
|
166
277
|
```
|
|
167
278
|
|
|
168
|
-
`proofs:live` runs the
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
[maintainer context](./CONTEXT.md#capability-and-proof-boundaries).
|
|
279
|
+
`proofs:live` runs the Organization Core SDK journey against the configured
|
|
280
|
+
Convex deployment and Base Sepolia. Run it with the synchronized candidate.
|
|
281
|
+
The package tests prove local SDK behavior.
|
|
282
|
+
See [maintainer context](./CONTEXT.md#capability-and-proof-boundaries).
|
|
172
283
|
|
|
173
284
|
The browser production path owns its Openfort signer when no signer is supplied.
|
|
174
|
-
|
|
175
|
-
|
|
285
|
+
Integration recovery evidence and its published-package boundary were recorded
|
|
286
|
+
in the #870 receipt (since deleted; see git history).
|
|
287
|
+
|
|
288
|
+
### Node session storage
|
|
289
|
+
|
|
290
|
+
A Node host can pass `nodeSessionStorage: { load, save }` to
|
|
291
|
+
`createCapxulClient`. Both methods return promises. `load` returns an opaque
|
|
292
|
+
BetterAuth credential or `null`; `save` receives the credential or `null` on
|
|
293
|
+
logout. Store it with owner-only access and isolate it by application and
|
|
294
|
+
environment. Do not print it or use a cached `AuthSession` as backend authority.
|
|
295
|
+
The SDK validates the credential and expiry with BetterAuth on process restart.
|
|
296
|
+
With durable storage, each adapter serializes session reads, OTP verification,
|
|
297
|
+
sign-out, and Convex token reads. A delayed response cannot clear or rotate a
|
|
298
|
+
credential from a later operation on that adapter. Cancellation while queued
|
|
299
|
+
returns immediately and prevents that operation from reaching the provider.
|
|
300
|
+
Hosts that share storage across clients or processes must fence writes, for
|
|
301
|
+
example with an atomic compare-and-set; the adapter cannot order other clients.
|
|
302
|
+
|
|
303
|
+
For separate OTP request and verification processes, retain the email and
|
|
304
|
+
original request timestamp. After `auth.getSession()` confirms signed-out state,
|
|
305
|
+
call `auth.resumeOtp({ email, requestedAt })`, then `auth.verifyOtp({ email,
|
|
306
|
+
code })`. Resume restores pending input only. It does not send another OTP or
|
|
307
|
+
authenticate the caller. Keep OTP values out of arguments, logs, and stored data.
|
|
308
|
+
|
|
309
|
+
### Existing financial documents
|
|
310
|
+
|
|
311
|
+
Use `paymentDocuments.verify({ documentHash, contentHash })` to verify stored
|
|
312
|
+
content. Use `paymentDocuments.render` with the same pair for safe semantic HTML.
|
|
313
|
+
Call `paymentDocuments.get` only for a deliberate download. Its `bytesBase64`
|
|
314
|
+
contains the exact stored bytes. Do not log document results or error payloads.
|
|
315
|
+
Get references from Payment or Movement activity detail. References include
|
|
316
|
+
kind, format, and role. A hash is not permission to read a document.
|
|
317
|
+
Reads check current Payment participants or current Movement actor access.
|
|
318
|
+
Missing and unauthorized pairs return the same refusal. These methods do not
|
|
319
|
+
create documents or add Payment links.
|
|
320
|
+
|
|
321
|
+
## Inbox documents
|
|
322
|
+
|
|
323
|
+
Personal Account and Organization Inbox reads include `documents` on each
|
|
324
|
+
item. An unpaid issued Invoice can have document references before a Payment
|
|
325
|
+
exists. Requests without an attached document return an empty array. The
|
|
326
|
+
references preserve the document and content hashes needed by
|
|
327
|
+
`paymentDocuments.get`, `verify`, and `render`. Inbox listing does not render
|
|
328
|
+
or download documents. Existing issuer and payer access checks still apply.
|
|
329
|
+
|
|
330
|
+
Issued requests and Inbox items retain a linked `paymentId` when preparation
|
|
331
|
+
has started. Invoice Inbox items also retain the stored `issuerSafe`. These
|
|
332
|
+
fields do not mean that the Payment has settled.
|
|
333
|
+
|
|
334
|
+
`inbox.paymentTerms(requestId)` reads exact private terms without a signer or
|
|
335
|
+
write. It returns the issued Invoice instruction or a deterministic Memo
|
|
336
|
+
instruction for a generic request. Prepared requests retain their stored
|
|
337
|
+
instruction. Only the addressed payer can read these terms.
|
|
338
|
+
|
|
339
|
+
`inbox.fulfill(confirmation, { onPrepared })` executes the confirmed request
|
|
340
|
+
through the existing signer. `onPrepared({ paymentIds })` receives verified
|
|
341
|
+
durable IDs before signing. If the callback fails, signing stops. Do not await
|
|
342
|
+
the fulfillment call inside this callback. Organization confirmation requires
|
|
343
|
+
an admitted Permission; personal confirmation does not accept one. The backend
|
|
344
|
+
checks the payer, exact amount, issuer Safe, release, and frozen instruction.
|
|
345
|
+
|
|
346
|
+
## Fixed offers and checkout
|
|
347
|
+
|
|
348
|
+
`account.offers` and `org(id).offers` manage reusable fixed-price offers with
|
|
349
|
+
explicit quantity limits. Creation uses a caller request key. Revisions append
|
|
350
|
+
immutable prices and require the expected revision.
|
|
351
|
+
|
|
352
|
+
`checkouts.offer(id)` is a public read and creates no purchase. `checkouts.create`
|
|
353
|
+
uses a UUID v4 purchase key, selected revision, quantity, and funding wallet to
|
|
354
|
+
freeze one purchase. `checkouts.get` restores its exact snapshot. `checkouts.prepare`
|
|
355
|
+
returns unsigned approval and Payments-contract transactions for that wallet.
|
|
356
|
+
The consumer checks chain, balance, allowance, and gas. Preparation does not sign,
|
|
357
|
+
submit, or prove settlement. No checkout URL is returned before the page exists.
|
|
358
|
+
|
|
359
|
+
## Fixed offer checkout candidate
|
|
360
|
+
|
|
361
|
+
The checkout APIs below are source candidate APIs. The published beta17 baseline
|
|
362
|
+
does not include them. Consume a matching packed candidate for local integration.
|
|
363
|
+
|
|
364
|
+
`account.offers` and `org(id).offers` provide create, list, get, revise, and
|
|
365
|
+
deactivate. Create uses a caller replay key. Revision and deactivation require the
|
|
366
|
+
current revision. Each offer has one asset, an exact unit amount, and bounded whole
|
|
367
|
+
unit quantities. An offer revision does not change an existing purchase.
|
|
368
|
+
|
|
369
|
+
Public `checkouts.offer(id)` reads approved offer fields. `checkouts.create(input)`
|
|
370
|
+
explicitly freezes the offer revision, quantity, funding wallet, recipient, amount,
|
|
371
|
+
network, contract, instruction, and settlement identity. Reuse the same UUID v4
|
|
372
|
+
purchase key after a lost reply. Different terms with the same key refuse.
|
|
373
|
+
`checkouts.get(id)` recovers the frozen snapshot. These methods do not sign.
|
|
374
|
+
|
|
375
|
+
`checkouts.prepare({ checkoutId, sourceWallet })` returns exact unsigned approval
|
|
376
|
+
and payment transactions. The consumer checks network, balance, gas, and allowance
|
|
377
|
+
before requesting wallet action. A connection or submission is not settlement.
|
|
378
|
+
|
|
379
|
+
`checkouts.state(id)` performs a checked read. It returns historical receipt data
|
|
380
|
+
separately from current verification: `unpaid`, `verified`, `orphaned`, or
|
|
381
|
+
`unavailable`. Provider failure cannot prove that a receipt was removed.
|
|
382
|
+
`checkouts.reconcile({ checkoutId, transactionHash })` returns the candidate result
|
|
383
|
+
and independently checked stored state. An unrelated candidate cannot change a
|
|
384
|
+
valid stored payment. Canonical occurrence correction retains logical identity.
|
|
385
|
+
|
|
386
|
+
These offer methods do not implement recurring billing.
|
|
387
|
+
`checkouts.payRegistered({ checkoutId, payer, permissionId? })` prepares, verifies,
|
|
388
|
+
and submits through the existing registered signer. Account and Organization
|
|
389
|
+
authority remain mandatory. Submission does not establish settlement. Public
|
|
390
|
+
Invoice funding and frontend registered-payer adoption remain separate work.
|
|
391
|
+
|
|
392
|
+
`isCheckoutSettlementConsumed(error, settlementId)` decodes the contract's refusal
|
|
393
|
+
for the exact frozen ID. Use it for structured or raw RPC errors. Error message
|
|
394
|
+
text alone does not establish that an ID was consumed.
|
|
395
|
+
|
|
396
|
+
## Invoice checkout candidate
|
|
397
|
+
|
|
398
|
+
`checkouts.invoice(linkToken)` exposes only the approved payment summary.
|
|
399
|
+
Itemization, notes, billing identity and document bytes remain restricted to the
|
|
400
|
+
issuer and named debtor. Funding does not change that debtor or grant document
|
|
401
|
+
access. Existing link tokens resolve the original obligation.
|
|
402
|
+
|
|
403
|
+
`prepareInvoice({ linkToken, sourceWallet })` returns unsigned transactions.
|
|
404
|
+
`invoiceState(linkToken)` checks current receipt evidence. `reconcileInvoice`
|
|
405
|
+
verifies an explicitly supplied transaction against the fixed terms. Late exact
|
|
406
|
+
payments remain visible after expiry or cancellation. Conflicting consumption is
|
|
407
|
+
unavailable, never paid, and does not create another settlement identity.
|
|
408
|
+
|
|
409
|
+
`payInvoiceRegistered({ linkToken, payer, sourceWallet, permissionId? })` uses the
|
|
410
|
+
existing registered signer. The selected Safe must match the backend's current
|
|
411
|
+
spending authority. The response omits private source commands and document
|
|
412
|
+
bundles. Existing private preparations retain their original settlement ID.
|
|
413
|
+
|
|
414
|
+
`checkoutUrl(frontendOrigin, reference)` constructs an offer or Invoice route.
|
|
415
|
+
The caller supplies the selected environment's frontend origin. The consumer
|
|
416
|
+
still validates the reference and reads authoritative backend terms. Constructing
|
|
417
|
+
a URL does not prove that its consumer is deployed.
|
|
418
|
+
|
|
419
|
+
### Browser failure capture
|
|
420
|
+
|
|
421
|
+
Use the optional `failureCapture` callback for a host-owned synchronous browser
|
|
422
|
+
capture bridge. The callback receives immutable masked factory words only for
|
|
423
|
+
the SDK's original typed failure. Omission preserves current capture behavior.
|
|
424
|
+
See [Configure SDK observability](../../apps/docs/content/docs/sdk/how-to/observation.mdx#browser-failure-capture)
|
|
425
|
+
for the fixed message paths and host privacy contract.
|