@capxul/sdk 0.2.0-alpha.4 → 1.0.0-alpha.6

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.
Files changed (45) hide show
  1. package/README.md +5 -308
  2. package/dist/InMemoryAuthCacheAdapter-BK-B_ERB.mjs +113 -0
  3. package/dist/InMemoryAuthCacheAdapter-BK-B_ERB.mjs.map +1 -0
  4. package/dist/index.d.mts +1362 -0
  5. package/dist/index.d.mts.map +1 -0
  6. package/dist/index.mjs +5393 -0
  7. package/dist/index.mjs.map +1 -0
  8. package/dist/node/index.d.mts +55 -0
  9. package/dist/node/index.d.mts.map +1 -0
  10. package/dist/node/index.mjs +159 -0
  11. package/dist/node/index.mjs.map +1 -0
  12. package/dist/ports/safe-deployment.d.mts +2 -0
  13. package/dist/ports/safe-deployment.mjs +38 -0
  14. package/dist/ports/safe-deployment.mjs.map +1 -0
  15. package/dist/safe-deployment-Vni46k3t.d.mts +137 -0
  16. package/dist/safe-deployment-Vni46k3t.d.mts.map +1 -0
  17. package/dist/signer-3-awyCio.d.mts +145 -0
  18. package/dist/signer-3-awyCio.d.mts.map +1 -0
  19. package/package.json +39 -70
  20. package/CHANGELOG.md +0 -274
  21. package/LICENSE +0 -44
  22. package/dist/client-CbUeJoM9.d.ts +0 -1335
  23. package/dist/client-UEm2oZbZ.d.cts +0 -1335
  24. package/dist/client.cjs +0 -4042
  25. package/dist/client.d.cts +0 -6
  26. package/dist/client.d.ts +0 -6
  27. package/dist/client.js +0 -4040
  28. package/dist/errors-CwhCWGxm.d.ts +0 -70
  29. package/dist/errors-rqxuUhQP.d.cts +0 -70
  30. package/dist/errors.cjs +0 -35
  31. package/dist/errors.d.cts +0 -2
  32. package/dist/errors.d.ts +0 -2
  33. package/dist/errors.js +0 -31
  34. package/dist/index.cjs +0 -4273
  35. package/dist/index.d.cts +0 -461
  36. package/dist/index.d.ts +0 -461
  37. package/dist/index.js +0 -4234
  38. package/dist/next-action-CTGl8wpy.d.cts +0 -177
  39. package/dist/next-action-CTGl8wpy.d.ts +0 -177
  40. package/dist/types-BD4VAQb5.d.ts +0 -1205
  41. package/dist/types-HlwCIjgQ.d.cts +0 -1205
  42. package/dist/webhooks.cjs +0 -118
  43. package/dist/webhooks.d.cts +0 -33
  44. package/dist/webhooks.d.ts +0 -33
  45. package/dist/webhooks.js +0 -116
package/README.md CHANGED
@@ -1,312 +1,9 @@
1
1
  # @capxul/sdk
2
2
 
3
- Headless TypeScript client for Capxul — issue and accept stablecoin
4
- payments, manage organization treasuries, and orchestrate on-chain
5
- flows backed by audited custody contracts.
3
+ Layer 0 SDK package.
6
4
 
7
- > **Alpha — `0.x` is pre-release.** APIs can change between alpha
8
- > versions. Pin to an exact version (`@capxul/sdk@0.1.0-alpha.0`) and
9
- > read the changelog before upgrading.
5
+ - [Getting started](./docs/getting-started.md) canonical consumer DX
6
+ - [Package docs index](./docs/README.md)
7
+ - [Env module](./docs/env.md)
10
8
 
11
- ## Install
12
-
13
- ```bash
14
- pnpm add @capxul/sdk @capxul/sdk-react
15
- # or: npm install @capxul/sdk @capxul/sdk-react
16
- ```
17
-
18
- `@capxul/sdk` is the headless contract — flows, types, errors. The
19
- React-flavored entry point lives in `@capxul/sdk-react`. Pick:
20
-
21
- - **Browser apps (recommended).** Install both. Use
22
- `<CapxulProvider config={{ mode: "publishable-key", publishableKey }}>`
23
- from `@capxul/sdk-react` and the `useMe` / `useCapxulStatus` hooks.
24
- See that package's README for the lazy-DX example.
25
- - **Server / CLI / scripts.** Install only `@capxul/sdk`. Build a
26
- `CapxulClient` directly with your own auth and Convex adapters
27
- (see "Server-side construction" below).
28
-
29
- ## Two-layer trust
30
-
31
- Capxul splits the platform into two layers with different licenses:
32
-
33
- | Layer | What it does | License |
34
- | ---------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
35
- | Custody (on-chain) | Safe v1.4.1 + ERC-4337 modules — every contract that holds, transfers, or signs over user funds | Open source, audited, in the [Capxul GitHub repo](https://github.com/Xelmar-tech/Capxul/tree/main/packages/contracts) |
36
- | Orchestration (this package) | Workflow logic, business rules, integration glue, the `/v1/*` HTTP client | Proprietary — see `LICENSE` |
37
-
38
- You don't have to take our word for the parts that hold money — those
39
- contracts are open and auditable. The parts that orchestrate workflows
40
- on top are our product.
41
-
42
- ## Quickstart (server / CLI)
43
-
44
- ```ts
45
- import { createCapxulClient, createLocalSigner, tryCatch } from "@capxul/sdk";
46
-
47
- const signer = createLocalSigner(process.env.CAPXUL_PRIVATE_KEY!);
48
- const capxul = createCapxulClient({
49
- apiKey: process.env.CAPXUL_API_KEY!, // server-to-server partner key
50
- signer,
51
- });
52
-
53
- const [err, payment] = await tryCatch(
54
- capxul.payments.create({
55
- to: { email: "vendor@example.com" },
56
- amount: { value: "100", currency: "USD" },
57
- }),
58
- );
59
-
60
- if (err) {
61
- if (err.code === "INSUFFICIENT_BALANCE") {
62
- // narrow `err.code` to route the UI / reply
63
- }
64
- throw err;
65
- }
66
-
67
- console.log(payment.id);
68
- ```
69
-
70
- ## Quickstart (browser via React)
71
-
72
- See [`@capxul/sdk-react`](https://www.npmjs.com/package/@capxul/sdk-react).
73
- The browser path uses a publishable key (`cap_pk_…`) and bootstraps
74
- runtime URLs lazily — no need to ship secrets to the client:
75
-
76
- ```tsx
77
- import { CapxulProvider, useMe, useCapxulStatus } from "@capxul/sdk-react";
78
-
79
- <CapxulProvider
80
- config={{
81
- mode: "publishable-key",
82
- publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
83
- }}
84
- >
85
- <App />
86
- </CapxulProvider>;
87
- ```
88
-
89
- ## Publishable-key transport
90
-
91
- The browser transport accepts a publishable key, then lazily resolves
92
- runtime URLs by POSTing to `/v1/client/bootstrap` on the first auth or
93
- `ensureRuntime()` call. The successful backend response is intentionally
94
- small:
95
-
96
- ```json
97
- {
98
- "authBaseUrl": "https://<deployment>.convex.site/api/auth",
99
- "convexUrl": "https://<deployment>.convex.cloud"
100
- }
101
- ```
102
-
103
- The resolved runtime is cached for the lifetime of the transport. Concurrent
104
- callers share one bootstrap request, successful resolutions are reused, and
105
- failed bootstrap attempts reset so the next call can retry. Tests and
106
- non-default deployments can inject both `fetchImpl` and an absolute
107
- `bootstrapUrl`:
108
-
109
- ```ts
110
- import { makeHttpTransport, CapxulError } from "@capxul/sdk";
111
-
112
- const transport = makeHttpTransport({
113
- mode: "publishable-key",
114
- publishableKey: process.env.NEXT_PUBLIC_CAPXUL_PUBLISHABLE_KEY!,
115
- bootstrapUrl: "https://api.capxul.com/v1/client/bootstrap",
116
- fetchImpl: fetch,
117
- });
118
-
119
- await transport.ensureRuntime();
120
- ```
121
-
122
- Local setup errors and backend bootstrap errors both use `CapxulError`, but
123
- carry different `details.source` values:
124
-
125
- ```ts
126
- try {
127
- await transport.ensureRuntime();
128
- } catch (error) {
129
- if (error instanceof CapxulError) {
130
- if (error.details?.source === "sdk-config") {
131
- // Missing or malformed local config, such as publishableKey or bootstrapUrl.
132
- }
133
- if (error.details?.source === "backend-bootstrap") {
134
- // Sanitized backend refusal, such as NOT_AUTHENTICATED or PERMISSION_DENIED.
135
- }
136
- }
137
- }
138
- ```
139
-
140
- Runtime proof status:
141
-
142
- | Surface | Source support | Runtime proof | Status |
143
- | -------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
144
- | SDK transport | `makeHttpTransport({ mode: "publishable-key", publishableKey })` | `packages/sdk/tests/unit/transport.test.ts` proves config validation, singleflight bootstrap, retry after failure, lifecycle transitions, and sanitized backend errors. | Proven with mocked fetch |
145
- | React provider | `CapxulProvider config={{ mode: "publishable-key", ... }}` | `packages/sdk-react/ops/proof/react-headless.test.tsx` proves bootstrap before a real `useMe()` read through the provider and lazy Convex data client. | Proven with mocked fetch + headless React |
146
- | Reference CLI | `bootstrap probe --mock --json` | `apps/reference-cli/scripts/agent-driver.ts` phase 0 and the direct CLI command prove provider/bootstrap/auth ordering and sanitized output. | Proven locally; live endpoint remains manual-key gated |
147
-
148
- Copy-paste local replication:
149
-
150
- ```bash
151
- corepack pnpm --filter @capxul/sdk check-types
152
- corepack pnpm --filter @capxul/sdk build
153
- corepack pnpm --filter @capxul/sdk-react check-types
154
- corepack pnpm --filter @capxul/sdk-react build
155
- corepack pnpm --filter @capxul/reference-cli check-types
156
- corepack pnpm --filter @capxul/reference-cli build
157
- node apps/reference-cli/dist/cli.js bootstrap probe --mock --json
158
- ```
159
-
160
- Run the SDK and React SDK builds before the reference CLI typecheck in a
161
- fresh checkout; the CLI depends on their generated declaration outputs.
162
-
163
- Expected sanitized pass signal:
164
-
165
- ```json
166
- {
167
- "command": "bootstrap.probe",
168
- "ok": true,
169
- "mode": "publishable-key",
170
- "status": "ready",
171
- "bootstrapRequests": 1,
172
- "authRequests": 1,
173
- "keyLengthClass": "provided",
174
- "mocked": true
175
- }
176
- ```
177
-
178
- ## Public surface (alpha)
179
-
180
- ```ts
181
- capxul.auth // sendOtp, verifyOtp, signOut, serviceTokenMint
182
- capxul.me // get, update — first-party caller identity
183
- capxul.accounts // create, retrieve, list
184
- capxul.organizations // CRUD + members + payments + treasury
185
- capxul.payments // create, retrieve, list
186
- capxul.withdrawals // create, retrieve, list
187
- capxul.documents // personal invoices first; broader document variants typed
188
- capxul.flows.{auth, onboarding, provisioning} // XState v5 flows
189
- ```
190
-
191
- ### Personal invoice documents
192
-
193
- The first `documents` runtime surface is the personal invoice pilot.
194
- It creates immutable open invoice documents with a canonical
195
- `invoiceHash`. The backend stores the private invoice payload; the
196
- chain receives only the hash as `documentHash`.
197
-
198
- ```ts
199
- const [err, invoice] = await capxul.documents.create({
200
- type: "invoice",
201
- recipient: { email: "client@example.com" },
202
- amount: { value: "25.00", currency: "USD" },
203
- lineItems: [
204
- { description: "Design", quantity: 1, unitPrice: "25.00", currency: "USD" },
205
- ],
206
- reference: "May design work",
207
- dueAt: "2026-05-20T00:00:00.000Z",
208
- });
209
- ```
210
-
211
- Use `capxul.documents.retrieve(docId)`, `.list({ type: "invoice" })`,
212
- and `.cancel(docId)` for the rest of the pilot surface. Org-admin
213
- invoices, file uploads, PDFs, partial payments, and mutable open
214
- invoices are outside this pilot.
215
-
216
- Helpers:
217
-
218
- - `tryCatch(promise)` — error tuple `[error, data]`; routes on
219
- `error.code` (typed `CapxulErrorCode` union)
220
- - `matchError`, `matchStatus`, `matchAction` — exhaustive pattern
221
- matchers per CANON.md §4.44
222
- - Branded type constructors — `toEmail`, `toAccountId`,
223
- `toOrganizationId`, … — never cast raw strings
224
-
225
- ## Organizations
226
-
227
- Create an organization, manage its treasury, and invite members with
228
- role-based access:
229
-
230
- ```ts
231
- // Create
232
- const [err, org] = await capxul.organizations.create({ name: "Acme" });
233
-
234
- // Invite a member — returns the raw token once
235
- const [err, { member, inviteToken }] =
236
- await capxul.organizations.members.invite({
237
- organizationId: org.id,
238
- email: "alice@example.com",
239
- role: "finance_manager",
240
- });
241
-
242
- // List members — defaults to active; pass status to filter
243
- const [err, list] = await capxul.organizations.members.list({
244
- organizationId: org.id,
245
- status: "pending", // "pending" | "active" | "revoked" | "expired" | "all"
246
- });
247
-
248
- // Accept an invitation (called by the invitee)
249
- const [err, member] = await capxul.organizations.members.accept({
250
- token: inviteToken,
251
- });
252
-
253
- // Revoke a pending invitation
254
- const [err] = await capxul.organizations.members.revoke({
255
- organizationId: org.id,
256
- memberId: member.id,
257
- });
258
-
259
- // Remove an active member
260
- const [err] = await capxul.organizations.members.remove({
261
- organizationId: org.id,
262
- memberId: member.id,
263
- });
264
- ```
265
-
266
- Member roles: `owner`, `finance_manager`, `payments_operator`,
267
- `org_admin`, `viewer`, `accountant`. Only `owner` and `org_admin` can
268
- invite, revoke, remove, or change roles.
269
-
270
- Pending invitations auto-accept on first sign-in when the user's email
271
- matches. Overdue pending invitations expire via a nightly cron.
272
-
273
- ## Error model
274
-
275
- Every method returns a `CapxulResult<T, Codes>` tuple. Errors are
276
- instances of `CapxulError` with a typed `code` (narrowed per method).
277
-
278
- ```ts
279
- const [err, account] = await tryCatch(capxul.me.get());
280
-
281
- if (err) {
282
- switch (err.code) {
283
- case "PROFILE_NOT_FOUND":
284
- return router.push("/onboarding");
285
- case "NOT_AUTHENTICATED":
286
- return router.push("/login");
287
- default:
288
- throw err; // unhandled — bubble to the error boundary
289
- }
290
- }
291
- ```
292
-
293
- There is no second error type. Backend `CapxulError` → Convex
294
- serialization → SDK deserialization → frontend `CapxulError` — one
295
- class, 34 codes, end-to-end.
296
-
297
- ## Telemetry (opt-out)
298
-
299
- The SDK reports operational telemetry (operation IDs, latencies,
300
- correlation IDs, error codes) to Capxul. **PII is never sent** — events
301
- are scrubbed at the source. Disable by passing
302
- `{ telemetry: { enabled: false } }` (coming in `0.2.0`); current alpha
303
- sends only operational signals which Capxul Terms disclose explicitly.
304
-
305
- ## License
306
-
307
- Proprietary — see [`LICENSE`](./LICENSE). The orchestration code in
308
- this package is governed by the Capxul Terms of Service. The
309
- value-bearing custody contracts are open source — see "Two-layer trust"
310
- above.
311
-
312
- Contact: [legal@capxul.com](mailto:legal@capxul.com)
9
+ React apps: [`@capxul/sdk-react` provider docs](../sdk-react/docs/provider.md).
@@ -0,0 +1,113 @@
1
+ import { Context, Data, Effect, Layer } from "effect";
2
+ import { toAuthUserId, toEmail, toEpochMs, toEpochSeconds, toJwtToken, toSessionToken } from "@capxul/types";
3
+ //#region src/ports/auth-cache.ts
4
+ var AuthCacheError = class extends Data.TaggedError("AuthCacheError") {};
5
+ var AuthCachePortTag = class extends Context.Tag("@capxul/sdk/ports/AuthCachePort")() {};
6
+ //#endregion
7
+ //#region src/adapters/auth-cache/serialization.ts
8
+ function parseAuthSession(raw) {
9
+ if (typeof raw !== "object" || raw === null) return null;
10
+ const candidate = raw;
11
+ try {
12
+ return {
13
+ authUserId: toAuthUserId(candidate.authUserId),
14
+ email: toEmail(candidate.email),
15
+ token: toSessionToken(candidate.token),
16
+ expiresAt: toEpochMs(candidate.expiresAt)
17
+ };
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+ function parseCachedJwt(raw) {
23
+ if (typeof raw !== "object" || raw === null) return null;
24
+ const candidate = raw;
25
+ try {
26
+ return {
27
+ token: toJwtToken(candidate.token),
28
+ expEpochSeconds: toEpochSeconds(candidate.expEpochSeconds)
29
+ };
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ //#endregion
35
+ //#region src/adapters/auth-cache/BrowserAuthCacheAdapter.ts
36
+ const SESSION_KEY = "capxul.session";
37
+ const JWT_KEY = "capxul.jwt";
38
+ var BrowserAuthCacheAdapter = class {
39
+ storage;
40
+ constructor(storage) {
41
+ this.storage = storage;
42
+ }
43
+ getSession = authCacheTry("getSession", () => {
44
+ const raw = this.storage.getItem(SESSION_KEY);
45
+ if (raw === null) return null;
46
+ try {
47
+ return parseAuthSession(JSON.parse(raw));
48
+ } catch {
49
+ return null;
50
+ }
51
+ });
52
+ setSession = (session) => authCacheTry("setSession", () => {
53
+ this.storage.setItem(SESSION_KEY, JSON.stringify(session));
54
+ });
55
+ clearSession = authCacheTry("clearSession", () => {
56
+ this.storage.removeItem(SESSION_KEY);
57
+ });
58
+ getJwt = authCacheTry("getJwt", () => {
59
+ const raw = this.storage.getItem(JWT_KEY);
60
+ if (raw === null) return null;
61
+ try {
62
+ return parseCachedJwt(JSON.parse(raw));
63
+ } catch {
64
+ return null;
65
+ }
66
+ });
67
+ setJwt = (jwt) => authCacheTry("setJwt", () => {
68
+ this.storage.setItem(JWT_KEY, JSON.stringify(jwt));
69
+ });
70
+ clearJwt = authCacheTry("clearJwt", () => {
71
+ this.storage.removeItem(JWT_KEY);
72
+ });
73
+ };
74
+ function toAuthCacheError(operation, cause) {
75
+ return new AuthCacheError({
76
+ operation,
77
+ cause
78
+ });
79
+ }
80
+ function authCacheTry(operation, run) {
81
+ return Effect.try({
82
+ try: run,
83
+ catch: (cause) => toAuthCacheError(operation, cause)
84
+ });
85
+ }
86
+ //#endregion
87
+ //#region src/adapters/auth-cache/InMemoryAuthCacheAdapter.ts
88
+ var InMemoryAuthCacheAdapter = class {
89
+ session = null;
90
+ jwt = null;
91
+ getSession = Effect.sync(() => this.session);
92
+ setSession = (session) => Effect.sync(() => {
93
+ this.session = session;
94
+ });
95
+ clearSession = Effect.sync(() => {
96
+ this.session = null;
97
+ });
98
+ getJwt = Effect.sync(() => this.jwt);
99
+ setJwt = (jwt) => Effect.sync(() => {
100
+ this.jwt = jwt;
101
+ });
102
+ clearJwt = Effect.sync(() => {
103
+ this.jwt = null;
104
+ });
105
+ };
106
+ Layer.effect(AuthCachePortTag, Effect.sync(() => new InMemoryAuthCacheAdapter()).pipe(Effect.mapError((cause) => new AuthCacheError({
107
+ operation: "initialize",
108
+ cause
109
+ }))));
110
+ //#endregion
111
+ export { AuthCacheError as a, parseCachedJwt as i, BrowserAuthCacheAdapter as n, AuthCachePortTag as o, parseAuthSession as r, InMemoryAuthCacheAdapter as t };
112
+
113
+ //# sourceMappingURL=InMemoryAuthCacheAdapter-BK-B_ERB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InMemoryAuthCacheAdapter-BK-B_ERB.mjs","names":[],"sources":["../src/ports/auth-cache.ts","../src/adapters/auth-cache/serialization.ts","../src/adapters/auth-cache/BrowserAuthCacheAdapter.ts","../src/adapters/auth-cache/InMemoryAuthCacheAdapter.ts"],"sourcesContent":["import { Context, Data, Effect } from \"effect\";\nimport type { AuthSession, EpochSeconds, JwtToken } from \"@capxul/types\";\n\n/**\n * AuthCachePort (TA4) — replaces `SessionStoragePort` in the rebuild slice's\n * consumer-facing wiring. Stores the user-snapshot Session AND the cached\n * Convex JWT (W7 bridge endpoint). The two slots are independent so a session\n * refresh doesn't invalidate the JWT and vice versa.\n *\n * Three adapters (TA7):\n * - `BrowserAuthCacheAdapter` — backed by `localStorage`\n * - `FileSystemAuthCacheAdapter` — mode-0600 JSON file in `~/.config/capxul/`\n * - `InMemoryAuthCacheAdapter` — for tests\n *\n * `SessionStoragePort` was retired by the Stage 4 rebuild; session and JWT\n * persistence now share this cache boundary.\n */\nexport interface AuthCachePort {\n readonly getSession: Effect.Effect<AuthSession | null, AuthCacheError>;\n readonly setSession: (session: AuthSession) => Effect.Effect<void, AuthCacheError>;\n readonly clearSession: Effect.Effect<void, AuthCacheError>;\n\n /**\n * JWT cache for the `/api/auth/convex/token` bridge endpoint (W7).\n * Stored separately from the session so a session refresh doesn't\n * invalidate cached JWT.\n */\n readonly getJwt: Effect.Effect<CachedJwt | null, AuthCacheError>;\n readonly setJwt: (jwt: CachedJwt) => Effect.Effect<void, AuthCacheError>;\n readonly clearJwt: Effect.Effect<void, AuthCacheError>;\n}\n\n/**\n * Cached Convex JWT shape (TA4). `expEpochSeconds` is decoded from the JWT's\n * `exp` claim at fetch time so the `tokenProvider` cache eviction logic can\n * proactively refresh at `exp - 30s` per TA3.\n */\nexport interface CachedJwt {\n readonly token: JwtToken;\n readonly expEpochSeconds: EpochSeconds;\n}\n\nexport class AuthCacheError extends Data.TaggedError(\"AuthCacheError\")<{\n readonly operation: string;\n readonly cause: unknown;\n}> {}\n\nexport class AuthCachePortTag extends Context.Tag(\"@capxul/sdk/ports/AuthCachePort\")<\n AuthCachePortTag,\n AuthCachePort\n>() {}\n","import {\n toAuthUserId,\n toEmail,\n toEpochMs,\n toEpochSeconds,\n toJwtToken,\n toSessionToken,\n type AuthSession,\n} from \"@capxul/types\";\n\nimport type { CachedJwt } from \"../../ports/auth-cache\";\n\nexport function parseAuthSession(raw: unknown): AuthSession | null {\n if (typeof raw !== \"object\" || raw === null) return null;\n const candidate = raw as {\n readonly authUserId?: unknown;\n readonly email?: unknown;\n readonly token?: unknown;\n readonly expiresAt?: unknown;\n };\n\n try {\n return {\n authUserId: toAuthUserId(candidate.authUserId),\n email: toEmail(candidate.email),\n token: toSessionToken(candidate.token),\n expiresAt: toEpochMs(candidate.expiresAt),\n };\n } catch {\n return null;\n }\n}\n\nexport function parseCachedJwt(raw: unknown): CachedJwt | null {\n if (typeof raw !== \"object\" || raw === null) return null;\n const candidate = raw as {\n readonly token?: unknown;\n readonly expEpochSeconds?: unknown;\n };\n\n try {\n return {\n token: toJwtToken(candidate.token),\n expEpochSeconds: toEpochSeconds(candidate.expEpochSeconds),\n };\n } catch {\n return null;\n }\n}\n","import { Effect, Layer } from \"effect\";\nimport type { AuthSession } from \"@capxul/types\";\n\nimport {\n AuthCacheError,\n AuthCachePortTag,\n type AuthCachePort,\n type CachedJwt,\n} from \"../../ports/auth-cache\";\nimport { parseAuthSession, parseCachedJwt } from \"./serialization\";\n\nexport interface BrowserStorageShape {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nconst SESSION_KEY = \"capxul.session\";\nconst JWT_KEY = \"capxul.jwt\";\n\nexport class BrowserAuthCacheAdapter implements AuthCachePort {\n private readonly storage: BrowserStorageShape;\n\n constructor(storage: BrowserStorageShape) {\n this.storage = storage;\n }\n\n readonly getSession = authCacheTry(\"getSession\", () => {\n const raw = this.storage.getItem(SESSION_KEY);\n if (raw === null) return null;\n try {\n return parseAuthSession(JSON.parse(raw));\n } catch {\n return null;\n }\n });\n\n readonly setSession = (session: AuthSession) =>\n authCacheTry(\"setSession\", () => {\n this.storage.setItem(SESSION_KEY, JSON.stringify(session));\n });\n\n readonly clearSession = authCacheTry(\"clearSession\", () => {\n this.storage.removeItem(SESSION_KEY);\n });\n\n readonly getJwt = authCacheTry(\"getJwt\", () => {\n const raw = this.storage.getItem(JWT_KEY);\n if (raw === null) return null;\n try {\n return parseCachedJwt(JSON.parse(raw));\n } catch {\n return null;\n }\n });\n\n readonly setJwt = (jwt: CachedJwt) =>\n authCacheTry(\"setJwt\", () => {\n this.storage.setItem(JWT_KEY, JSON.stringify(jwt));\n });\n\n readonly clearJwt = authCacheTry(\"clearJwt\", () => {\n this.storage.removeItem(JWT_KEY);\n });\n}\n\nexport function BrowserAuthCacheLayer(input: {\n readonly storage: BrowserStorageShape;\n}): Layer.Layer<AuthCachePortTag, AuthCacheError> {\n return Layer.effect(\n AuthCachePortTag,\n Effect.sync(() => new BrowserAuthCacheAdapter(input.storage)).pipe(\n Effect.mapError((cause) => toAuthCacheError(\"initialize\", cause)),\n ),\n );\n}\n\nfunction toAuthCacheError(operation: string, cause: unknown): AuthCacheError {\n return new AuthCacheError({ operation, cause });\n}\n\nfunction authCacheTry<T>(operation: string, run: () => T): Effect.Effect<T, AuthCacheError> {\n return Effect.try({\n try: run,\n catch: (cause) => toAuthCacheError(operation, cause),\n });\n}\n","import { Effect, Layer } from \"effect\";\nimport type { AuthSession } from \"@capxul/types\";\n\nimport {\n AuthCacheError,\n AuthCachePortTag,\n type AuthCachePort,\n type CachedJwt,\n} from \"../../ports/auth-cache\";\n\nexport class InMemoryAuthCacheAdapter implements AuthCachePort {\n private session: AuthSession | null = null;\n private jwt: CachedJwt | null = null;\n\n readonly getSession = Effect.sync(() => this.session);\n\n readonly setSession = (session: AuthSession) =>\n Effect.sync(() => {\n this.session = session;\n });\n\n readonly clearSession = Effect.sync(() => {\n this.session = null;\n });\n\n readonly getJwt = Effect.sync(() => this.jwt);\n\n readonly setJwt = (jwt: CachedJwt) =>\n Effect.sync(() => {\n this.jwt = jwt;\n });\n\n readonly clearJwt = Effect.sync(() => {\n this.jwt = null;\n });\n}\n\nexport const InMemoryAuthCacheLayer = Layer.effect(\n AuthCachePortTag,\n Effect.sync(() => new InMemoryAuthCacheAdapter()).pipe(\n Effect.mapError((cause) => new AuthCacheError({ operation: \"initialize\", cause })),\n ),\n);\n"],"mappings":";;;AA0CA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,EAGlE,CAAC;AAEJ,IAAa,mBAAb,cAAsC,QAAQ,IAAI,iCAAiC,EAGjF,EAAE,CAAC;;;ACtCL,SAAgB,iBAAiB,KAAkC;CACjE,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM,OAAO;CACpD,MAAM,YAAY;CAOlB,IAAI;EACF,OAAO;GACL,YAAY,aAAa,UAAU,UAAU;GAC7C,OAAO,QAAQ,UAAU,KAAK;GAC9B,OAAO,eAAe,UAAU,KAAK;GACrC,WAAW,UAAU,UAAU,SAAS;EAC1C;CACF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,eAAe,KAAgC;CAC7D,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM,OAAO;CACpD,MAAM,YAAY;CAKlB,IAAI;EACF,OAAO;GACL,OAAO,WAAW,UAAU,KAAK;GACjC,iBAAiB,eAAe,UAAU,eAAe;EAC3D;CACF,QAAQ;EACN,OAAO;CACT;AACF;;;AC/BA,MAAM,cAAc;AACpB,MAAM,UAAU;AAEhB,IAAa,0BAAb,MAA8D;CAC5D;CAEA,YAAY,SAA8B;EACxC,KAAK,UAAU;CACjB;CAEA,aAAsB,aAAa,oBAAoB;EACrD,MAAM,MAAM,KAAK,QAAQ,QAAQ,WAAW;EAC5C,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI;GACF,OAAO,iBAAiB,KAAK,MAAM,GAAG,CAAC;EACzC,QAAQ;GACN,OAAO;EACT;CACF,CAAC;CAED,cAAuB,YACrB,aAAa,oBAAoB;EAC/B,KAAK,QAAQ,QAAQ,aAAa,KAAK,UAAU,OAAO,CAAC;CAC3D,CAAC;CAEH,eAAwB,aAAa,sBAAsB;EACzD,KAAK,QAAQ,WAAW,WAAW;CACrC,CAAC;CAED,SAAkB,aAAa,gBAAgB;EAC7C,MAAM,MAAM,KAAK,QAAQ,QAAQ,OAAO;EACxC,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI;GACF,OAAO,eAAe,KAAK,MAAM,GAAG,CAAC;EACvC,QAAQ;GACN,OAAO;EACT;CACF,CAAC;CAED,UAAmB,QACjB,aAAa,gBAAgB;EAC3B,KAAK,QAAQ,QAAQ,SAAS,KAAK,UAAU,GAAG,CAAC;CACnD,CAAC;CAEH,WAAoB,aAAa,kBAAkB;EACjD,KAAK,QAAQ,WAAW,OAAO;CACjC,CAAC;AACH;AAaA,SAAS,iBAAiB,WAAmB,OAAgC;CAC3E,OAAO,IAAI,eAAe;EAAE;EAAW;CAAM,CAAC;AAChD;AAEA,SAAS,aAAgB,WAAmB,KAAgD;CAC1F,OAAO,OAAO,IAAI;EAChB,KAAK;EACL,QAAQ,UAAU,iBAAiB,WAAW,KAAK;CACrD,CAAC;AACH;;;AC5EA,IAAa,2BAAb,MAA+D;CAC7D,UAAsC;CACtC,MAAgC;CAEhC,aAAsB,OAAO,WAAW,KAAK,OAAO;CAEpD,cAAuB,YACrB,OAAO,WAAW;EAChB,KAAK,UAAU;CACjB,CAAC;CAEH,eAAwB,OAAO,WAAW;EACxC,KAAK,UAAU;CACjB,CAAC;CAED,SAAkB,OAAO,WAAW,KAAK,GAAG;CAE5C,UAAmB,QACjB,OAAO,WAAW;EAChB,KAAK,MAAM;CACb,CAAC;CAEH,WAAoB,OAAO,WAAW;EACpC,KAAK,MAAM;CACb,CAAC;AACH;AAEsC,MAAM,OAC1C,kBACA,OAAO,WAAW,IAAI,yBAAyB,CAAC,EAAE,KAChD,OAAO,UAAU,UAAU,IAAI,eAAe;CAAE,WAAW;CAAc;AAAM,CAAC,CAAC,CACnF,CACF"}