samva 0.0.1 → 0.2.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.
Files changed (79) hide show
  1. package/AGENTS.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +392 -5
  4. package/dist/categories-CsUa8ojo.js +143 -0
  5. package/dist/client/client/client.gen.d.ts +2 -0
  6. package/dist/client/client/index.d.ts +10 -0
  7. package/dist/client/client/types.gen.d.ts +120 -0
  8. package/dist/client/client/utils.gen.d.ts +37 -0
  9. package/dist/client/client.gen.d.ts +12 -0
  10. package/dist/client/core/auth.gen.d.ts +25 -0
  11. package/dist/client/core/bodySerializer.gen.d.ts +25 -0
  12. package/dist/client/core/params.gen.d.ts +43 -0
  13. package/dist/client/core/pathSerializer.gen.d.ts +33 -0
  14. package/dist/client/core/queryKeySerializer.gen.d.ts +18 -0
  15. package/dist/client/core/serverSentEvents.gen.d.ts +71 -0
  16. package/dist/client/core/types.gen.d.ts +83 -0
  17. package/dist/client/core/utils.gen.d.ts +19 -0
  18. package/dist/client/sdk.gen.d.ts +1558 -0
  19. package/dist/client/types.gen.d.ts +7917 -0
  20. package/dist/client-BqkU-sFv.js +6049 -0
  21. package/dist/effect/api-keys.d.ts +190 -0
  22. package/dist/effect/api-keys.js +27 -0
  23. package/dist/effect/campaigns.d.ts +2055 -0
  24. package/dist/effect/campaigns.js +33 -0
  25. package/dist/effect/categories.d.ts +65 -0
  26. package/dist/effect/categories.js +2 -0
  27. package/dist/effect/client.d.ts +9 -0
  28. package/dist/effect/client.js +13 -0
  29. package/dist/effect/contacts.d.ts +1278 -0
  30. package/dist/effect/contacts.js +39 -0
  31. package/dist/effect/conversations.d.ts +1604 -0
  32. package/dist/effect/conversations.js +23 -0
  33. package/dist/effect/design.d.ts +209 -0
  34. package/dist/effect/design.js +13 -0
  35. package/dist/effect/email.d.ts +1022 -0
  36. package/dist/effect/email.js +111 -0
  37. package/dist/effect/generated-metadata.d.ts +54 -0
  38. package/dist/effect/generated.d.ts +32156 -0
  39. package/dist/effect/index.d.ts +36 -0
  40. package/dist/effect/index.js +17 -0
  41. package/dist/effect/internal/client.d.ts +35 -0
  42. package/dist/effect/internal/operation.d.ts +466 -0
  43. package/dist/effect/messages.d.ts +1047 -0
  44. package/dist/effect/messages.js +23 -0
  45. package/dist/effect/operations.d.ts +97 -0
  46. package/dist/effect/operations.js +15 -0
  47. package/dist/effect/organizations.d.ts +559 -0
  48. package/dist/effect/organizations.js +21 -0
  49. package/dist/effect/pagination.d.ts +37 -0
  50. package/dist/effect/retry.d.ts +60 -0
  51. package/dist/effect/retry.js +112 -0
  52. package/dist/effect/runtime.d.ts +7 -0
  53. package/dist/effect/scheduled-messages.d.ts +1009 -0
  54. package/dist/effect/scheduled-messages.js +17 -0
  55. package/dist/effect/templates.d.ts +2591 -0
  56. package/dist/effect/templates.js +66 -0
  57. package/dist/effect/webhooks.d.ts +228 -0
  58. package/dist/effect/webhooks.js +33 -0
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.js +2 -0
  61. package/dist/normalize-LkWl4HUF.js +82 -0
  62. package/dist/normalize.d.ts +87 -0
  63. package/dist/operation-Ccqh_yxb.js +596 -0
  64. package/dist/promises/errors.gen.d.ts +179 -0
  65. package/dist/promises/index.d.ts +153 -0
  66. package/dist/promises/index.js +2 -0
  67. package/dist/promises/types.gen.d.ts +1 -0
  68. package/dist/promises-Dgv8UmCC.js +5911 -0
  69. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  70. package/dist/service-map.d.ts +732 -0
  71. package/dist/webhooks/effect.d.ts +1 -0
  72. package/dist/webhooks/effect.js +1 -0
  73. package/dist/webhooks/promise.d.ts +1 -0
  74. package/dist/webhooks/promise.js +1 -0
  75. package/package.json +151 -4
  76. package/CLAUDE.md +0 -111
  77. package/bun.lock +0 -29
  78. package/index.ts +0 -1
  79. package/tsconfig.json +0 -29
package/AGENTS.md ADDED
@@ -0,0 +1,13 @@
1
+ # Samva SDK consumer guide
2
+
3
+ Use `createClient` from `samva` for Promise applications. Ergonomic methods return decoded values
4
+ and throw `SamvaApiError` subclasses; use `client.raw` only when request and response envelopes are
5
+ required.
6
+
7
+ Effect applications should import Pascal-cased domain modules from focused subpaths such as
8
+ `samva/effect/email` and provide `Client.layerFetch` from `samva/effect/client`. Install the optional
9
+ peer `effect@4.0.0-beta.102` when using those entrypoints. Keep that exact beta pinned. Tagged API
10
+ failures remain in the Effect error channel and can be handled with `Effect.catchTag`.
11
+
12
+ Treat exported request and response types as the API contract. Do not import package-internal files
13
+ or generated implementation paths; use only the subpaths declared in `package.json`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arya Labs, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,15 +1,402 @@
1
1
  # samva
2
2
 
3
- To install dependencies:
3
+ TypeScript SDK for Samva's public customer API.
4
+
5
+ ## Documentation
6
+
7
+ See [AGENTS.md](./AGENTS.md) for concise guidance when using this package with coding agents.
8
+
9
+ ## Overview
10
+
11
+ The root entrypoint is Promise-based:
12
+
13
+ ```typescript
14
+ import { createClient } from "samva";
15
+ ```
16
+
17
+ The same Promise SDK is also available at `samva/promises`. Native Effect users import domain modules such as `samva/effect/email` and provide `samva/effect/client`. Both clients are generated from Samva's public OpenAPI document, while the Effect runtime does not wrap or execute the Promise client. Effect is an optional peer dependency, so Promise-only applications do not install or load it.
18
+
19
+ ## Installation
4
20
 
5
21
  ```bash
6
- bun install
22
+ npm install samva
23
+ # or
24
+ bun add samva
25
+ # or
26
+ yarn add samva
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { createClient } from "samva";
33
+
34
+ const client = createClient({
35
+ baseUrl: "https://api.samva.dev",
36
+ apiKey: "<samva-api-key>",
37
+ });
38
+
39
+ const contact = await client.contacts.findOrCreate({
40
+ name: "Ada Lovelace",
41
+ email: "ada@example.com",
42
+ });
43
+
44
+ const message = await client.messages.send({
45
+ to: [contact],
46
+ channel: "email",
47
+ email: {
48
+ subject: "Hello",
49
+ html: "<p>Hello World</p>",
50
+ text: "Hello World",
51
+ },
52
+ metadata: {
53
+ externalUserId: "user_123",
54
+ },
55
+ });
56
+
57
+ const messages = await client.messages.list({
58
+ page: "1",
59
+ limit: "20",
60
+ });
7
61
  ```
8
62
 
9
- To run:
63
+ ## Effect Quick Start
64
+
65
+ Install `effect` next to `samva` when using the Effect entrypoint:
10
66
 
11
67
  ```bash
12
- bun run index.ts
68
+ bun add samva effect@4.0.0-beta.102
69
+ ```
70
+
71
+ ```typescript
72
+ import { Effect } from "effect";
73
+ import * as Client from "samva/effect/client";
74
+ import * as Email from "samva/effect/email";
75
+
76
+ const program = Email.send({
77
+ to: "ada@example.com",
78
+ subject: "Welcome",
79
+ html: "<h1>Welcome to Samva</h1>",
80
+ }).pipe(Effect.provide(Client.layerFetch({ apiKey: "<samva-api-key>" })));
81
+
82
+ const message = await Effect.runPromise(program);
83
+ ```
84
+
85
+ ## Authentication
86
+
87
+ Samva supports two client auth modes. Use one per `createClient` instance.
88
+
89
+ ### API key (automation, CI, single org)
90
+
91
+ ```typescript
92
+ const client = createClient({
93
+ apiKey: "samva_sk_live_...",
94
+ baseUrl: "https://api.samva.dev",
95
+ });
13
96
  ```
14
97
 
15
- This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
98
+ - `X-API-Key` is configured automatically from `apiKey`.
99
+ - Organization context is derived from the API key — do not send `x-org-slug`.
100
+
101
+ ### OAuth bearer (interactive, multi-org)
102
+
103
+ ```typescript
104
+ const client = createClient({
105
+ authToken: sessionAccessToken,
106
+ baseUrl: "https://api.samva.dev",
107
+ headers: { "x-org-slug": "your-org-slug" },
108
+ });
109
+ ```
110
+
111
+ - Requests use `Authorization: Bearer <authToken>`.
112
+ - The token is user-scoped; send the target organization slug on every request via the `x-org-slug` header.
113
+ - There is no SDK `orgSlug` helper — pass the header directly.
114
+
115
+ For terminal workflows (`samva login`, `samva org use`, `samva email send --dry-run`), see the [`@samva/cli` README](https://github.com/AryaLabsHQ/samva/tree/main/packages/cli).
116
+
117
+ ## Service Surface
118
+
119
+ The Promise client exposes:
120
+
121
+ - `apiKeys`
122
+ - `campaigns`
123
+ - `contacts`
124
+ - `conversations`
125
+ - `design`
126
+ - `email`
127
+ - `messages`
128
+ - `operations`
129
+ - `organizations`
130
+ - `scheduledMessages`
131
+ - `templates`
132
+ - `webhooks`
133
+
134
+ The Effect SDK exposes Pascal-cased domain modules with lower-camel operations:
135
+
136
+ - `ApiKeys`
137
+ - `Campaigns`
138
+ - `Contacts`
139
+ - `Conversations`
140
+ - `Design`
141
+ - `Email`
142
+ - `Messages`
143
+ - `Operations`
144
+ - `Organizations`
145
+ - `ScheduledMessages`
146
+ - `Templates`
147
+ - `Webhooks`
148
+
149
+ Effect operations take direct parameter objects: body, path, and query fields are not wrapped in `payload` or `params` envelopes. Import the modules you use from `samva/effect/<lower-kebab>` and provide `Client.layerFetch` once at the program boundary. `Client.raw` exposes the underlying generated client for advanced access, but ordinary workflows do not require it.
150
+
151
+ Effect failures carry Samva's real tagged errors (`RateLimitedError`, `ValidationError`, `PaymentRequiredError`, …) directly in the error channel, so `Effect.catchTag("RateLimitedError", …)` works against any operation without enumerating status codes, and each error exposes its own fields (no wrapper `cause`). The SDK also exports category helpers such as `isRetryable`, `isTransient`, `catchAuthError`, and `catchTransient`. Success values decode to domain types: annotated absolute instants become `Date`, sensitive fields become `Redacted`, and unannotated wire timestamps remain strings.
152
+
153
+ ### Default-on retry
154
+
155
+ The Effect client retries safe operations out of the box, so you do not wrap calls in `Effect.retry`:
156
+
157
+ - **What retries** — read operations (`GET`/`HEAD`) and sends. Sends are safe because the client attaches an `Idempotency-Key` (below), so the server deduplicates a replayed attempt. Every other mutating operation (`create`, `update`, `remove`, …) is keyless and is never auto-retried.
158
+ - **When** — only for `isRetryable` failures: throttling (`429`), transient server errors (including a transient `BillingProviderError`), and request-transport failures. `ValidationError`, `ConflictError`, `NotFound`, auth, and billing-limit `PaymentRequiredError` failures never retry.
159
+ - **How** — jittered exponential backoff, bounded to four attempts total. A `429` honors the server's `retryAfterSeconds` hint (capped at 60s) in place of the computed delay.
160
+
161
+ Retry policy is read from context via the `Retry.Service` service. When it is absent, the default applies, so no operation gains a requirement. Override it with a Layer:
162
+
163
+ ```typescript
164
+ import { Effect } from "effect";
165
+ import { isRetryable } from "samva/effect/categories";
166
+ import * as Retry from "samva/effect/retry";
167
+
168
+ // Turn auto-retry off for a program:
169
+ program.pipe(Effect.provide(Retry.layerDisabled));
170
+
171
+ // Or install a custom policy (any Effect.retry options):
172
+ program.pipe(Effect.provide(Retry.layer({ times: 6, while: isRetryable })));
173
+ ```
174
+
175
+ ### Idempotency keys
176
+
177
+ Each `messages.send` / `email.send` call generates an `Idempotency-Key` per logical call — stable across the built-in retries, distinct across calls — so a retried send never delivers twice. Pass an explicit key for cross-process deduplication (safe to replay the same request from a different machine):
178
+
179
+ ```typescript
180
+ const send = Email.send(input, { idempotencyKey: "order-4417-receipt" });
181
+ ```
182
+
183
+ Reusing a key with identical content replays the original response; reusing it with different content fails with `ConflictError`.
184
+
185
+ ### Stream pagination
186
+
187
+ Every list operation that returns the uniform `{ items, pagination: { page, limit, total, totalPages } }` envelope carries two `Stream` companions, so you traverse pages without hand-rolling a `page` loop:
188
+
189
+ - **`list.items(input?)`** — a `Stream` of individual items across all pages.
190
+ - **`list.pages(input?)`** — a `Stream` of raw page envelopes.
191
+
192
+ ```typescript
193
+ import { Stream } from "effect";
194
+ import * as Contacts from "samva/effect/contacts";
195
+
196
+ // Every contact, one page fetched at a time:
197
+ const allContacts = Stream.runCollect(Contacts.list.items());
198
+
199
+ // Bounded consumption is lazy — this fetches only as many pages as it needs:
200
+ const firstFifty = Stream.runCollect(Contacts.list.items().pipe(Stream.take(50)));
201
+
202
+ // Caller params (start page, limit, filters) are respected:
203
+ const activeFrom2 = Contacts.list.items({ page: "2", limit: "100", status: "active" });
204
+ ```
205
+
206
+ The companions are attached uniformly to every paginated list, including `Messages.list`, `Contacts.list`, `Campaigns.list`, `Email.listDomains`, and `Webhooks.list`. Each page fetch reuses the ambient `Retry` policy. Reads retry per page; pagination does not nest retries. Page and limit inputs retain their OpenAPI wire-string representation. The Promise client is unchanged; stream pagination is Effect-only.
207
+
208
+ ### Redacted secrets
209
+
210
+ Secret-bearing response fields — the plaintext API key returned by `apiKeys.create` / `apiKeys.rotate`, and the webhook signing secret from `webhooks.regenerateSecret` — decode to `Redacted<string>` on the Effect surface. The secret never leaks through logs, `String(...)`, or `JSON.stringify`; unwrap it explicitly with `Redacted.value` when you need the plaintext:
211
+
212
+ ```typescript
213
+ import { Effect, Redacted } from "effect";
214
+ import * as ApiKeys from "samva/effect/api-keys";
215
+
216
+ const inspectKey = Effect.gen(function* () {
217
+ const created = yield* ApiKeys.create({ name: "CI" });
218
+ `${created.key}`; // "<redacted>" — safe to log
219
+ return Redacted.value(created.key); // the plaintext key, unwrapped on purpose
220
+ });
221
+ ```
222
+
223
+ The wire body and the Promise client keep plain strings; only the Effect surface is `Redacted`.
224
+
225
+ ## OpenAPI vendor extensions
226
+
227
+ The published `openapi.json` carries machine-readable semantics on top of the standard schema, derived from the same contract annotations the Effect SDK uses (no second taxonomy):
228
+
229
+ | Extension | Where | Meaning |
230
+ | -------------- | ----------------- | ------------------------------------------------------------------------------------------------- |
231
+ | `x-category` | error schemas | The error's semantic categories (e.g. `["throttling", "transient"]`). Omitted when uncategorized. |
232
+ | `x-retryable` | error schemas | `true` when the error is safe to retry. |
233
+ | `x-idempotent` | operations | `true` when the operation accepts an `Idempotency-Key` header. |
234
+ | `x-sensitive` | schema properties | `true` for secret-bearing fields (plain `string` on the wire; `Redacted` in the Effect SDK). |
235
+
236
+ These let MCP and third-party consumers read retry/idempotency/secret semantics straight from the spec.
237
+
238
+ ## Usage Examples
239
+
240
+ ### Send Email Through Unified Messages
241
+
242
+ ```typescript
243
+ const contact = await client.contacts.findOrCreate({
244
+ name: "Ada Lovelace",
245
+ email: "ada@example.com",
246
+ });
247
+
248
+ const response = await client.messages.send({
249
+ to: [contact],
250
+ channel: "email",
251
+ email: {
252
+ subject: "Welcome",
253
+ html: "<h1>Welcome to Samva</h1>",
254
+ text: "Welcome to Samva",
255
+ },
256
+ metadata: {
257
+ campaign: "welcome",
258
+ },
259
+ });
260
+ ```
261
+
262
+ ### Native Effect Send Email
263
+
264
+ ```typescript
265
+ import * as Email from "samva/effect/email";
266
+
267
+ const message =
268
+ yield *
269
+ Email.send({
270
+ to: "ada@example.com",
271
+ subject: "Welcome",
272
+ html: "<h1>Welcome to Samva</h1>",
273
+ });
274
+ ```
275
+
276
+ ### List Conversations
277
+
278
+ ```typescript
279
+ const response = await client.conversations.list({
280
+ page: "1",
281
+ limit: "20",
282
+ status: "active",
283
+ });
284
+ ```
285
+
286
+ ### Schedule a Message
287
+
288
+ ```typescript
289
+ const scheduled = await client.scheduledMessages.create({
290
+ send: {
291
+ to: [{ email: "ada@example.com" }],
292
+ channel: "email",
293
+ email: { subject: "Reminder", html: "<p>See you tomorrow.</p>" },
294
+ },
295
+ scheduledFor: "2026-08-01T09:00:00Z",
296
+ });
297
+
298
+ // Later, cancel it before it dispatches
299
+ await client.scheduledMessages.cancel({ id: scheduled.id });
300
+ ```
301
+
302
+ ### Create and Run a Campaign
303
+
304
+ ```typescript
305
+ const campaign = await client.campaigns.create({
306
+ name: "August newsletter",
307
+ channel: "email",
308
+ content: {
309
+ channel: "email",
310
+ email: { subject: "What's new", html: "<h1>Updates</h1>" },
311
+ },
312
+ audience: { includeTags: ["subscribers"], excludeTags: ["bounced"] },
313
+ });
314
+
315
+ const run = await client.campaigns.scheduleRun({
316
+ id: campaign.id,
317
+ scheduledFor: "2026-08-01T09:00:00Z",
318
+ });
319
+
320
+ const recipients = await client.campaigns.listRecipients({
321
+ id: campaign.id,
322
+ runId: run.id,
323
+ });
324
+ ```
325
+
326
+ ### Custom Headers
327
+
328
+ ```typescript
329
+ const client = createClient({
330
+ apiKey: "<samva-api-key>",
331
+ headers: {
332
+ "X-Custom-Header": "value",
333
+ },
334
+ });
335
+ ```
336
+
337
+ ### Error Handling
338
+
339
+ ```typescript
340
+ import { RateLimitedError, SamvaApiError, SamvaTransportError } from "samva";
341
+
342
+ try {
343
+ const message = await client.email.send({
344
+ to: "ada@example.com",
345
+ subject: "Test",
346
+ html: "<p>Test</p>",
347
+ });
348
+ console.log(message.id);
349
+ } catch (error) {
350
+ if (error instanceof RateLimitedError) {
351
+ console.error(`Retry after ${error.retryAfterSeconds} seconds`);
352
+ } else if (error instanceof SamvaApiError) {
353
+ console.error(error._tag, error.status, error.message);
354
+ } else if (error instanceof SamvaTransportError) {
355
+ console.error("Transport failure", error.cause);
356
+ } else {
357
+ throw error;
358
+ }
359
+ }
360
+ ```
361
+
362
+ Every ergonomic method returns its decoded success value and throws a generated `SamvaApiError`
363
+ subclass for failures declared by OpenAPI. Network and malformed-response failures throw
364
+ `SamvaTransportError`.
365
+
366
+ ### Raw response access
367
+
368
+ ```typescript
369
+ const result = await client.raw.messages.send({
370
+ body: {
371
+ to: [{ contactId: "contact-id" }],
372
+ channel: "sms",
373
+ sms: { text: "Hello" },
374
+ },
375
+ });
376
+
377
+ if (result.error) console.error(result.response?.status, result.error);
378
+ ```
379
+
380
+ Use `client.raw` only when you need the generated `{ data, error, request, response }` envelope or
381
+ transport-level request options.
382
+
383
+ ## Type Safety
384
+
385
+ All request and response types are generated from OpenAPI schemas:
386
+
387
+ ```typescript
388
+ import type { MessagesSendData } from "samva";
389
+
390
+ const requestData: MessagesSendData = {
391
+ body: {
392
+ to: [{ contactId: "contact-id" }],
393
+ channel: "email",
394
+ email: {
395
+ subject: "Hello",
396
+ html: "<p>Hello World</p>",
397
+ },
398
+ },
399
+ };
400
+
401
+ const response = await client.messages.send(requestData.body);
402
+ ```
@@ -0,0 +1,143 @@
1
+ import { Effect } from "effect";
2
+ import * as Predicate$1 from "effect/Predicate";
3
+ //#region src/effect/generated-metadata.ts
4
+ const SamvaErrorMetadata = {
5
+ BillingProviderError: {
6
+ categories: ["transient"],
7
+ retryable: true
8
+ },
9
+ ConflictError: {
10
+ categories: ["conflict"],
11
+ retryable: false
12
+ },
13
+ EmailChannelError: {
14
+ categories: [],
15
+ retryable: false
16
+ },
17
+ ExternalServiceError: {
18
+ categories: ["transient"],
19
+ retryable: true
20
+ },
21
+ FlagDisabledError: {
22
+ categories: [],
23
+ retryable: false
24
+ },
25
+ ForbiddenError: {
26
+ categories: ["auth"],
27
+ retryable: false
28
+ },
29
+ InternalError: {
30
+ categories: ["transient"],
31
+ retryable: true
32
+ },
33
+ PaymentRequiredError: {
34
+ categories: ["billing"],
35
+ retryable: false
36
+ },
37
+ RateLimitedError: {
38
+ categories: ["throttling", "transient"],
39
+ retryable: true
40
+ },
41
+ ResourceNotFoundError: {
42
+ categories: ["notFound"],
43
+ retryable: false
44
+ },
45
+ UnauthorizedError: {
46
+ categories: ["auth"],
47
+ retryable: false
48
+ },
49
+ ValidationError: {
50
+ categories: ["validation"],
51
+ retryable: false
52
+ },
53
+ WebhookEnqueueError: {
54
+ categories: ["transient"],
55
+ retryable: true
56
+ }
57
+ };
58
+ //#endregion
59
+ //#region src/effect/categories.ts
60
+ /**
61
+ * Semantic error-category helpers for the Effect SDK.
62
+ *
63
+ * Every operation's error channel carries the server's real tagged errors
64
+ * ({@link import("./index").ConflictError} …). These helpers let callers catch
65
+ * or test by *category* — auth, validation, throttling, transient … — without
66
+ * enumerating tags, and cover the raw transport/decode errors too. The category
67
+ * assignments are generated from the OpenAPI category extensions.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { catchAuthError, isRetryable } from "samva/effect";
72
+ *
73
+ * samva.messages.send(input).pipe(
74
+ * catchAuthError((error) => Effect.logError(`auth failed: ${error.message}`)),
75
+ * Effect.retry({ while: isRetryable }),
76
+ * );
77
+ * ```
78
+ */
79
+ const emailChannelCategories = (reason) => {
80
+ switch (reason) {
81
+ case "auth": return ["auth"];
82
+ case "rejected": return ["validation"];
83
+ case "rate_limited": return ["throttling", "transient"];
84
+ case "transport": return ["transient"];
85
+ default: return [];
86
+ }
87
+ };
88
+ const categoriesOf = (error) => {
89
+ if (!Predicate$1.isObject(error) || typeof error._tag !== "string") return [];
90
+ if (error._tag === "EmailChannelError") return emailChannelCategories(error.reason);
91
+ return SamvaErrorMetadata[error._tag]?.categories ?? [];
92
+ };
93
+ const hasCategory = (error, category) => categoriesOf(error).includes(category);
94
+ const isRetryableError = (error) => {
95
+ if (!Predicate$1.isObject(error) || typeof error._tag !== "string") return false;
96
+ if (error._tag === "EmailChannelError") return categoriesOf(error).includes("transient");
97
+ return SamvaErrorMetadata[error._tag]?.retryable ?? false;
98
+ };
99
+ const categoryGuard = (category) => (error) => hasCategory(error, category);
100
+ const isAuthError = categoryGuard("auth");
101
+ const isValidationError = categoryGuard("validation");
102
+ const isNotFoundError = categoryGuard("notFound");
103
+ const isConflictError = categoryGuard("conflict");
104
+ const isThrottlingError = categoryGuard("throttling");
105
+ const isBillingError = categoryGuard("billing");
106
+ /**
107
+ * Network-level request failure (no HTTP response). Only `TransportError` is
108
+ * transient/retryable: `EncodeError` and `InvalidUrlError` are deterministic
109
+ * programmer errors (bad input, bad URL) that would fail identically on every
110
+ * attempt — retrying them would loop forever.
111
+ */
112
+ const isRequestTransportError = (error) => {
113
+ if (!Predicate$1.isTagged(error, "HttpClientError")) return false;
114
+ const reason = error.reason;
115
+ return Predicate$1.isObject(reason) && Predicate$1.isTagged(reason, "TransportError");
116
+ };
117
+ /** True for transient failures, including raw request-transport errors (D4). */
118
+ const isTransient = (error) => hasCategory(error, "transient") || isRequestTransportError(error);
119
+ /** True when an error is declared safe to retry (throttling / transient / transport). */
120
+ const isRetryable = (error) => isRetryableError(error) || isRequestTransportError(error);
121
+ /**
122
+ * Catch every error in `category`. The handler receives the category's error
123
+ * union — including `EmailChannelError` where its `reason` can place it in the
124
+ * category — while only total-membership classes are narrowed out of the error
125
+ * channel (`EmailChannelError` stays: instances with other reasons pass through).
126
+ */
127
+ const catchCategory = (category, handler) => (effect) => Effect.catchIf(effect, (error) => hasCategory(error, category), handler);
128
+ const catchAuthError = (handler) => catchCategory("auth", handler);
129
+ const catchValidationError = (handler) => catchCategory("validation", handler);
130
+ const catchNotFoundError = (handler) => catchCategory("notFound", handler);
131
+ const catchConflictError = (handler) => catchCategory("conflict", handler);
132
+ const catchThrottlingError = (handler) => catchCategory("throttling", handler);
133
+ const catchBillingError = (handler) => catchCategory("billing", handler);
134
+ /**
135
+ * Catch transient failures — including raw request-transport errors — for
136
+ * fallback/degrade paths. The declared error channel is left intact because
137
+ * transient covers untyped transport errors as well.
138
+ */
139
+ const catchTransient = (handler) => (effect) => Effect.catchIf(effect, (error) => isTransient(error), handler);
140
+ //#endregion
141
+ export { isTransient as _, catchNotFoundError as a, catchValidationError as c, isAuthError as d, isBillingError as f, isThrottlingError as g, isRetryable as h, catchConflictError as i, categoriesOf as l, isNotFoundError as m, catchBillingError as n, catchThrottlingError as o, isConflictError as p, catchCategory as r, catchTransient as s, catchAuthError as t, hasCategory as u, isValidationError as v };
142
+
143
+ //# sourceMappingURL=categories-CsUa8ojo.js.map
@@ -0,0 +1,2 @@
1
+ import type { Client, Config } from "./types.gen";
2
+ export declare const createClient: (config?: Config) => Client;
@@ -0,0 +1,10 @@
1
+ export type { Auth } from "../core/auth.gen";
2
+ export type { QuerySerializerOptions } from "../core/bodySerializer.gen";
3
+ export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from "../core/bodySerializer.gen";
4
+ export { buildClientParams } from "../core/params.gen";
5
+ export { serializeQueryKeyValue } from "../core/queryKeySerializer.gen";
6
+ export type { ServerSentEventsResult } from "../core/serverSentEvents.gen";
7
+ export type { ClientMeta } from "../core/types.gen";
8
+ export { createClient } from "./client.gen";
9
+ export type { Client, ClientOptions, Config, CreateClientConfig, Options, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from "./types.gen";
10
+ export { createConfig, mergeHeaders } from "./utils.gen";