samva 0.16.0 → 0.18.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.
- package/AGENTS.md +9 -1
- package/README.md +57 -1
- package/dist/{rolldown-runtime-D7D4PA-g.js → _virtual/_rolldown/runtime.js} +1 -1
- package/dist/client/client/client.gen.js +174 -0
- package/dist/client/client/utils.gen.js +177 -0
- package/dist/client/client.gen.js +8 -0
- package/dist/client/core/auth.gen.js +12 -0
- package/dist/client/core/bodySerializer.gen.js +6 -0
- package/dist/client/core/params.gen.js +77 -0
- package/dist/client/core/pathSerializer.gen.js +82 -0
- package/dist/client/core/serverSentEvents.gen.js +96 -0
- package/dist/client/core/utils.gen.js +78 -0
- package/dist/client/sdk.gen.d.ts +174 -3
- package/dist/{promises-KrA2nSLg.js → client/sdk.gen.js} +464 -2289
- package/dist/client/types.gen.d.ts +1126 -11
- package/dist/client-attribution.js +9 -0
- package/dist/effect/analytics.d.ts +6 -6
- package/dist/effect/analytics.js +11 -9
- package/dist/effect/api-keys.d.ts +13 -13
- package/dist/effect/api-keys.js +13 -12
- package/dist/effect/attachments.d.ts +4 -4
- package/dist/effect/attachments.js +9 -7
- package/dist/effect/brands.d.ts +7 -7
- package/dist/effect/brands.js +12 -10
- package/dist/effect/campaigns.d.ts +18 -18
- package/dist/effect/campaigns.js +17 -15
- package/dist/effect/categories.d.ts +2 -2
- package/dist/effect/categories.js +95 -1
- package/dist/effect/client.d.ts +1 -1
- package/dist/effect/client.js +4 -5
- package/dist/effect/contact-groups.d.ts +14 -14
- package/dist/effect/contact-groups.js +15 -13
- package/dist/effect/contacts.d.ts +77 -25
- package/dist/effect/contacts.js +27 -22
- package/dist/effect/conversations.d.ts +17 -17
- package/dist/effect/conversations.js +18 -16
- package/dist/effect/custom-fields.d.ts +4 -4
- package/dist/effect/custom-fields.js +9 -7
- package/dist/effect/email.d.ts +78 -77
- package/dist/effect/email.js +68 -65
- package/dist/effect/errors.d.ts +2 -0
- package/dist/effect/errors.js +3 -0
- package/dist/effect/events.d.ts +121 -0
- package/dist/effect/events.js +17 -0
- package/dist/effect/extension-installations.d.ts +11 -11
- package/dist/effect/extension-installations.js +14 -12
- package/dist/effect/extensions.d.ts +9 -9
- package/dist/effect/extensions.js +10 -8
- package/dist/effect/feedback.d.ts +2 -2
- package/dist/effect/feedback.js +6 -5
- package/dist/effect/generated/analytics.d.ts +715 -0
- package/dist/effect/generated/analytics.js +188 -0
- package/dist/effect/generated/api-keys.d.ts +1627 -0
- package/dist/effect/generated/api-keys.js +249 -0
- package/dist/effect/generated/attachments.d.ts +554 -0
- package/dist/effect/generated/attachments.js +125 -0
- package/dist/effect/generated/brands.d.ts +1118 -0
- package/dist/effect/generated/brands.js +155 -0
- package/dist/effect/generated/campaigns.d.ts +3783 -0
- package/dist/effect/generated/campaigns.js +491 -0
- package/dist/effect/generated/contact-events.d.ts +158 -0
- package/dist/effect/generated/contact-events.js +37 -0
- package/dist/effect/generated/contact-groups.d.ts +1445 -0
- package/dist/effect/generated/contact-groups.js +419 -0
- package/dist/effect/generated/contacts.d.ts +3789 -0
- package/dist/effect/generated/contacts.js +709 -0
- package/dist/effect/generated/conversations.d.ts +2870 -0
- package/dist/effect/generated/conversations.js +461 -0
- package/dist/effect/generated/custom-fields.d.ts +562 -0
- package/dist/effect/generated/custom-fields.js +162 -0
- package/dist/effect/generated/email.d.ts +10622 -0
- package/dist/effect/generated/email.js +2347 -0
- package/dist/effect/generated/events.d.ts +463 -0
- package/dist/effect/generated/events.js +70 -0
- package/dist/effect/generated/extension-installations.d.ts +1786 -0
- package/dist/effect/generated/extension-installations.js +382 -0
- package/dist/effect/generated/extensions.d.ts +927 -0
- package/dist/effect/generated/extensions.js +203 -0
- package/dist/effect/generated/feedback.d.ts +116 -0
- package/dist/effect/generated/feedback.js +43 -0
- package/dist/effect/generated/index.d.ts +61 -0
- package/dist/effect/generated/journeys.d.ts +6135 -0
- package/dist/effect/generated/journeys.js +464 -0
- package/dist/effect/generated/mailboxes.d.ts +13641 -0
- package/dist/effect/generated/mailboxes.js +2312 -0
- package/dist/effect/generated/media.d.ts +741 -0
- package/dist/effect/generated/media.js +181 -0
- package/dist/effect/generated/messages.d.ts +2355 -0
- package/dist/effect/generated/messages.js +671 -0
- package/dist/effect/generated/namespaces.d.ts +654 -0
- package/dist/effect/generated/namespaces.js +135 -0
- package/dist/effect/generated/operations.d.ts +514 -0
- package/dist/effect/generated/operations.js +80 -0
- package/dist/effect/generated/organizations.d.ts +1111 -0
- package/dist/effect/generated/organizations.js +231 -0
- package/dist/effect/generated/scheduled-messages.d.ts +1798 -0
- package/dist/effect/generated/scheduled-messages.js +190 -0
- package/dist/effect/generated/shared.d.ts +3250 -0
- package/dist/effect/generated/shared.js +1053 -0
- package/dist/effect/generated/subscriptions.d.ts +188 -0
- package/dist/effect/generated/subscriptions.js +61 -0
- package/dist/effect/generated/templates.d.ts +8177 -0
- package/dist/effect/generated/templates.js +2214 -0
- package/dist/effect/generated/unsubscribe-groups.d.ts +1153 -0
- package/dist/effect/generated/unsubscribe-groups.js +267 -0
- package/dist/effect/generated/webhooks.d.ts +1697 -0
- package/dist/effect/generated/webhooks.js +500 -0
- package/dist/effect/generated/whoami.d.ts +291 -0
- package/dist/effect/generated/whoami.js +54 -0
- package/dist/effect/generated-metadata.d.ts +1627 -0
- package/dist/effect/generated-metadata.js +919 -0
- package/dist/effect/index.d.ts +3 -2
- package/dist/effect/index.js +33 -31
- package/dist/effect/internal/client.d.ts +10 -7
- package/dist/effect/internal/client.js +69 -0
- package/dist/effect/internal/email-sender.d.ts +17 -0
- package/dist/effect/internal/email-sender.js +21 -0
- package/dist/effect/internal/mailbox-events.js +33 -0
- package/dist/effect/internal/operation.d.ts +12 -72
- package/dist/effect/internal/operation.js +116 -0
- package/dist/effect/internal/send.d.ts +60 -0
- package/dist/effect/internal/send.js +56 -0
- package/dist/effect/journeys.d.ts +2153 -0
- package/dist/effect/journeys.js +39 -0
- package/dist/effect/mailboxes.d.ts +80 -80
- package/dist/effect/mailboxes.js +135 -3
- package/dist/effect/media.d.ts +5 -5
- package/dist/effect/media.js +10 -8
- package/dist/effect/messages.d.ts +16 -16
- package/dist/effect/messages.js +15 -12
- package/dist/effect/namespaces.d.ts +7 -7
- package/dist/effect/namespaces.js +10 -8
- package/dist/effect/operations.d.ts +5 -5
- package/dist/effect/operations.js +8 -6
- package/dist/effect/organizations.d.ts +7 -7
- package/dist/effect/organizations.js +12 -10
- package/dist/effect/pagination.js +39 -0
- package/dist/effect/retry.js +3 -3
- package/dist/effect/runtime.js +25 -0
- package/dist/effect/scheduled-messages.d.ts +7 -7
- package/dist/effect/scheduled-messages.js +10 -8
- package/dist/effect/subscriptions.d.ts +1 -1
- package/dist/effect/subscriptions.js +5 -4
- package/dist/effect/templates.d.ts +40 -40
- package/dist/effect/templates.js +39 -37
- package/dist/effect/unsubscribe-groups.d.ts +11 -11
- package/dist/effect/unsubscribe-groups.js +14 -12
- package/dist/effect/webhooks.d.ts +23 -23
- package/dist/effect/webhooks.js +16 -14
- package/dist/effect/whoami.d.ts +1 -1
- package/dist/effect/whoami.js +5 -4
- package/dist/index.js +4 -3
- package/dist/{inline-attachments-Cegg6tTF.js → inline-attachments.js} +13 -7
- package/dist/{mailbox-events-VKR4f3qq.js → mailbox-events.js} +2 -2
- package/dist/{normalize-B79uJIeb.js → normalize.js} +2 -2
- package/dist/package.js +6 -0
- package/dist/promises/errors.gen.js +1340 -0
- package/dist/promises/index.d.ts +7 -3
- package/dist/promises/index.js +288 -3
- package/dist/promises/types.gen.d.ts +1 -1
- package/dist/service-map.d.ts +85 -0
- package/dist/{operation-BTXjPrwf.js → service-map.js} +58 -219
- package/package.json +17 -3
- package/dist/categories-DV51kr2H.js +0 -187
- package/dist/client-BMUew5Wp.js +0 -13794
- package/dist/effect/generated.d.ts +0 -64004
- package/dist/mailboxes-Thp5z0oX.js +0 -161
package/dist/effect/index.d.ts
CHANGED
|
@@ -29,7 +29,9 @@ export * as CustomFields from "./custom-fields";
|
|
|
29
29
|
export * as Email from "./email";
|
|
30
30
|
export * as ExtensionInstallations from "./extension-installations";
|
|
31
31
|
export * as Extensions from "./extensions";
|
|
32
|
+
export * as Events from "./events";
|
|
32
33
|
export * as Feedback from "./feedback";
|
|
34
|
+
export * as Journeys from "./journeys";
|
|
33
35
|
export * as Mailboxes from "./mailboxes";
|
|
34
36
|
export * as Media from "./media";
|
|
35
37
|
export * as Messages from "./messages";
|
|
@@ -43,8 +45,7 @@ export * as Templates from "./templates";
|
|
|
43
45
|
export * as UnsubscribeGroups from "./unsubscribe-groups";
|
|
44
46
|
export * as Webhooks from "./webhooks";
|
|
45
47
|
export * as Whoami from "./whoami";
|
|
46
|
-
export
|
|
48
|
+
export * from "./errors";
|
|
47
49
|
export { type ErrorCategory, categoriesOf, hasCategory, isAuthError, isValidationError, isNotFoundError, isConflictError, isThrottlingError, isBillingError, isTransient, isRetryable, catchCategory, catchAuthError, catchValidationError, catchNotFoundError, catchConflictError, catchThrottlingError, catchBillingError, catchTransient, } from "./categories";
|
|
48
|
-
export { MediaUploadError } from "../inline-attachments";
|
|
49
50
|
export type { CursorPageEnvelope } from "./pagination";
|
|
50
51
|
export type { Json as JsonValue } from "effect/Schema";
|
package/dist/effect/index.js
CHANGED
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
|
|
1
|
+
import { MediaUploadError } from "../inline-attachments.js";
|
|
2
|
+
import { AnalyticsQueryError, BillingProviderError, BrandAddressRequiredError, CampaignFooterRequiredError, ConflictError, DeliveryUnavailableError, EmailChannelError, ExternalServiceError, FlagDisabledError, ForbiddenError, GatewayTimeoutError, InternalError, MailboxChannelUnsupportedError, MailboxSendConflictError, MailboxSendRefusedError, OnboardingReviewRequiredError, PaymentRequiredError, RateLimitedError, ResourceNotFoundError, SenderNotReadyError, UnauthorizedError, ValidationError } from "./generated/shared.js";
|
|
3
|
+
import { catchAuthError, catchBillingError, catchCategory, catchConflictError, catchNotFoundError, catchThrottlingError, catchTransient, catchValidationError, categoriesOf, hasCategory, isAuthError, isBillingError, isConflictError, isNotFoundError, isRetryable, isThrottlingError, isTransient, isValidationError } from "./categories.js";
|
|
4
|
+
import { retry_exports } from "./retry.js";
|
|
5
|
+
import { analytics_exports } from "./analytics.js";
|
|
6
|
+
import { api_keys_exports } from "./api-keys.js";
|
|
7
|
+
import { attachments_exports } from "./attachments.js";
|
|
8
|
+
import { brands_exports } from "./brands.js";
|
|
9
|
+
import { campaigns_exports } from "./campaigns.js";
|
|
10
|
+
import { client_exports } from "./client.js";
|
|
11
|
+
import { contact_groups_exports } from "./contact-groups.js";
|
|
12
|
+
import { contacts_exports } from "./contacts.js";
|
|
13
|
+
import { conversations_exports } from "./conversations.js";
|
|
14
|
+
import { custom_fields_exports } from "./custom-fields.js";
|
|
15
|
+
import { email_exports } from "./email.js";
|
|
16
|
+
import { extension_installations_exports } from "./extension-installations.js";
|
|
17
|
+
import { extensions_exports } from "./extensions.js";
|
|
18
|
+
import { events_exports } from "./events.js";
|
|
19
|
+
import { feedback_exports } from "./feedback.js";
|
|
20
|
+
import { journeys_exports } from "./journeys.js";
|
|
21
|
+
import { mailboxes_exports } from "./mailboxes.js";
|
|
22
|
+
import { media_exports } from "./media.js";
|
|
23
|
+
import { messages_exports } from "./messages.js";
|
|
24
|
+
import { namespaces_exports } from "./namespaces.js";
|
|
25
|
+
import { operations_exports } from "./operations.js";
|
|
26
|
+
import { organizations_exports } from "./organizations.js";
|
|
27
|
+
import { scheduled_messages_exports } from "./scheduled-messages.js";
|
|
28
|
+
import { subscriptions_exports } from "./subscriptions.js";
|
|
29
|
+
import { templates_exports } from "./templates.js";
|
|
30
|
+
import { unsubscribe_groups_exports } from "./unsubscribe-groups.js";
|
|
31
|
+
import { webhooks_exports } from "./webhooks.js";
|
|
32
|
+
import { whoami_exports } from "./whoami.js";
|
|
33
|
+
export { analytics_exports as Analytics, AnalyticsQueryError, api_keys_exports as ApiKeys, attachments_exports as Attachments, BillingProviderError, BrandAddressRequiredError, brands_exports as Brands, CampaignFooterRequiredError, campaigns_exports as Campaigns, client_exports as Client, ConflictError, contact_groups_exports as ContactGroups, contacts_exports as Contacts, conversations_exports as Conversations, custom_fields_exports as CustomFields, DeliveryUnavailableError, email_exports as Email, EmailChannelError, events_exports as Events, extension_installations_exports as ExtensionInstallations, extensions_exports as Extensions, ExternalServiceError, feedback_exports as Feedback, FlagDisabledError, ForbiddenError, GatewayTimeoutError, InternalError, journeys_exports as Journeys, MailboxChannelUnsupportedError, MailboxSendConflictError, MailboxSendRefusedError, mailboxes_exports as Mailboxes, media_exports as Media, MediaUploadError, messages_exports as Messages, namespaces_exports as Namespaces, OnboardingReviewRequiredError, operations_exports as Operations, organizations_exports as Organizations, PaymentRequiredError, RateLimitedError, ResourceNotFoundError, retry_exports as Retry, scheduled_messages_exports as ScheduledMessages, SenderNotReadyError, subscriptions_exports as Subscriptions, templates_exports as Templates, UnauthorizedError, unsubscribe_groups_exports as UnsubscribeGroups, ValidationError, webhooks_exports as Webhooks, whoami_exports as Whoami, catchAuthError, catchBillingError, catchCategory, catchConflictError, catchNotFoundError, catchThrottlingError, catchTransient, catchValidationError, categoriesOf, hasCategory, isAuthError, isBillingError, isConflictError, isNotFoundError, isRetryable, isThrottlingError, isTransient, isValidationError };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Redacted } from "effect";
|
|
2
|
-
import { HttpApiClient } from "effect/http-api";
|
|
2
|
+
import { HttpApiClient, type HttpApiGroup } from "effect/http-api";
|
|
3
3
|
import * as HttpClient from "effect/http/HttpClient";
|
|
4
4
|
import { MediaUploadError } from "../../inline-attachments";
|
|
5
5
|
import type { MailboxEventStreamConnection } from "../../mailbox-events";
|
|
6
|
-
import { SamvaApi } from "../generated";
|
|
6
|
+
import type { SamvaApi } from "../generated/index";
|
|
7
7
|
export interface ConfigBase {
|
|
8
8
|
readonly baseUrl?: string;
|
|
9
9
|
readonly headers?: Readonly<Record<string, string | undefined>>;
|
|
@@ -15,18 +15,22 @@ export type Config = (ConfigBase & {
|
|
|
15
15
|
readonly authToken: string;
|
|
16
16
|
readonly apiKey?: never;
|
|
17
17
|
});
|
|
18
|
-
|
|
18
|
+
type Api = HttpApiClient.ForApi<typeof SamvaApi>;
|
|
19
19
|
type UnionToIntersection<Union> = (Union extends unknown ? (arg: Union) => void : never) extends (arg: infer Intersection) => void ? Intersection : never;
|
|
20
20
|
type FlattenGroups<Client> = {
|
|
21
21
|
[Group in keyof Client]: {
|
|
22
22
|
[Endpoint in keyof Client[Group]]: Client[Group][Endpoint];
|
|
23
23
|
};
|
|
24
24
|
}[keyof Client];
|
|
25
|
-
export type FlatRaw = UnionToIntersection<FlattenGroups<
|
|
25
|
+
export type FlatRaw = UnionToIntersection<FlattenGroups<Api>>;
|
|
26
26
|
export type Operation = keyof FlatRaw & string;
|
|
27
|
+
export type RawCall = (request?: unknown) => Effect.Effect<unknown, unknown>;
|
|
27
28
|
export interface Interface {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* The generated call for one endpoint. A group's client is built on the first call to any of its
|
|
31
|
+
* endpoints and reused, so a group the program never calls costs nothing.
|
|
32
|
+
*/
|
|
33
|
+
readonly endpoint: (group: HttpApiGroup.Constraint, name: Operation) => Effect.Effect<RawCall>;
|
|
30
34
|
/** The origin and headers a connection outside HTTP requests, such as the event stream, uses. */
|
|
31
35
|
readonly connection: MailboxEventStreamConnection;
|
|
32
36
|
/**
|
|
@@ -48,5 +52,4 @@ export declare class Service extends Service_base {
|
|
|
48
52
|
}
|
|
49
53
|
export declare const layer: (config: Config) => Layer.Layer<Service, never, HttpClient.HttpClient>;
|
|
50
54
|
export declare const layerFetch: (config: Config) => Layer.Layer<Service>;
|
|
51
|
-
export declare const raw: Effect.Effect<Raw, never, Service>;
|
|
52
55
|
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { CLIENT_ATTRIBUTION_HEADER, clientAttribution } from "../../client-attribution.js";
|
|
2
|
+
import { MediaUploadError, uploadContentType } from "../../inline-attachments.js";
|
|
3
|
+
import { ApiKeySecurityMiddleware } from "../generated/shared.js";
|
|
4
|
+
import { HttpApi, HttpApiClient, HttpApiMiddleware } from "effect/http-api";
|
|
5
|
+
import { Context, Effect, Layer, Redacted } from "effect";
|
|
6
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
7
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
8
|
+
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
9
|
+
//#region src/effect/internal/client.ts
|
|
10
|
+
const DEFAULT_BASE_URL = "https://api.samva.dev";
|
|
11
|
+
var Service = class extends Context.Service()("samva/Client") {};
|
|
12
|
+
const authHeaders = (config) => config.authToken !== void 0 ? { Authorization: `Bearer ${config.authToken}` } : { "x-api-key": Redacted.isRedacted(config.apiKey) ? Redacted.value(config.apiKey) : config.apiKey };
|
|
13
|
+
const AuthorizationClient = HttpApiMiddleware.layerClient(ApiKeySecurityMiddleware, ({ next, request }) => next(request));
|
|
14
|
+
const make = (config) => Effect.gen(function* () {
|
|
15
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
16
|
+
const removesAttribution = Object.entries(config.headers ?? {}).some(([name, value]) => name.toLowerCase() === "x-samva-client" && value === void 0);
|
|
17
|
+
const storage = yield* HttpClient.HttpClient;
|
|
18
|
+
const httpClient = storage.pipe(HttpClient.mapRequest((request) => {
|
|
19
|
+
let attributed = HttpClientRequest.setHeader(request, CLIENT_ATTRIBUTION_HEADER, clientAttribution("typescript-effect"));
|
|
20
|
+
if (config.headers !== void 0) attributed = HttpClientRequest.setHeaders(attributed, config.headers);
|
|
21
|
+
if (removesAttribution) attributed = HttpClientRequest.removeHeader(attributed, CLIENT_ATTRIBUTION_HEADER);
|
|
22
|
+
return HttpClientRequest.setHeaders(attributed, authHeaders(config));
|
|
23
|
+
}));
|
|
24
|
+
const groups = /* @__PURE__ */ new Map();
|
|
25
|
+
const groupClient = (group) => {
|
|
26
|
+
const built = groups.get(group.identifier);
|
|
27
|
+
if (built !== void 0) return Effect.succeed(built);
|
|
28
|
+
return HttpApiClient.group(HttpApi.make("SamvaApi").add(group), {
|
|
29
|
+
group: group.identifier,
|
|
30
|
+
httpClient,
|
|
31
|
+
baseUrl
|
|
32
|
+
}).pipe(Effect.provide(AuthorizationClient), Effect.map((client) => {
|
|
33
|
+
const calls = client;
|
|
34
|
+
groups.set(group.identifier, calls);
|
|
35
|
+
return calls;
|
|
36
|
+
}));
|
|
37
|
+
};
|
|
38
|
+
const headers = removesAttribution ? {} : { [CLIENT_ATTRIBUTION_HEADER]: clientAttribution("typescript-effect") };
|
|
39
|
+
for (const [name, value] of Object.entries(config.headers ?? {})) if (value !== void 0) headers[name] = value;
|
|
40
|
+
const putUpload = (input) => storage.execute(HttpClientRequest.make(input.method)(input.url).pipe(HttpClientRequest.bodyUint8Array(input.bytes, uploadContentType(input.headers)), HttpClientRequest.setHeaders(input.headers))).pipe(Effect.mapError((cause) => new MediaUploadError({
|
|
41
|
+
filename: input.filename,
|
|
42
|
+
mediaId: input.mediaId,
|
|
43
|
+
cause
|
|
44
|
+
})), Effect.flatMap((response) => response.status >= 200 && response.status < 300 ? Effect.void : Effect.fail(new MediaUploadError({
|
|
45
|
+
filename: input.filename,
|
|
46
|
+
mediaId: input.mediaId,
|
|
47
|
+
status: response.status
|
|
48
|
+
}))));
|
|
49
|
+
return Service.of({
|
|
50
|
+
putUpload,
|
|
51
|
+
endpoint: (group, name) => Effect.flatMap(groupClient(group), (client) => {
|
|
52
|
+
const call = client[name];
|
|
53
|
+
return call === void 0 ? Effect.die(/* @__PURE__ */ new Error(`${name} is not an endpoint of ${group.identifier}`)) : Effect.succeed(call);
|
|
54
|
+
}),
|
|
55
|
+
connection: {
|
|
56
|
+
baseUrl,
|
|
57
|
+
headers: {
|
|
58
|
+
...headers,
|
|
59
|
+
...authHeaders(config)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const layer = (config) => Layer.effect(Service, make(config));
|
|
65
|
+
const layerFetch = (config) => layer(config).pipe(Layer.provide(FetchHttpClient.layer));
|
|
66
|
+
//#endregion
|
|
67
|
+
export { Service, layer, layerFetch };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { FlatRaw, Service } from "./client";
|
|
3
|
+
import { type RequestArg } from "./operation";
|
|
4
|
+
import type { ErgonomicEmailList, SendOptions } from "./send";
|
|
5
|
+
type AddSenderRequest = NonNullable<RequestArg<FlatRaw["emailAddSender"]>>;
|
|
6
|
+
export type EmailAddSenderInput = Omit<AddSenderRequest["payload"], "replyToEmails"> & {
|
|
7
|
+
readonly replyToEmails?: ErgonomicEmailList | null | undefined;
|
|
8
|
+
};
|
|
9
|
+
export declare const addEmailSender: (input: EmailAddSenderInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
10
|
+
readonly id: string & import("effect/Brand").Brand<"EmailSenderId">;
|
|
11
|
+
readonly email: string;
|
|
12
|
+
readonly status: "failed" | "pending" | "verified";
|
|
13
|
+
readonly providerLifecycle: "active" | "deleting" | "failed" | "provisioning";
|
|
14
|
+
readonly providerFailureCode: string | null;
|
|
15
|
+
readonly createdAt: string;
|
|
16
|
+
}, import("..").ConflictError | import("..").ExternalServiceError | import("..").ForbiddenError | import("..").GatewayTimeoutError | import("effect/http/HttpClientError").HttpClientError | import("..").InternalError | import("..").ResourceNotFoundError | import("effect/Schema").SchemaError | import("..").UnauthorizedError | import("..").ValidationError, Service>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { replyToEmailsPayload } from "../../normalize.js";
|
|
2
|
+
import { generateIdempotencyKey, withRetry } from "../retry.js";
|
|
3
|
+
import { callEndpoint, stripUndefined } from "./operation.js";
|
|
4
|
+
import { EmailGroup } from "../generated/email.js";
|
|
5
|
+
import { Effect } from "effect";
|
|
6
|
+
//#region src/effect/internal/email-sender.ts
|
|
7
|
+
const addEmailSender = Effect.fn("Email.addSender")(function* (input, options) {
|
|
8
|
+
const idempotencyKey = options?.idempotencyKey ?? (yield* generateIdempotencyKey);
|
|
9
|
+
const { replyToEmails, ...sender } = input;
|
|
10
|
+
return yield* withRetry(callEndpoint(EmailGroup, "emailAddSender", {
|
|
11
|
+
headers: { "idempotency-key": idempotencyKey },
|
|
12
|
+
payload: stripUndefined({
|
|
13
|
+
...sender,
|
|
14
|
+
...replyToEmailsPayload(replyToEmails)
|
|
15
|
+
})
|
|
16
|
+
}));
|
|
17
|
+
});
|
|
18
|
+
//#endregion
|
|
19
|
+
export { addEmailSender };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=email-sender.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MailboxEventStream, MailboxEventStreamError } from "../../mailbox-events.js";
|
|
2
|
+
import { Service } from "./client.js";
|
|
3
|
+
import { Cause, Effect, Exit, Stream } from "effect";
|
|
4
|
+
//#region src/effect/internal/mailbox-events.ts
|
|
5
|
+
const toStreamError = (cause) => cause instanceof MailboxEventStreamError ? cause : new MailboxEventStreamError({
|
|
6
|
+
_tag: "StreamDisconnectedError",
|
|
7
|
+
message: String(cause)
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* The organization's mailbox events as a stream, resuming from `cursor`. An event counts as handled
|
|
11
|
+
* once the stream is pulled again, or once it ends successfully after the event (as `Stream.take`
|
|
12
|
+
* does); a failure or interruption leaves the event in hand unacknowledged, so a resume repeats it.
|
|
13
|
+
* The stream fails with a {@link MailboxEventStreamFailure}, which `Effect.catchTag` narrows by the
|
|
14
|
+
* `_tag` of the host's refusal.
|
|
15
|
+
*/
|
|
16
|
+
const events = (options = {}) => Stream.scoped(Stream.unwrap(Effect.gen(function* () {
|
|
17
|
+
const client = yield* Service;
|
|
18
|
+
const iterator = (yield* Effect.acquireRelease(Effect.try({
|
|
19
|
+
try: () => new MailboxEventStream(client.connection, options),
|
|
20
|
+
catch: toStreamError
|
|
21
|
+
}), (opened, exit) => Effect.sync(() => {
|
|
22
|
+
if (Exit.isSuccess(exit)) opened.markHandled();
|
|
23
|
+
opened.close();
|
|
24
|
+
})))[Symbol.asyncIterator]();
|
|
25
|
+
return Stream.fromEffectRepeat(Effect.tryPromise({
|
|
26
|
+
try: () => iterator.next(),
|
|
27
|
+
catch: toStreamError
|
|
28
|
+
}).pipe(Effect.flatMap((next) => next.done ? Cause.done() : Effect.succeed(next.value))));
|
|
29
|
+
})));
|
|
30
|
+
//#endregion
|
|
31
|
+
export { events };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=mailbox-events.js.map
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { type Brand, Effect, Redacted, Stream } from "effect";
|
|
2
|
-
import {
|
|
2
|
+
import type { HttpApiGroup } from "effect/http-api";
|
|
3
3
|
import { SDK_OPERATION_CALLS } from "../../service-map";
|
|
4
|
-
import { SendMessagePayload } from "../generated";
|
|
5
4
|
import { type FlatRaw, type Operation, Service } from "./client";
|
|
6
|
-
type SdkOperation = keyof typeof SDK_OPERATION_CALLS & Operation;
|
|
7
|
-
type MethodReturn<Method> = Method extends (...args: infer _Args) => infer Return ? Return : never;
|
|
5
|
+
export type SdkOperation = keyof typeof SDK_OPERATION_CALLS & Operation;
|
|
6
|
+
export type MethodReturn<Method> = Method extends (...args: infer _Args) => infer Return ? Return : never;
|
|
8
7
|
type Wire<Value> = Value extends string ? Value extends Brand.Brand<string> ? string : Value : Value extends Redacted.Redacted<infer Raw> ? Raw | Value : Value extends number | boolean | bigint | symbol | null | undefined ? Value : Value extends Date ? Value : Value extends ReadonlyArray<infer Item> ? ReadonlyArray<Wire<Item>> : Value extends object ? {
|
|
9
8
|
readonly [Key in keyof Value]: Wire<Value[Key]> | ({} extends Pick<Value, Key> ? undefined : never);
|
|
10
9
|
} : Value;
|
|
@@ -15,7 +14,7 @@ type FlattenRequest<Request> = (Request extends {
|
|
|
15
14
|
} ? Query : unknown) & (Request extends {
|
|
16
15
|
readonly payload: infer Payload;
|
|
17
16
|
} ? Payload : unknown);
|
|
18
|
-
type RequestArg<Method> = Method extends (request: infer Request, ...rest: ReadonlyArray<unknown>) => unknown ? Request : never;
|
|
17
|
+
export type RequestArg<Method> = Method extends (request: infer Request, ...rest: ReadonlyArray<unknown>) => unknown ? Request : never;
|
|
19
18
|
type Input<Name extends SdkOperation> = Wire<FlattenRequest<NonNullable<RequestArg<FlatRaw[Name]>>>>;
|
|
20
19
|
type IdempotencyKeyField<Request> = Request extends {
|
|
21
20
|
readonly headers: {
|
|
@@ -52,71 +51,12 @@ type PaginationCompanions<Parameters, A, E, R> = {} extends Parameters ? {
|
|
|
52
51
|
readonly items: (input: Parameters) => Stream.Stream<EnvelopeItem<A>, E, R>;
|
|
53
52
|
};
|
|
54
53
|
type WithPagination<Method> = Method extends (input: infer Parameters) => Effect.Effect<infer A, infer E, infer R> ? IsPaginated<A> extends true ? Method & PaginationCompanions<Parameters, A, E, R> : Method : Method;
|
|
55
|
-
export declare const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
readonly channel: "email";
|
|
64
|
-
}>;
|
|
65
|
-
type ErgonomicReplyTo = string | ReadonlyArray<string>;
|
|
66
|
-
type ErgonomicEmailList = string | ReadonlyArray<string>;
|
|
67
|
-
type ErgonomicEmailContent = Omit<EmailPayload["email"], "replyTo"> & {
|
|
68
|
-
readonly replyTo?: ErgonomicReplyTo | null | undefined;
|
|
69
|
-
};
|
|
70
|
-
export interface SendOptions {
|
|
71
|
-
readonly idempotencyKey?: string;
|
|
72
|
-
}
|
|
73
|
-
export type MessageSendInput = Omit<EmailPayload, "to" | "cc" | "bcc" | "email"> & {
|
|
74
|
-
readonly to: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
75
|
-
readonly cc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient> | null;
|
|
76
|
-
readonly bcc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient> | null;
|
|
77
|
-
readonly email: ErgonomicEmailContent;
|
|
78
|
-
};
|
|
79
|
-
export type EmailSendInput = Omit<EmailPayload, "channel" | "email" | "to" | "cc" | "bcc"> & ErgonomicEmailContent & {
|
|
80
|
-
readonly to: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
81
|
-
readonly cc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
82
|
-
readonly bcc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
83
|
-
};
|
|
84
|
-
type SendResult = MethodReturn<FlatRaw["messagesSend"]>;
|
|
85
|
-
type UploadFailure = MediaUploadError | Effect.Error<SendResult>;
|
|
86
|
-
export declare const sendMessage: (input: MessageSendInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
87
|
-
readonly id: string & Brand.Brand<"MessageId">;
|
|
88
|
-
readonly status: "bounced" | "complained" | "delivered" | "failed" | "pending" | "processing" | "queued" | "read" | "scheduled" | "sent";
|
|
89
|
-
readonly waitReason: "campaign-paused" | "content-review" | "daily-quota" | "domain-health" | "domain-warming" | "onboarding-review" | "rate" | "retry" | "screening-unavailable" | "sending-paused" | "upstream-unavailable" | null;
|
|
90
|
-
readonly reviewExpectedAt: string | null;
|
|
91
|
-
readonly skippedRecipients: readonly {
|
|
92
|
-
readonly address: string;
|
|
93
|
-
readonly reason: "bounce" | "complaint" | "group-unsubscribe" | "manual" | "unsubscribe";
|
|
94
|
-
readonly unsubscribeGroupId?: (string & Brand.Brand<"UnsubscribeId">) | null;
|
|
95
|
-
}[];
|
|
96
|
-
readonly createdAt: string;
|
|
97
|
-
}, UploadFailure, Service>;
|
|
98
|
-
export declare const sendEmail: (input: EmailSendInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
99
|
-
readonly id: string & Brand.Brand<"MessageId">;
|
|
100
|
-
readonly status: "bounced" | "complained" | "delivered" | "failed" | "pending" | "processing" | "queued" | "read" | "scheduled" | "sent";
|
|
101
|
-
readonly waitReason: "campaign-paused" | "content-review" | "daily-quota" | "domain-health" | "domain-warming" | "onboarding-review" | "rate" | "retry" | "screening-unavailable" | "sending-paused" | "upstream-unavailable" | null;
|
|
102
|
-
readonly reviewExpectedAt: string | null;
|
|
103
|
-
readonly skippedRecipients: readonly {
|
|
104
|
-
readonly address: string;
|
|
105
|
-
readonly reason: "bounce" | "complaint" | "group-unsubscribe" | "manual" | "unsubscribe";
|
|
106
|
-
readonly unsubscribeGroupId?: (string & Brand.Brand<"UnsubscribeId">) | null;
|
|
107
|
-
}[];
|
|
108
|
-
readonly createdAt: string;
|
|
109
|
-
}, UploadFailure, Service>;
|
|
110
|
-
type AddSenderRequest = NonNullable<RequestArg<FlatRaw["emailAddSender"]>>;
|
|
111
|
-
export type EmailAddSenderInput = Omit<AddSenderRequest["payload"], "replyToEmails"> & {
|
|
112
|
-
readonly replyToEmails?: ErgonomicEmailList | null | undefined;
|
|
113
|
-
};
|
|
114
|
-
export declare const addEmailSender: (input: EmailAddSenderInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
115
|
-
readonly id: string & Brand.Brand<"EmailSenderId">;
|
|
116
|
-
readonly email: string;
|
|
117
|
-
readonly status: "failed" | "pending" | "verified";
|
|
118
|
-
readonly providerLifecycle: "active" | "deleting" | "failed" | "provisioning";
|
|
119
|
-
readonly providerFailureCode: string | null;
|
|
120
|
-
readonly createdAt: string;
|
|
121
|
-
}, import("..").ConflictError | import("..").ExternalServiceError | import("..").ForbiddenError | import("..").GatewayTimeoutError | import("effect/http/HttpClientError").HttpClientError | import("..").InternalError | import("..").ResourceNotFoundError | import("effect/Schema").SchemaError | import("..").UnauthorizedError | import("..").ValidationError, Service>;
|
|
54
|
+
export declare const stripUndefined: <Value>(input: Value) => Value;
|
|
55
|
+
/**
|
|
56
|
+
* Operation factory for one API group. Each domain module binds its own group, so importing it
|
|
57
|
+
* loads that group's schemas and nothing else.
|
|
58
|
+
*/
|
|
59
|
+
export declare const forGroup: (group: HttpApiGroup.Constraint) => <Name extends SdkOperation>(operationName: Name) => WithPagination<OperationFunction<Name>>;
|
|
60
|
+
/** Call one endpoint with an already-shaped request, outside the direct-operation adapter. */
|
|
61
|
+
export declare const callEndpoint: (group: HttpApiGroup.Constraint, name: SdkOperation, request: unknown) => Effect.Effect<unknown, unknown, Service>;
|
|
122
62
|
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { SDK_OPERATION_CALLS } from "../../service-map.js";
|
|
2
|
+
import { SamvaOperations } from "../generated-metadata.js";
|
|
3
|
+
import { streamCursorItems, streamCursorPages } from "../pagination.js";
|
|
4
|
+
import { withRetry } from "../retry.js";
|
|
5
|
+
import { Service } from "./client.js";
|
|
6
|
+
import { Effect, Predicate } from "effect";
|
|
7
|
+
//#region src/effect/internal/operation.ts
|
|
8
|
+
const READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
9
|
+
const MODULE_NAMES = {
|
|
10
|
+
analytics: "Analytics",
|
|
11
|
+
apiKeys: "ApiKeys",
|
|
12
|
+
attachments: "Attachments",
|
|
13
|
+
brands: "Brands",
|
|
14
|
+
campaigns: "Campaigns",
|
|
15
|
+
contactGroups: "ContactGroups",
|
|
16
|
+
contacts: "Contacts",
|
|
17
|
+
conversations: "Conversations",
|
|
18
|
+
customFields: "CustomFields",
|
|
19
|
+
email: "Email",
|
|
20
|
+
extensions: "Extensions",
|
|
21
|
+
feedback: "Feedback",
|
|
22
|
+
mailboxes: "Mailboxes",
|
|
23
|
+
media: "Media",
|
|
24
|
+
messages: "Messages",
|
|
25
|
+
namespaces: "Namespaces",
|
|
26
|
+
operations: "Operations",
|
|
27
|
+
organizations: "Organizations",
|
|
28
|
+
scheduledMessages: "ScheduledMessages",
|
|
29
|
+
templates: "Templates",
|
|
30
|
+
unsubscribe: "UnsubscribeGroups",
|
|
31
|
+
webhooks: "Webhooks",
|
|
32
|
+
whoami: "Whoami"
|
|
33
|
+
};
|
|
34
|
+
const traceName = (operationName) => {
|
|
35
|
+
for (const [prefix, moduleName] of Object.entries(MODULE_NAMES)) {
|
|
36
|
+
if (!operationName.startsWith(prefix)) continue;
|
|
37
|
+
const method = operationName.slice(prefix.length);
|
|
38
|
+
return `${moduleName}.${method[0].toLowerCase()}${method.slice(1)}`;
|
|
39
|
+
}
|
|
40
|
+
return `Samva.${operationName}`;
|
|
41
|
+
};
|
|
42
|
+
const stripUndefined = (input) => {
|
|
43
|
+
if (!Predicate.isObject(input)) return input;
|
|
44
|
+
if (Array.isArray(input)) return input.map(stripUndefined);
|
|
45
|
+
if (Object.getPrototypeOf(input) !== Object.prototype) return input;
|
|
46
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0).map(([key, value]) => [key, stripUndefined(value)]));
|
|
47
|
+
};
|
|
48
|
+
const adapt = (operationName, rawCall, input) => {
|
|
49
|
+
const descriptor = SDK_OPERATION_CALLS[operationName];
|
|
50
|
+
const call = READ_METHODS.has(SamvaOperations[operationName].method) ? (request) => withRetry(rawCall(request)) : rawCall;
|
|
51
|
+
if (descriptor.kind === "none") return call();
|
|
52
|
+
if (descriptor.kind === "params") return call({ query: stripUndefined(input) });
|
|
53
|
+
if (descriptor.kind === "payload") return call({ payload: stripUndefined(input) });
|
|
54
|
+
if (descriptor.kind === "idempotentPayload") {
|
|
55
|
+
const { idempotencyKey, ...payload } = input;
|
|
56
|
+
return call({
|
|
57
|
+
headers: { "idempotency-key": idempotencyKey },
|
|
58
|
+
payload: stripUndefined(payload)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const pathDescriptor = descriptor;
|
|
62
|
+
const inputRecord = input ?? {};
|
|
63
|
+
const params = {};
|
|
64
|
+
for (const path of pathDescriptor.paths) {
|
|
65
|
+
if (inputRecord[path] === void 0) return Effect.die(/* @__PURE__ */ new Error(`${operationName} requires path field ${path}`));
|
|
66
|
+
params[path] = inputRecord[path];
|
|
67
|
+
}
|
|
68
|
+
if (pathDescriptor.kind === "path") return call({ params });
|
|
69
|
+
if (pathDescriptor.kind === "idempotentPath") return call({
|
|
70
|
+
params,
|
|
71
|
+
headers: { "idempotency-key": inputRecord.idempotencyKey }
|
|
72
|
+
});
|
|
73
|
+
const optionalKeyHeaders = { headers: inputRecord.idempotencyKey === void 0 ? {} : { "idempotency-key": inputRecord.idempotencyKey } };
|
|
74
|
+
if (pathDescriptor.kind === "optionalIdempotentPath") return call({
|
|
75
|
+
params,
|
|
76
|
+
...optionalKeyHeaders
|
|
77
|
+
});
|
|
78
|
+
const headerKey = pathDescriptor.kind === "optionalIdempotentPathPayload";
|
|
79
|
+
const remainingInput = stripUndefined(Object.fromEntries(Object.entries(inputRecord).filter(([key]) => !pathDescriptor.paths.includes(key) && !(headerKey && key === "idempotencyKey"))));
|
|
80
|
+
if (pathDescriptor.kind === "optionalIdempotentPathPayload") return call({
|
|
81
|
+
params,
|
|
82
|
+
payload: remainingInput,
|
|
83
|
+
...optionalKeyHeaders
|
|
84
|
+
});
|
|
85
|
+
return call({
|
|
86
|
+
params,
|
|
87
|
+
[pathDescriptor.kind === "pathParams" ? "query" : "payload"]: remainingInput
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const isCursorPageEnvelope = (value) => Predicate.isObject(value) && Array.isArray(value.items) && (value.nextCursor === null || typeof value.nextCursor === "string");
|
|
91
|
+
/**
|
|
92
|
+
* Operation factory for one API group. Each domain module binds its own group, so importing it
|
|
93
|
+
* loads that group's schemas and nothing else.
|
|
94
|
+
*/
|
|
95
|
+
const forGroup = (group) => (operationName) => {
|
|
96
|
+
if (SamvaOperations[operationName].group !== group.identifier) throw new Error(`${operationName} is not an endpoint of ${group.identifier}`);
|
|
97
|
+
return make(group, operationName);
|
|
98
|
+
};
|
|
99
|
+
/** Call one endpoint with an already-shaped request, outside the direct-operation adapter. */
|
|
100
|
+
const callEndpoint = (group, name, request) => Effect.flatMap(Service, (client) => Effect.flatMap(client.endpoint(group, name), (call) => call(request)));
|
|
101
|
+
const make = (group, operationName) => {
|
|
102
|
+
const call = Effect.fn(traceName(operationName))((input) => Effect.flatMap(Service, (client) => Effect.flatMap(client.endpoint(group, operationName), (rawCall) => adapt(operationName, rawCall, input))));
|
|
103
|
+
const operation = call;
|
|
104
|
+
if (SamvaOperations[operationName].paginated) {
|
|
105
|
+
const list = (input) => Effect.filterOrElse(call(input), isCursorPageEnvelope, () => Effect.die(/* @__PURE__ */ new Error(`${operationName} did not return a cursor page envelope`)));
|
|
106
|
+
Object.assign(operation, {
|
|
107
|
+
pages: (input) => streamCursorPages(list, input ?? {}),
|
|
108
|
+
items: (input) => streamCursorItems(list, input ?? {})
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return operation;
|
|
112
|
+
};
|
|
113
|
+
//#endregion
|
|
114
|
+
export { callEndpoint, forGroup, stripUndefined };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=operation.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { type MediaUploadError } from "../../inline-attachments";
|
|
3
|
+
import type { SendMessagePayload } from "../generated/scheduled-messages";
|
|
4
|
+
import { type FlatRaw, Service } from "./client";
|
|
5
|
+
import { type MethodReturn } from "./operation";
|
|
6
|
+
type MessagePayload = typeof SendMessagePayload.Encoded;
|
|
7
|
+
export type MessageRecipient = MessagePayload["to"][number];
|
|
8
|
+
type ContactObjectRecipient = {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
};
|
|
11
|
+
type ErgonomicMessageRecipient = MessageRecipient | ContactObjectRecipient | string;
|
|
12
|
+
type EmailPayload = Extract<MessagePayload, {
|
|
13
|
+
readonly channel: "email";
|
|
14
|
+
}>;
|
|
15
|
+
type ErgonomicReplyTo = string | ReadonlyArray<string>;
|
|
16
|
+
export type ErgonomicEmailList = string | ReadonlyArray<string>;
|
|
17
|
+
type ErgonomicEmailContent = Omit<EmailPayload["email"], "replyTo"> & {
|
|
18
|
+
readonly replyTo?: ErgonomicReplyTo | null | undefined;
|
|
19
|
+
};
|
|
20
|
+
export interface SendOptions {
|
|
21
|
+
readonly idempotencyKey?: string;
|
|
22
|
+
}
|
|
23
|
+
export type MessageSendInput = Omit<EmailPayload, "to" | "cc" | "bcc" | "email"> & {
|
|
24
|
+
readonly to: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
25
|
+
readonly cc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient> | null;
|
|
26
|
+
readonly bcc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient> | null;
|
|
27
|
+
readonly email: ErgonomicEmailContent;
|
|
28
|
+
};
|
|
29
|
+
export type EmailSendInput = Omit<EmailPayload, "channel" | "email" | "to" | "cc" | "bcc"> & ErgonomicEmailContent & {
|
|
30
|
+
readonly to: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
31
|
+
readonly cc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
32
|
+
readonly bcc?: ErgonomicMessageRecipient | ReadonlyArray<ErgonomicMessageRecipient>;
|
|
33
|
+
};
|
|
34
|
+
type SendResult = MethodReturn<FlatRaw["messagesSend"]>;
|
|
35
|
+
type UploadFailure = MediaUploadError | Effect.Error<SendResult>;
|
|
36
|
+
export declare const sendMessage: (input: MessageSendInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
37
|
+
readonly id: string & import("effect/Brand").Brand<"MessageId">;
|
|
38
|
+
readonly status: "bounced" | "complained" | "delivered" | "failed" | "pending" | "processing" | "queued" | "read" | "scheduled" | "sent";
|
|
39
|
+
readonly waitReason: "campaign-paused" | "content-review" | "daily-quota" | "domain-health" | "domain-warming" | "onboarding-review" | "rate" | "retry" | "screening-unavailable" | "sending-paused" | "upstream-unavailable" | null;
|
|
40
|
+
readonly reviewExpectedAt: string | null;
|
|
41
|
+
readonly skippedRecipients: readonly {
|
|
42
|
+
readonly address: string;
|
|
43
|
+
readonly reason: "bounce" | "complaint" | "group-unsubscribe" | "manual" | "unsubscribe";
|
|
44
|
+
readonly unsubscribeGroupId?: (string & import("effect/Brand").Brand<"UnsubscribeId">) | null;
|
|
45
|
+
}[];
|
|
46
|
+
readonly createdAt: string;
|
|
47
|
+
}, UploadFailure, Service>;
|
|
48
|
+
export declare const sendEmail: (input: EmailSendInput, options?: SendOptions | undefined) => Effect.Effect<{
|
|
49
|
+
readonly id: string & import("effect/Brand").Brand<"MessageId">;
|
|
50
|
+
readonly status: "bounced" | "complained" | "delivered" | "failed" | "pending" | "processing" | "queued" | "read" | "scheduled" | "sent";
|
|
51
|
+
readonly waitReason: "campaign-paused" | "content-review" | "daily-quota" | "domain-health" | "domain-warming" | "onboarding-review" | "rate" | "retry" | "screening-unavailable" | "sending-paused" | "upstream-unavailable" | null;
|
|
52
|
+
readonly reviewExpectedAt: string | null;
|
|
53
|
+
readonly skippedRecipients: readonly {
|
|
54
|
+
readonly address: string;
|
|
55
|
+
readonly reason: "bounce" | "complaint" | "group-unsubscribe" | "manual" | "unsubscribe";
|
|
56
|
+
readonly unsubscribeGroupId?: (string & import("effect/Brand").Brand<"UnsubscribeId">) | null;
|
|
57
|
+
}[];
|
|
58
|
+
readonly createdAt: string;
|
|
59
|
+
}, UploadFailure, Service>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { mediaCreateBody, planInlineUploads, withUploadedAttachments } from "../../inline-attachments.js";
|
|
2
|
+
import { buildEmailSendPayload, buildMessagesSendPayload } from "../../normalize.js";
|
|
3
|
+
import { generateIdempotencyKey, withRetry } from "../retry.js";
|
|
4
|
+
import { Service } from "./client.js";
|
|
5
|
+
import { callEndpoint, stripUndefined } from "./operation.js";
|
|
6
|
+
import { MediaGroup } from "../generated/media.js";
|
|
7
|
+
import { MessagesGroup } from "../generated/messages.js";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
//#region src/effect/internal/send.ts
|
|
10
|
+
/**
|
|
11
|
+
* Inline attachment content over the API's request cap travels as media instead: the largest
|
|
12
|
+
* attachments are uploaded (create, put to the presigned URL, complete) and replaced by their
|
|
13
|
+
* `mediaId`. The returned payload is the one request every send attempt reuses, so nothing is
|
|
14
|
+
* uploaded twice.
|
|
15
|
+
*/
|
|
16
|
+
const uploadOversizedAttachments = (client, payload) => Effect.gen(function* () {
|
|
17
|
+
const plan = planInlineUploads(payload);
|
|
18
|
+
if (plan.length === 0) return payload;
|
|
19
|
+
const uploaded = /* @__PURE__ */ new Map();
|
|
20
|
+
for (const upload of plan) {
|
|
21
|
+
const created = yield* callEndpoint(MediaGroup, "mediaCreate", { payload: mediaCreateBody(upload) });
|
|
22
|
+
yield* client.putUpload({
|
|
23
|
+
filename: upload.filename,
|
|
24
|
+
mediaId: created.id,
|
|
25
|
+
url: created.upload.url,
|
|
26
|
+
method: created.upload.method,
|
|
27
|
+
headers: created.upload.headers,
|
|
28
|
+
bytes: upload.bytes
|
|
29
|
+
});
|
|
30
|
+
yield* callEndpoint(MediaGroup, "mediaComplete", { params: { id: created.id } });
|
|
31
|
+
uploaded.set(upload.index, {
|
|
32
|
+
mediaId: created.id,
|
|
33
|
+
upload
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return withUploadedAttachments(payload, uploaded);
|
|
37
|
+
});
|
|
38
|
+
const sendPayload = (payload, options) => Effect.gen(function* () {
|
|
39
|
+
const client = yield* Service;
|
|
40
|
+
const idempotencyKey = options?.idempotencyKey ?? (yield* generateIdempotencyKey);
|
|
41
|
+
const prepared = yield* uploadOversizedAttachments(client, payload);
|
|
42
|
+
return yield* withRetry(callEndpoint(MessagesGroup, "messagesSend", {
|
|
43
|
+
payload: stripUndefined(prepared),
|
|
44
|
+
headers: { "idempotency-key": idempotencyKey }
|
|
45
|
+
}));
|
|
46
|
+
});
|
|
47
|
+
const sendMessage = Effect.fn("Messages.send")(function* (input, options) {
|
|
48
|
+
return yield* sendPayload(buildMessagesSendPayload(input), options);
|
|
49
|
+
});
|
|
50
|
+
const sendEmail = Effect.fn("Email.send")(function* (input, options) {
|
|
51
|
+
return yield* sendPayload(buildEmailSendPayload(input), options);
|
|
52
|
+
});
|
|
53
|
+
//#endregion
|
|
54
|
+
export { sendEmail, sendMessage };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=send.js.map
|