sently 1.0.0 → 1.1.1

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/AGENTS.md +3 -2
  2. package/CHANGELOG.md +97 -0
  3. package/README.md +20 -5
  4. package/dist/chunk-z1589fjk.js.map +2 -2
  5. package/dist/core/push-types.d.ts +53 -4
  6. package/dist/transports/inbucket.d.ts +196 -0
  7. package/dist/transports/inbucket.js +3 -0
  8. package/dist/transports/inbucket.js.map +10 -0
  9. package/dist/transports/mailpit.d.ts +108 -8
  10. package/dist/transports/mailpit.js +2 -2
  11. package/dist/transports/mailpit.js.map +3 -3
  12. package/dist/transports/taqnyat-sms.d.ts +85 -0
  13. package/dist/transports/taqnyat-sms.js +2 -2
  14. package/dist/transports/taqnyat-sms.js.map +3 -3
  15. package/dist/transports/taqnyat-whatsapp.d.ts +112 -4
  16. package/dist/transports/taqnyat-whatsapp.js +2 -2
  17. package/dist/transports/taqnyat-whatsapp.js.map +3 -3
  18. package/dist/transports/webpush.d.ts +24 -1
  19. package/dist/transports/webpush.js +2 -2
  20. package/dist/transports/webpush.js.map +3 -3
  21. package/dist/webhooks/sndr.js +2 -2
  22. package/dist/webhooks/sndr.js.map +3 -3
  23. package/package.json +7 -2
  24. package/site/content/docs/ai/llms-txt.mdx +2 -0
  25. package/site/content/docs/channels/email.mdx +2 -1
  26. package/site/content/docs/channels/push.mdx +3 -1
  27. package/site/content/docs/decorators/preview.mdx +2 -1
  28. package/site/content/docs/get-started/entrypoints.mdx +1 -1
  29. package/site/content/docs/get-started/support-matrix.mdx +2 -2
  30. package/site/content/docs/guides/vendor-extras-otp.mdx +2 -1
  31. package/site/content/docs/guides/webhooks.mdx +2 -0
  32. package/site/content/docs/guides/webpush-interop.mdx +9 -1
  33. package/site/content/docs/reference/exports.mdx +1 -1
  34. package/site/content/docs/reference/push-options.mdx +22 -5
  35. package/site/content/docs/transports/inbucket.mdx +200 -0
  36. package/site/content/docs/transports/index.mdx +2 -3
  37. package/site/content/docs/transports/mailpit.mdx +114 -14
  38. package/site/content/docs/transports/meta.json +5 -4
  39. package/site/content/docs/transports/smtp.mdx +3 -2
  40. package/site/content/docs/transports/sndr.mdx +68 -5
  41. package/site/content/docs/transports/taqnyat.mdx +361 -0
  42. package/site/content/docs/transports/webpush.mdx +208 -17
  43. package/site/content/docs/transports/taqnyat-mail.mdx +0 -41
  44. package/site/content/docs/transports/taqnyat-sms.mdx +0 -41
  45. package/site/content/docs/transports/taqnyat-whatsapp.mdx +0 -40
@@ -45,7 +45,7 @@ export interface MailpitAddress {
45
45
  /** Email address. */
46
46
  Address: string;
47
47
  }
48
- /** Summary row from `GET /api/v1/messages`. */
48
+ /** Summary row from `GET /api/v1/messages` / `GET /api/v1/search`. */
49
49
  export interface MailpitMessageSummary {
50
50
  /** Mailpit message id. */
51
51
  ID: string;
@@ -66,9 +66,9 @@ export interface MailpitMessageSummary {
66
66
  /** Short plain-text snippet. */
67
67
  Snippet: string;
68
68
  }
69
- /** Response from `GET /api/v1/messages`. */
69
+ /** Response from `GET /api/v1/messages` / `GET /api/v1/search`. */
70
70
  export interface MailpitMessageList {
71
- /** Total messages stored. */
71
+ /** Total messages stored (or matching the search). */
72
72
  total: number;
73
73
  /** Unread message count. */
74
74
  unread: number;
@@ -102,13 +102,81 @@ export interface MailpitMessage {
102
102
  /** Attachment count. */
103
103
  Attachments: number;
104
104
  }
105
- /** Options for {@link MailpitTransport.messages}. */
105
+ /**
106
+ * Message headers from `GET /api/v1/message/{id}/headers`.
107
+ * Keys are header names; values are one or more header lines.
108
+ */
109
+ export type MailpitHeaders = Record<string, string[]>;
110
+ /** Aggregate counts from {@link MailpitHtmlCheck}. */
111
+ export interface MailpitHtmlCheckTotal {
112
+ /** Node count in the HTML. */
113
+ Nodes: number;
114
+ /** Partially supported checks. */
115
+ Partial: number;
116
+ /** Fully supported checks. */
117
+ Supported: number;
118
+ /** Total checks run. */
119
+ Tests: number;
120
+ /** Unsupported checks. */
121
+ Unsupported: number;
122
+ }
123
+ /** One warning row from Mailpit’s HTML checker. */
124
+ export interface MailpitHtmlCheckWarning {
125
+ /** Warning category. */
126
+ Category: string;
127
+ /** Human-readable description. */
128
+ Description: string;
129
+ /** Keyword summary. */
130
+ Keywords: string;
131
+ /** Title. */
132
+ Title: string;
133
+ /** caniemail.com URL when present. */
134
+ URL: string;
135
+ /** Per-platform support breakdown. */
136
+ Score: {
137
+ Found: number;
138
+ Partial: number;
139
+ Supported: number;
140
+ Unsupported: number;
141
+ };
142
+ }
143
+ /** Response from `GET /api/v1/message/{id}/html-check`. */
144
+ export interface MailpitHtmlCheck {
145
+ /** Platforms covered by the check. */
146
+ Platforms: Record<string, string[]>;
147
+ /** Aggregate support totals. */
148
+ Total: MailpitHtmlCheckTotal;
149
+ /** Individual warnings. */
150
+ Warnings: MailpitHtmlCheckWarning[];
151
+ }
152
+ /** One link result from Mailpit’s link checker. */
153
+ export interface MailpitLinkCheckItem {
154
+ /** Status label (e.g. `"OK"`, `"Error"`). */
155
+ Status: string;
156
+ /** HTTP status code when available. */
157
+ StatusCode: number;
158
+ /** Checked URL. */
159
+ URL: string;
160
+ }
161
+ /** Response from `GET /api/v1/message/{id}/link-check`. */
162
+ export interface MailpitLinkCheck {
163
+ /** Number of failing links. */
164
+ Errors: number;
165
+ /** Per-URL results. */
166
+ Links: MailpitLinkCheckItem[];
167
+ }
168
+ /** Options for {@link MailpitTransport.messages} / {@link MailpitTransport.search}. */
106
169
  export interface MailpitMessagesOptions {
107
170
  /** Max messages to return. */
108
171
  limit?: number;
109
172
  /** Pagination offset. */
110
173
  start?: number;
111
174
  }
175
+ /** Options for {@link MailpitTransport.linkCheck}. */
176
+ export interface MailpitLinkCheckOptions {
177
+ /** Follow HTTP redirects when checking links. Default: `false`. */
178
+ follow?: boolean;
179
+ }
112
180
  /** Error thrown when the Mailpit REST API returns a non-success response. */
113
181
  export declare class MailpitError extends SentlyError {
114
182
  readonly statusCode: number;
@@ -120,7 +188,7 @@ export declare class MailpitError extends SentlyError {
120
188
  * Development transport for [Mailpit](https://github.com/axllent/mailpit).
121
189
  *
122
190
  * Sends via SMTP (defaults: `localhost:1025`) and exposes REST helpers for
123
- * listing, reading, and deleting captured messages.
191
+ * listing, searching, reading, checking, and deleting captured messages.
124
192
  */
125
193
  export declare class MailpitTransport implements Transport {
126
194
  readonly provider = "mailpit";
@@ -150,8 +218,37 @@ export declare class MailpitTransport implements Transport {
150
218
  * Newest messages appear first.
151
219
  */
152
220
  messages(options?: MailpitMessagesOptions): Promise<MailpitMessageList>;
153
- /** Fetches a full message via `GET /api/v1/message/{id}`. */
221
+ /**
222
+ * Searches captured messages via `GET /api/v1/search`.
223
+ * Query syntax matches the Mailpit UI (`subject:`, `to:`, `tag:`, …).
224
+ */
225
+ search(query: string, options?: MailpitMessagesOptions): Promise<MailpitMessageList>;
226
+ /**
227
+ * Fetches a full message via `GET /api/v1/message/{id}`.
228
+ * Pass `"latest"` for the newest message.
229
+ */
154
230
  getMessage(id: string): Promise<MailpitMessage>;
231
+ /**
232
+ * Fetches message headers via `GET /api/v1/message/{id}/headers`.
233
+ * Pass `"latest"` for the newest message.
234
+ */
235
+ getHeaders(id: string): Promise<MailpitHeaders>;
236
+ /**
237
+ * Runs Mailpit’s HTML/CSS client-compatibility check via
238
+ * `GET /api/v1/message/{id}/html-check`.
239
+ * Pass `"latest"` for the newest message.
240
+ */
241
+ htmlCheck(id: string): Promise<MailpitHtmlCheck>;
242
+ /**
243
+ * Runs Mailpit’s link checker via `GET /api/v1/message/{id}/link-check`.
244
+ * Pass `"latest"` for the newest message.
245
+ */
246
+ linkCheck(id: string, options?: MailpitLinkCheckOptions): Promise<MailpitLinkCheck>;
247
+ /**
248
+ * Sets read status via `PUT /api/v1/messages`.
249
+ * Pass an empty `ids` array to update every message in the mailbox.
250
+ */
251
+ setRead(ids: string[], read: boolean): Promise<void>;
155
252
  /**
156
253
  * Deletes messages by id via `DELETE /api/v1/messages`.
157
254
  * Pass an empty array (or call {@link deleteAll}) to clear the inbox.
@@ -159,8 +256,11 @@ export declare class MailpitTransport implements Transport {
159
256
  deleteMessages(ids: string[]): Promise<void>;
160
257
  /** Deletes every captured message. */
161
258
  deleteAll(): Promise<void>;
259
+ private listPath;
260
+ private encodeId;
162
261
  private getSmtp;
163
262
  private apiHeaders;
164
- private apiGet;
165
- private apiDelete;
263
+ private apiFetch;
264
+ private apiJson;
265
+ private apiOk;
166
266
  }
@@ -1,3 +1,3 @@
1
- import{n as X}from"../chunk-a057jzsy.js";import"../chunk-f5x8vesr.js";import"../chunk-yg3kj723.js";import"../chunk-w4c9kj70.js";import"../chunk-1ke3nmyg.js";import{M as N}from"../chunk-d5aw5vag.js";import"../chunk-8kpgbrba.js";import{da as Q}from"../chunk-hnzmn4s4.js";import{ja as V,ka as W}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var Y="localhost",Z=1025,$="http://localhost:8025";class K extends V{statusCode;apiError;constructor(w,z,G){super(w,z===0?"CONNECTION_FAILED":W(z),{...z>0?{statusCode:z}:{},provider:"mailpit",cause:G});this.statusCode=z;this.apiError=G;this.name="MailpitError"}}class k{provider="mailpit";host;port;secure;requireTLS;auth;tls;connectionTimeout;adapter;apiUrl;apiAuth;smtp=null;constructor(w={}){if(this.host=w.host??Y,this.port=w.port??Z,this.secure=w.secure??!1,this.requireTLS=w.requireTLS??!1,w.auth!==void 0)this.auth=w.auth;if(w.tls!==void 0)this.tls=w.tls;if(w.connectionTimeout!==void 0)this.connectionTimeout=w.connectionTimeout;if(w.adapter!==void 0)this.adapter=w.adapter;if(this.apiUrl=(w.apiUrl??$).replace(/\/$/,""),w.apiAuth!==void 0)this.apiAuth=w.apiAuth}get webUrl(){return this.apiUrl}async send(w){return(await this.getSmtp()).send(w)}async verify(){return{...await(await this.getSmtp()).verify(),provider:"mailpit"}}async close(){if(this.smtp)await this.smtp.close()}async messages(w={}){let z=new URLSearchParams;if(w.limit!==void 0)z.set("limit",String(w.limit));if(w.start!==void 0)z.set("start",String(w.start));let G=z.toString(),J=G?`/api/v1/messages?${G}`:"/api/v1/messages";return this.apiGet(J)}async getMessage(w){if(!w)throw new K("Message id is required",400,{code:"INVALID_CONFIG"});return this.apiGet(`/api/v1/message/${encodeURIComponent(w)}`)}async deleteMessages(w){await this.apiDelete("/api/v1/messages",{IDs:w})}async deleteAll(){await this.deleteMessages([])}async getSmtp(){if(!this.smtp){let w=this.adapter??await N({secure:this.secure,...this.connectionTimeout!==void 0?{connectionTimeout:this.connectionTimeout}:{},...this.tls!==void 0?{tls:this.tls}:{}});this.smtp=new X({host:this.host,port:this.port,secure:this.secure,requireTLS:this.requireTLS,adapter:w,...this.auth!==void 0?{auth:this.auth}:{},...this.tls!==void 0?{tls:this.tls}:{},...this.connectionTimeout!==void 0?{connectionTimeout:this.connectionTimeout}:{}})}return this.smtp}apiHeaders(){let w={Accept:"application/json","Content-Type":"application/json"};if(this.apiAuth){let z=Q(`${this.apiAuth.user}:${this.apiAuth.pass}`).replace(/\r\n/g,"");w.Authorization=`Basic ${z}`}return w}async apiGet(w){let z;try{z=await fetch(`${this.apiUrl}${w}`,{method:"GET",headers:this.apiHeaders()})}catch(G){throw new K(G instanceof Error?G.message:"Mailpit API request failed",0,G)}if(!z.ok){let G=await z.text().catch(()=>"");throw new K(G||`Mailpit API error (${z.status})`,z.status,G)}return await z.json()}async apiDelete(w,z){let G;try{G=await fetch(`${this.apiUrl}${w}`,{method:"DELETE",headers:this.apiHeaders(),body:JSON.stringify(z)})}catch(J){throw new K(J instanceof Error?J.message:"Mailpit API request failed",0,J)}if(!G.ok){let J=await G.text().catch(()=>"");throw new K(J||`Mailpit API error (${G.status})`,G.status,J)}}}export{k as MailpitTransport,K as MailpitError,Z as MAILPIT_DEFAULT_PORT,Y as MAILPIT_DEFAULT_HOST,$ as MAILPIT_DEFAULT_API_URL};
1
+ import{n as Y}from"../chunk-a057jzsy.js";import"../chunk-f5x8vesr.js";import"../chunk-yg3kj723.js";import"../chunk-w4c9kj70.js";import"../chunk-1ke3nmyg.js";import{M as Q}from"../chunk-d5aw5vag.js";import"../chunk-8kpgbrba.js";import{da as V}from"../chunk-hnzmn4s4.js";import{ja as W,ka as X}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";var Z="localhost",$=1025,k="http://localhost:8025";class N extends W{statusCode;apiError;constructor(w,z,G){super(w,z===0?"CONNECTION_FAILED":X(z),{...z>0?{statusCode:z}:{},provider:"mailpit",cause:G});this.statusCode=z;this.apiError=G;this.name="MailpitError"}}class v{provider="mailpit";host;port;secure;requireTLS;auth;tls;connectionTimeout;adapter;apiUrl;apiAuth;smtp=null;constructor(w={}){if(this.host=w.host??Z,this.port=w.port??$,this.secure=w.secure??!1,this.requireTLS=w.requireTLS??!1,w.auth!==void 0)this.auth=w.auth;if(w.tls!==void 0)this.tls=w.tls;if(w.connectionTimeout!==void 0)this.connectionTimeout=w.connectionTimeout;if(w.adapter!==void 0)this.adapter=w.adapter;if(this.apiUrl=(w.apiUrl??k).replace(/\/$/,""),w.apiAuth!==void 0)this.apiAuth=w.apiAuth}get webUrl(){return this.apiUrl}async send(w){return(await this.getSmtp()).send(w)}async verify(){return{...await(await this.getSmtp()).verify(),provider:"mailpit"}}async close(){if(this.smtp)await this.smtp.close()}async messages(w={}){return this.apiJson("GET",this.listPath("/api/v1/messages",w))}async search(w,z={}){if(!w)throw new N("Search query is required",400,{code:"INVALID_CONFIG"});let G=new URLSearchParams;if(G.set("query",w),z.limit!==void 0)G.set("limit",String(z.limit));if(z.start!==void 0)G.set("start",String(z.start));return this.apiJson("GET",`/api/v1/search?${G.toString()}`)}async getMessage(w){return this.apiJson("GET",`/api/v1/message/${this.encodeId(w)}`)}async getHeaders(w){return this.apiJson("GET",`/api/v1/message/${this.encodeId(w)}/headers`)}async htmlCheck(w){return this.apiJson("GET",`/api/v1/message/${this.encodeId(w)}/html-check`)}async linkCheck(w,z={}){let G=new URLSearchParams;if(z.follow===!0)G.set("follow","true");let J=G.toString(),K=`/api/v1/message/${this.encodeId(w)}/link-check${J?`?${J}`:""}`;return this.apiJson("GET",K)}async setRead(w,z){await this.apiOk("PUT","/api/v1/messages",{IDs:w,Read:z})}async deleteMessages(w){await this.apiOk("DELETE","/api/v1/messages",{IDs:w})}async deleteAll(){await this.deleteMessages([])}listPath(w,z){let G=new URLSearchParams;if(z.limit!==void 0)G.set("limit",String(z.limit));if(z.start!==void 0)G.set("start",String(z.start));let J=G.toString();return J?`${w}?${J}`:w}encodeId(w){if(!w)throw new N("Message id is required",400,{code:"INVALID_CONFIG"});return encodeURIComponent(w)}async getSmtp(){if(!this.smtp){let w=this.adapter??await Q({secure:this.secure,...this.connectionTimeout!==void 0?{connectionTimeout:this.connectionTimeout}:{},...this.tls!==void 0?{tls:this.tls}:{}});this.smtp=new Y({host:this.host,port:this.port,secure:this.secure,requireTLS:this.requireTLS,adapter:w,...this.auth!==void 0?{auth:this.auth}:{},...this.tls!==void 0?{tls:this.tls}:{},...this.connectionTimeout!==void 0?{connectionTimeout:this.connectionTimeout}:{}})}return this.smtp}apiHeaders(){let w={Accept:"application/json","Content-Type":"application/json"};if(this.apiAuth){let z=V(`${this.apiAuth.user}:${this.apiAuth.pass}`).replace(/\r\n/g,"");w.Authorization=`Basic ${z}`}return w}async apiFetch(w,z,G){let J;try{J=await fetch(`${this.apiUrl}${z}`,{method:w,headers:this.apiHeaders(),...G!==void 0?{body:JSON.stringify(G)}:{}})}catch(K){throw new N(K instanceof Error?K.message:"Mailpit API request failed",0,K)}if(!J.ok){let K=await J.text().catch(()=>"");throw new N(K||`Mailpit API error (${J.status})`,J.status,K)}return J}async apiJson(w,z,G){return await(await this.apiFetch(w,z,G)).json()}async apiOk(w,z,G){await this.apiFetch(w,z,G)}}export{v as MailpitTransport,N as MailpitError,$ as MAILPIT_DEFAULT_PORT,Z as MAILPIT_DEFAULT_HOST,k as MAILPIT_DEFAULT_API_URL};
2
2
 
3
- //# debugId=A41842358B8E380464756E2164756E21
3
+ //# debugId=051518FA2E8DE88C64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/mailpit.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Mailpit development transport — SMTP to a local Mailpit catcher with\n * REST helpers for inspecting and clearing captured messages.\n *\n * Defaults match a stock Mailpit install: SMTP `localhost:1025`,\n * UI/API `http://localhost:8025`.\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/mailer\";\n * import { MailpitTransport } from \"sently/transports/mailpit\";\n *\n * const mailpit = new MailpitTransport();\n * const mailer = await createMailer({ transport: mailpit });\n *\n * await mailer.send({\n * from: \"dev@example.com\",\n * to: \"you@example.com\",\n * subject: \"Hello\",\n * text: \"Captured by Mailpit\",\n * });\n *\n * const inbox = await mailpit.messages();\n * console.log(inbox.messages[0]?.Subject);\n * ```\n */\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailOptions,\n SendResult,\n SMTPAuth,\n SocketAdapter,\n TLSOptions,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { createDefaultAdapter } from \"../detect.js\";\nimport { SMTPTransport } from \"./smtp.js\";\n\n/** Default SMTP host for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_HOST = \"localhost\";\n\n/** Default SMTP port for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_PORT = 1025;\n\n/** Default web UI / REST API base URL for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_API_URL = \"http://localhost:8025\";\n\n/** Mailpit development transport configuration. */\nexport interface MailpitConfig {\n /** SMTP hostname. Default: `\"localhost\"`. */\n host?: string;\n /** SMTP port. Default: `1025`. */\n port?: number;\n /** Use implicit TLS on connect. Default: `false` (Mailpit is plain SMTP). */\n secure?: boolean;\n /**\n * Refuse AUTH over a non-TLS connection.\n * Default: `false` so optional local SMTP auth works without TLS.\n */\n requireTLS?: boolean;\n /** Optional SMTP authentication (Mailpit can accept any credentials). */\n auth?: SMTPAuth;\n /** TLS options when STARTTLS or implicit TLS is enabled. */\n tls?: TLSOptions;\n /** Socket connect timeout in milliseconds. */\n connectionTimeout?: number;\n /** Runtime socket adapter. Auto-detected on first send when omitted. */\n adapter?: SocketAdapter;\n /**\n * Mailpit web UI / REST API base URL (no trailing slash).\n * Default: `\"http://localhost:8025\"`.\n */\n apiUrl?: string;\n /** Basic auth for the Mailpit UI/API when the instance requires it. */\n apiAuth?: { user: string; pass: string };\n}\n\n/** Address object returned by the Mailpit REST API. */\nexport interface MailpitAddress {\n /** Display name, may be empty. */\n Name: string;\n /** Email address. */\n Address: string;\n}\n\n/** Summary row from `GET /api/v1/messages`. */\nexport interface MailpitMessageSummary {\n /** Mailpit message id. */\n ID: string;\n /** MIME Message-ID. */\n MessageID: string;\n /** Sender. */\n From: MailpitAddress;\n /** Recipients. */\n To: MailpitAddress[];\n /** Subject line. */\n Subject: string;\n /** ISO created timestamp. */\n Created: string;\n /** Attachment count. */\n Attachments: number;\n /** Whether the message has been read in the UI. */\n Read: boolean;\n /** Short plain-text snippet. */\n Snippet: string;\n}\n\n/** Response from `GET /api/v1/messages`. */\nexport interface MailpitMessageList {\n /** Total messages stored. */\n total: number;\n /** Unread message count. */\n unread: number;\n /** Messages returned in this page. */\n count: number;\n /** Message summaries (newest first). */\n messages: MailpitMessageSummary[];\n}\n\n/** Full message from `GET /api/v1/message/{id}`. */\nexport interface MailpitMessage {\n /** Mailpit message id. */\n ID: string;\n /** MIME Message-ID. */\n MessageID: string;\n /** Sender. */\n From: MailpitAddress;\n /** Recipients. */\n To: MailpitAddress[];\n /** CC recipients. */\n Cc?: MailpitAddress[];\n /** BCC recipients. */\n Bcc?: MailpitAddress[];\n /** Subject line. */\n Subject: string;\n /** Plain-text body. */\n Text: string;\n /** HTML body. */\n HTML: string;\n /** ISO created timestamp. */\n Date: string;\n /** Attachment count. */\n Attachments: number;\n}\n\n/** Options for {@link MailpitTransport.messages}. */\nexport interface MailpitMessagesOptions {\n /** Max messages to return. */\n limit?: number;\n /** Pagination offset. */\n start?: number;\n}\n\n/** Error thrown when the Mailpit REST API returns a non-success response. */\nexport class MailpitError extends SentlyError {\n /** Creates a Mailpit API error with status code (`0` = network/connect failure). */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, statusCode === 0 ? \"CONNECTION_FAILED\" : httpStatusToSentlyCode(statusCode), {\n ...(statusCode > 0 ? { statusCode } : {}),\n provider: \"mailpit\",\n cause: apiError,\n });\n this.name = \"MailpitError\";\n }\n}\n\n/**\n * Development transport for [Mailpit](https://github.com/axllent/mailpit).\n *\n * Sends via SMTP (defaults: `localhost:1025`) and exposes REST helpers for\n * listing, reading, and deleting captured messages.\n */\nexport class MailpitTransport implements Transport {\n readonly provider = \"mailpit\";\n\n private readonly host: string;\n private readonly port: number;\n private readonly secure: boolean;\n private readonly requireTLS: boolean;\n private readonly auth?: SMTPAuth;\n private readonly tls?: TLSOptions;\n private readonly connectionTimeout?: number;\n private readonly adapter?: SocketAdapter;\n private readonly apiUrl: string;\n private readonly apiAuth?: { user: string; pass: string };\n\n private smtp: SMTPTransport | null = null;\n\n /** Creates a Mailpit transport with local-dev defaults. */\n constructor(config: MailpitConfig = {}) {\n this.host = config.host ?? MAILPIT_DEFAULT_HOST;\n this.port = config.port ?? MAILPIT_DEFAULT_PORT;\n this.secure = config.secure ?? false;\n this.requireTLS = config.requireTLS ?? false;\n if (config.auth !== undefined) {\n this.auth = config.auth;\n }\n if (config.tls !== undefined) {\n this.tls = config.tls;\n }\n if (config.connectionTimeout !== undefined) {\n this.connectionTimeout = config.connectionTimeout;\n }\n if (config.adapter !== undefined) {\n this.adapter = config.adapter;\n }\n this.apiUrl = (config.apiUrl ?? MAILPIT_DEFAULT_API_URL).replace(/\\/$/, \"\");\n if (config.apiAuth !== undefined) {\n this.apiAuth = config.apiAuth;\n }\n }\n\n /** Web UI base URL (same origin as the REST API). */\n get webUrl(): string {\n return this.apiUrl;\n }\n\n /** Sends an email to Mailpit over SMTP. */\n async send(options: MailOptions): Promise<SendResult> {\n return (await this.getSmtp()).send(options);\n }\n\n /** Verifies SMTP connectivity to Mailpit. */\n async verify(): Promise<VerifyResult> {\n const result = await (await this.getSmtp()).verify();\n return {\n ...result,\n provider: \"mailpit\",\n };\n }\n\n /** Closes the underlying SMTP adapter if connected. */\n async close(): Promise<void> {\n if (this.smtp) {\n await this.smtp.close();\n }\n }\n\n /**\n * Lists captured messages via `GET /api/v1/messages`.\n * Newest messages appear first.\n */\n async messages(options: MailpitMessagesOptions = {}): Promise<MailpitMessageList> {\n const params = new URLSearchParams();\n if (options.limit !== undefined) {\n params.set(\"limit\", String(options.limit));\n }\n if (options.start !== undefined) {\n params.set(\"start\", String(options.start));\n }\n const query = params.toString();\n const path = query ? `/api/v1/messages?${query}` : \"/api/v1/messages\";\n return this.apiGet<MailpitMessageList>(path);\n }\n\n /** Fetches a full message via `GET /api/v1/message/{id}`. */\n async getMessage(id: string): Promise<MailpitMessage> {\n if (!id) {\n throw new MailpitError(\"Message id is required\", 400, { code: \"INVALID_CONFIG\" });\n }\n return this.apiGet<MailpitMessage>(`/api/v1/message/${encodeURIComponent(id)}`);\n }\n\n /**\n * Deletes messages by id via `DELETE /api/v1/messages`.\n * Pass an empty array (or call {@link deleteAll}) to clear the inbox.\n */\n async deleteMessages(ids: string[]): Promise<void> {\n await this.apiDelete(\"/api/v1/messages\", { IDs: ids });\n }\n\n /** Deletes every captured message. */\n async deleteAll(): Promise<void> {\n await this.deleteMessages([]);\n }\n\n private async getSmtp(): Promise<SMTPTransport> {\n if (!this.smtp) {\n const adapter =\n this.adapter ??\n (await createDefaultAdapter({\n secure: this.secure,\n ...(this.connectionTimeout !== undefined\n ? { connectionTimeout: this.connectionTimeout }\n : {}),\n ...(this.tls !== undefined ? { tls: this.tls } : {}),\n }));\n\n this.smtp = new SMTPTransport({\n host: this.host,\n port: this.port,\n secure: this.secure,\n requireTLS: this.requireTLS,\n adapter,\n ...(this.auth !== undefined ? { auth: this.auth } : {}),\n ...(this.tls !== undefined ? { tls: this.tls } : {}),\n ...(this.connectionTimeout !== undefined\n ? { connectionTimeout: this.connectionTimeout }\n : {}),\n });\n }\n return this.smtp;\n }\n\n private apiHeaders(): HeadersInit {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n if (this.apiAuth) {\n const token = encodeBase64(`${this.apiAuth.user}:${this.apiAuth.pass}`).replace(/\\r\\n/g, \"\");\n headers.Authorization = `Basic ${token}`;\n }\n return headers;\n }\n\n private async apiGet<T>(path: string): Promise<T> {\n let response: Response;\n try {\n response = await fetch(`${this.apiUrl}${path}`, {\n method: \"GET\",\n headers: this.apiHeaders(),\n });\n } catch (err) {\n throw new MailpitError(\n err instanceof Error ? err.message : \"Mailpit API request failed\",\n 0,\n err,\n );\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new MailpitError(\n body || `Mailpit API error (${response.status})`,\n response.status,\n body,\n );\n }\n\n return (await response.json()) as T;\n }\n\n private async apiDelete(path: string, body: unknown): Promise<void> {\n let response: Response;\n try {\n response = await fetch(`${this.apiUrl}${path}`, {\n method: \"DELETE\",\n headers: this.apiHeaders(),\n body: JSON.stringify(body),\n });\n } catch (err) {\n throw new MailpitError(\n err instanceof Error ? err.message : \"Mailpit API request failed\",\n 0,\n err,\n );\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new MailpitError(\n text || `Mailpit API error (${response.status})`,\n response.status,\n text,\n );\n }\n }\n}\n"
5
+ "/**\n * @module\n * Mailpit development transport — SMTP to a local Mailpit catcher with\n * REST helpers for inspecting, searching, and checking captured messages.\n *\n * Defaults match a stock Mailpit install: SMTP `localhost:1025`,\n * UI/API `http://localhost:8025`.\n *\n * @example\n * ```ts\n * import { createMailer } from \"sently/mailer\";\n * import { MailpitTransport } from \"sently/transports/mailpit\";\n *\n * const mailpit = new MailpitTransport();\n * const mailer = await createMailer({ transport: mailpit });\n *\n * await mailer.send({\n * from: \"dev@example.com\",\n * to: \"you@example.com\",\n * subject: \"Hello\",\n * text: \"Captured by Mailpit\",\n * });\n *\n * const inbox = await mailpit.messages();\n * console.log(inbox.messages[0]?.Subject);\n * ```\n */\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type {\n MailOptions,\n SendResult,\n SMTPAuth,\n SocketAdapter,\n TLSOptions,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { createDefaultAdapter } from \"../detect.js\";\nimport { SMTPTransport } from \"./smtp.js\";\n\n/** Default SMTP host for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_HOST = \"localhost\";\n\n/** Default SMTP port for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_PORT = 1025;\n\n/** Default web UI / REST API base URL for a local Mailpit instance. */\nexport const MAILPIT_DEFAULT_API_URL = \"http://localhost:8025\";\n\n/** Mailpit development transport configuration. */\nexport interface MailpitConfig {\n /** SMTP hostname. Default: `\"localhost\"`. */\n host?: string;\n /** SMTP port. Default: `1025`. */\n port?: number;\n /** Use implicit TLS on connect. Default: `false` (Mailpit is plain SMTP). */\n secure?: boolean;\n /**\n * Refuse AUTH over a non-TLS connection.\n * Default: `false` so optional local SMTP auth works without TLS.\n */\n requireTLS?: boolean;\n /** Optional SMTP authentication (Mailpit can accept any credentials). */\n auth?: SMTPAuth;\n /** TLS options when STARTTLS or implicit TLS is enabled. */\n tls?: TLSOptions;\n /** Socket connect timeout in milliseconds. */\n connectionTimeout?: number;\n /** Runtime socket adapter. Auto-detected on first send when omitted. */\n adapter?: SocketAdapter;\n /**\n * Mailpit web UI / REST API base URL (no trailing slash).\n * Default: `\"http://localhost:8025\"`.\n */\n apiUrl?: string;\n /** Basic auth for the Mailpit UI/API when the instance requires it. */\n apiAuth?: { user: string; pass: string };\n}\n\n/** Address object returned by the Mailpit REST API. */\nexport interface MailpitAddress {\n /** Display name, may be empty. */\n Name: string;\n /** Email address. */\n Address: string;\n}\n\n/** Summary row from `GET /api/v1/messages` / `GET /api/v1/search`. */\nexport interface MailpitMessageSummary {\n /** Mailpit message id. */\n ID: string;\n /** MIME Message-ID. */\n MessageID: string;\n /** Sender. */\n From: MailpitAddress;\n /** Recipients. */\n To: MailpitAddress[];\n /** Subject line. */\n Subject: string;\n /** ISO created timestamp. */\n Created: string;\n /** Attachment count. */\n Attachments: number;\n /** Whether the message has been read in the UI. */\n Read: boolean;\n /** Short plain-text snippet. */\n Snippet: string;\n}\n\n/** Response from `GET /api/v1/messages` / `GET /api/v1/search`. */\nexport interface MailpitMessageList {\n /** Total messages stored (or matching the search). */\n total: number;\n /** Unread message count. */\n unread: number;\n /** Messages returned in this page. */\n count: number;\n /** Message summaries (newest first). */\n messages: MailpitMessageSummary[];\n}\n\n/** Full message from `GET /api/v1/message/{id}`. */\nexport interface MailpitMessage {\n /** Mailpit message id. */\n ID: string;\n /** MIME Message-ID. */\n MessageID: string;\n /** Sender. */\n From: MailpitAddress;\n /** Recipients. */\n To: MailpitAddress[];\n /** CC recipients. */\n Cc?: MailpitAddress[];\n /** BCC recipients. */\n Bcc?: MailpitAddress[];\n /** Subject line. */\n Subject: string;\n /** Plain-text body. */\n Text: string;\n /** HTML body. */\n HTML: string;\n /** ISO created timestamp. */\n Date: string;\n /** Attachment count. */\n Attachments: number;\n}\n\n/**\n * Message headers from `GET /api/v1/message/{id}/headers`.\n * Keys are header names; values are one or more header lines.\n */\nexport type MailpitHeaders = Record<string, string[]>;\n\n/** Aggregate counts from {@link MailpitHtmlCheck}. */\nexport interface MailpitHtmlCheckTotal {\n /** Node count in the HTML. */\n Nodes: number;\n /** Partially supported checks. */\n Partial: number;\n /** Fully supported checks. */\n Supported: number;\n /** Total checks run. */\n Tests: number;\n /** Unsupported checks. */\n Unsupported: number;\n}\n\n/** One warning row from Mailpit’s HTML checker. */\nexport interface MailpitHtmlCheckWarning {\n /** Warning category. */\n Category: string;\n /** Human-readable description. */\n Description: string;\n /** Keyword summary. */\n Keywords: string;\n /** Title. */\n Title: string;\n /** caniemail.com URL when present. */\n URL: string;\n /** Per-platform support breakdown. */\n Score: {\n Found: number;\n Partial: number;\n Supported: number;\n Unsupported: number;\n };\n}\n\n/** Response from `GET /api/v1/message/{id}/html-check`. */\nexport interface MailpitHtmlCheck {\n /** Platforms covered by the check. */\n Platforms: Record<string, string[]>;\n /** Aggregate support totals. */\n Total: MailpitHtmlCheckTotal;\n /** Individual warnings. */\n Warnings: MailpitHtmlCheckWarning[];\n}\n\n/** One link result from Mailpit’s link checker. */\nexport interface MailpitLinkCheckItem {\n /** Status label (e.g. `\"OK\"`, `\"Error\"`). */\n Status: string;\n /** HTTP status code when available. */\n StatusCode: number;\n /** Checked URL. */\n URL: string;\n}\n\n/** Response from `GET /api/v1/message/{id}/link-check`. */\nexport interface MailpitLinkCheck {\n /** Number of failing links. */\n Errors: number;\n /** Per-URL results. */\n Links: MailpitLinkCheckItem[];\n}\n\n/** Options for {@link MailpitTransport.messages} / {@link MailpitTransport.search}. */\nexport interface MailpitMessagesOptions {\n /** Max messages to return. */\n limit?: number;\n /** Pagination offset. */\n start?: number;\n}\n\n/** Options for {@link MailpitTransport.linkCheck}. */\nexport interface MailpitLinkCheckOptions {\n /** Follow HTTP redirects when checking links. Default: `false`. */\n follow?: boolean;\n}\n\n/** Error thrown when the Mailpit REST API returns a non-success response. */\nexport class MailpitError extends SentlyError {\n /** Creates a Mailpit API error with status code (`0` = network/connect failure). */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message, statusCode === 0 ? \"CONNECTION_FAILED\" : httpStatusToSentlyCode(statusCode), {\n ...(statusCode > 0 ? { statusCode } : {}),\n provider: \"mailpit\",\n cause: apiError,\n });\n this.name = \"MailpitError\";\n }\n}\n\n/**\n * Development transport for [Mailpit](https://github.com/axllent/mailpit).\n *\n * Sends via SMTP (defaults: `localhost:1025`) and exposes REST helpers for\n * listing, searching, reading, checking, and deleting captured messages.\n */\nexport class MailpitTransport implements Transport {\n readonly provider = \"mailpit\";\n\n private readonly host: string;\n private readonly port: number;\n private readonly secure: boolean;\n private readonly requireTLS: boolean;\n private readonly auth?: SMTPAuth;\n private readonly tls?: TLSOptions;\n private readonly connectionTimeout?: number;\n private readonly adapter?: SocketAdapter;\n private readonly apiUrl: string;\n private readonly apiAuth?: { user: string; pass: string };\n\n private smtp: SMTPTransport | null = null;\n\n /** Creates a Mailpit transport with local-dev defaults. */\n constructor(config: MailpitConfig = {}) {\n this.host = config.host ?? MAILPIT_DEFAULT_HOST;\n this.port = config.port ?? MAILPIT_DEFAULT_PORT;\n this.secure = config.secure ?? false;\n this.requireTLS = config.requireTLS ?? false;\n if (config.auth !== undefined) {\n this.auth = config.auth;\n }\n if (config.tls !== undefined) {\n this.tls = config.tls;\n }\n if (config.connectionTimeout !== undefined) {\n this.connectionTimeout = config.connectionTimeout;\n }\n if (config.adapter !== undefined) {\n this.adapter = config.adapter;\n }\n this.apiUrl = (config.apiUrl ?? MAILPIT_DEFAULT_API_URL).replace(/\\/$/, \"\");\n if (config.apiAuth !== undefined) {\n this.apiAuth = config.apiAuth;\n }\n }\n\n /** Web UI base URL (same origin as the REST API). */\n get webUrl(): string {\n return this.apiUrl;\n }\n\n /** Sends an email to Mailpit over SMTP. */\n async send(options: MailOptions): Promise<SendResult> {\n return (await this.getSmtp()).send(options);\n }\n\n /** Verifies SMTP connectivity to Mailpit. */\n async verify(): Promise<VerifyResult> {\n const result = await (await this.getSmtp()).verify();\n return {\n ...result,\n provider: \"mailpit\",\n };\n }\n\n /** Closes the underlying SMTP adapter if connected. */\n async close(): Promise<void> {\n if (this.smtp) {\n await this.smtp.close();\n }\n }\n\n /**\n * Lists captured messages via `GET /api/v1/messages`.\n * Newest messages appear first.\n */\n async messages(options: MailpitMessagesOptions = {}): Promise<MailpitMessageList> {\n return this.apiJson<MailpitMessageList>(\"GET\", this.listPath(\"/api/v1/messages\", options));\n }\n\n /**\n * Searches captured messages via `GET /api/v1/search`.\n * Query syntax matches the Mailpit UI (`subject:`, `to:`, `tag:`, …).\n */\n async search(query: string, options: MailpitMessagesOptions = {}): Promise<MailpitMessageList> {\n if (!query) {\n throw new MailpitError(\"Search query is required\", 400, { code: \"INVALID_CONFIG\" });\n }\n const params = new URLSearchParams();\n params.set(\"query\", query);\n if (options.limit !== undefined) {\n params.set(\"limit\", String(options.limit));\n }\n if (options.start !== undefined) {\n params.set(\"start\", String(options.start));\n }\n return this.apiJson<MailpitMessageList>(\"GET\", `/api/v1/search?${params.toString()}`);\n }\n\n /**\n * Fetches a full message via `GET /api/v1/message/{id}`.\n * Pass `\"latest\"` for the newest message.\n */\n async getMessage(id: string): Promise<MailpitMessage> {\n return this.apiJson<MailpitMessage>(\"GET\", `/api/v1/message/${this.encodeId(id)}`);\n }\n\n /**\n * Fetches message headers via `GET /api/v1/message/{id}/headers`.\n * Pass `\"latest\"` for the newest message.\n */\n async getHeaders(id: string): Promise<MailpitHeaders> {\n return this.apiJson<MailpitHeaders>(\"GET\", `/api/v1/message/${this.encodeId(id)}/headers`);\n }\n\n /**\n * Runs Mailpit’s HTML/CSS client-compatibility check via\n * `GET /api/v1/message/{id}/html-check`.\n * Pass `\"latest\"` for the newest message.\n */\n async htmlCheck(id: string): Promise<MailpitHtmlCheck> {\n return this.apiJson<MailpitHtmlCheck>(\"GET\", `/api/v1/message/${this.encodeId(id)}/html-check`);\n }\n\n /**\n * Runs Mailpit’s link checker via `GET /api/v1/message/{id}/link-check`.\n * Pass `\"latest\"` for the newest message.\n */\n async linkCheck(id: string, options: MailpitLinkCheckOptions = {}): Promise<MailpitLinkCheck> {\n const params = new URLSearchParams();\n if (options.follow === true) {\n params.set(\"follow\", \"true\");\n }\n const query = params.toString();\n const path = `/api/v1/message/${this.encodeId(id)}/link-check${query ? `?${query}` : \"\"}`;\n return this.apiJson<MailpitLinkCheck>(\"GET\", path);\n }\n\n /**\n * Sets read status via `PUT /api/v1/messages`.\n * Pass an empty `ids` array to update every message in the mailbox.\n */\n async setRead(ids: string[], read: boolean): Promise<void> {\n await this.apiOk(\"PUT\", \"/api/v1/messages\", { IDs: ids, Read: read });\n }\n\n /**\n * Deletes messages by id via `DELETE /api/v1/messages`.\n * Pass an empty array (or call {@link deleteAll}) to clear the inbox.\n */\n async deleteMessages(ids: string[]): Promise<void> {\n await this.apiOk(\"DELETE\", \"/api/v1/messages\", { IDs: ids });\n }\n\n /** Deletes every captured message. */\n async deleteAll(): Promise<void> {\n await this.deleteMessages([]);\n }\n\n private listPath(base: string, options: MailpitMessagesOptions): string {\n const params = new URLSearchParams();\n if (options.limit !== undefined) {\n params.set(\"limit\", String(options.limit));\n }\n if (options.start !== undefined) {\n params.set(\"start\", String(options.start));\n }\n const query = params.toString();\n return query ? `${base}?${query}` : base;\n }\n\n private encodeId(id: string): string {\n if (!id) {\n throw new MailpitError(\"Message id is required\", 400, { code: \"INVALID_CONFIG\" });\n }\n return encodeURIComponent(id);\n }\n\n private async getSmtp(): Promise<SMTPTransport> {\n if (!this.smtp) {\n const adapter =\n this.adapter ??\n (await createDefaultAdapter({\n secure: this.secure,\n ...(this.connectionTimeout !== undefined\n ? { connectionTimeout: this.connectionTimeout }\n : {}),\n ...(this.tls !== undefined ? { tls: this.tls } : {}),\n }));\n\n this.smtp = new SMTPTransport({\n host: this.host,\n port: this.port,\n secure: this.secure,\n requireTLS: this.requireTLS,\n adapter,\n ...(this.auth !== undefined ? { auth: this.auth } : {}),\n ...(this.tls !== undefined ? { tls: this.tls } : {}),\n ...(this.connectionTimeout !== undefined\n ? { connectionTimeout: this.connectionTimeout }\n : {}),\n });\n }\n return this.smtp;\n }\n\n private apiHeaders(): HeadersInit {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n };\n if (this.apiAuth) {\n const token = encodeBase64(`${this.apiAuth.user}:${this.apiAuth.pass}`).replace(/\\r\\n/g, \"\");\n headers.Authorization = `Basic ${token}`;\n }\n return headers;\n }\n\n private async apiFetch(method: string, path: string, body?: unknown): Promise<Response> {\n let response: Response;\n try {\n response = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: this.apiHeaders(),\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n });\n } catch (err) {\n throw new MailpitError(\n err instanceof Error ? err.message : \"Mailpit API request failed\",\n 0,\n err,\n );\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new MailpitError(\n text || `Mailpit API error (${response.status})`,\n response.status,\n text,\n );\n }\n\n return response;\n }\n\n private async apiJson<T>(method: string, path: string, body?: unknown): Promise<T> {\n const response = await this.apiFetch(method, path, body);\n return (await response.json()) as T;\n }\n\n private async apiOk(method: string, path: string, body?: unknown): Promise<void> {\n await this.apiFetch(method, path, body);\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "A41842358B8E380464756E2164756E21",
7
+ "mappings": "yYA0CO,DAAM,HAAuB,OAGvB,HAAuB,AAGvB,HAA0B,mBAwLhC,CAAM,KAAqB,CAAY,CAI1B,WACA,SAHlB,WAAW,CACT,EACgB,EACA,EAChB,CACA,MAAM,EAAS,IAAe,EAAI,oBAAsB,EAAuB,CAAU,EAAG,IACtF,EAAa,EAAI,CAAE,YAAW,EAAI,CAAC,EACvC,SAAU,UACV,MAAO,CACT,CAAC,EAPe,kBACA,gBAOhB,KAAK,KAAO,eAEhB,CAQO,MAAM,CAAsC,CACxC,SAAW,UAEH,KACA,KACA,OACA,WACA,KACA,IACA,kBACA,QACA,OACA,QAET,KAA6B,KAGrC,WAAW,CAAC,EAAwB,CAAC,EAAG,CAKtC,GAJA,KAAK,KAAO,EAAO,MAAQ,EAC3B,KAAK,KAAO,EAAO,MAAQ,EAC3B,KAAK,OAAS,EAAO,QAAU,GAC/B,KAAK,WAAa,EAAO,YAAc,GACnC,EAAO,OAAS,OAClB,KAAK,KAAO,EAAO,KAErB,GAAI,EAAO,MAAQ,OACjB,KAAK,IAAM,EAAO,IAEpB,GAAI,EAAO,oBAAsB,OAC/B,KAAK,kBAAoB,EAAO,kBAElC,GAAI,EAAO,UAAY,OACrB,KAAK,QAAU,EAAO,QAGxB,GADA,KAAK,QAAU,EAAO,QAAU,GAAyB,QAAQ,MAAO,EAAE,EACtE,EAAO,UAAY,OACrB,KAAK,QAAU,EAAO,WAKtB,OAAM,EAAW,CACnB,OAAO,KAAK,YAIR,KAAI,CAAC,EAA2C,CACpD,OAAQ,MAAM,KAAK,QAAQ,GAAG,KAAK,CAAO,OAItC,OAAM,EAA0B,CAEpC,MAAO,IADQ,MAAO,MAAM,KAAK,QAAQ,GAAG,OAAO,EAGjD,SAAU,SACZ,OAII,MAAK,EAAkB,CAC3B,GAAI,KAAK,KACP,MAAM,KAAK,KAAK,MAAM,OAQpB,SAAQ,CAAC,EAAkC,CAAC,EAAgC,CAChF,OAAO,KAAK,QAA4B,MAAO,KAAK,SAAS,mBAAoB,CAAO,CAAC,OAOrF,OAAM,CAAC,EAAe,EAAkC,CAAC,EAAgC,CAC7F,GAAI,CAAC,EACH,MAAM,IAAI,EAAa,2BAA4B,IAAK,CAAE,KAAM,gBAAiB,CAAC,EAEpF,IAAM,EAAS,IAAI,gBAEnB,GADA,EAAO,IAAI,QAAS,CAAK,EACrB,EAAQ,QAAU,OACpB,EAAO,IAAI,QAAS,OAAO,EAAQ,KAAK,CAAC,EAE3C,GAAI,EAAQ,QAAU,OACpB,EAAO,IAAI,QAAS,OAAO,EAAQ,KAAK,CAAC,EAE3C,OAAO,KAAK,QAA4B,MAAO,kBAAkB,EAAO,SAAS,GAAG,OAOhF,WAAU,CAAC,EAAqC,CACpD,OAAO,KAAK,QAAwB,MAAO,mBAAmB,KAAK,SAAS,CAAE,GAAG,OAO7E,WAAU,CAAC,EAAqC,CACpD,OAAO,KAAK,QAAwB,MAAO,mBAAmB,KAAK,SAAS,CAAE,WAAW,OAQrF,UAAS,CAAC,EAAuC,CACrD,OAAO,KAAK,QAA0B,MAAO,mBAAmB,KAAK,SAAS,CAAE,cAAc,OAO1F,UAAS,CAAC,EAAY,EAAmC,CAAC,EAA8B,CAC5F,IAAM,EAAS,IAAI,gBACnB,GAAI,EAAQ,SAAW,GACrB,EAAO,IAAI,SAAU,MAAM,EAE7B,IAAM,EAAQ,EAAO,SAAS,EACxB,EAAO,mBAAmB,KAAK,SAAS,CAAE,eAAe,EAAQ,IAAI,IAAU,KACrF,OAAO,KAAK,QAA0B,MAAO,CAAI,OAO7C,QAAO,CAAC,EAAe,EAA8B,CACzD,MAAM,KAAK,MAAM,MAAO,mBAAoB,CAAE,IAAK,EAAK,KAAM,CAAK,CAAC,OAOhE,eAAc,CAAC,EAA8B,CACjD,MAAM,KAAK,MAAM,SAAU,mBAAoB,CAAE,IAAK,CAAI,CAAC,OAIvD,UAAS,EAAkB,CAC/B,MAAM,KAAK,eAAe,CAAC,CAAC,EAGtB,QAAQ,CAAC,EAAc,EAAyC,CACtE,IAAM,EAAS,IAAI,gBACnB,GAAI,EAAQ,QAAU,OACpB,EAAO,IAAI,QAAS,OAAO,EAAQ,KAAK,CAAC,EAE3C,GAAI,EAAQ,QAAU,OACpB,EAAO,IAAI,QAAS,OAAO,EAAQ,KAAK,CAAC,EAE3C,IAAM,EAAQ,EAAO,SAAS,EAC9B,OAAO,EAAQ,GAAG,KAAQ,IAAU,EAG9B,QAAQ,CAAC,EAAoB,CACnC,GAAI,CAAC,EACH,MAAM,IAAI,EAAa,yBAA0B,IAAK,CAAE,KAAM,gBAAiB,CAAC,EAElF,OAAO,mBAAmB,CAAE,OAGhB,QAAO,EAA2B,CAC9C,GAAI,CAAC,KAAK,KAAM,CACd,IAAM,EACJ,KAAK,SACJ,MAAM,EAAqB,CAC1B,OAAQ,KAAK,UACT,KAAK,oBAAsB,OAC3B,CAAE,kBAAmB,KAAK,iBAAkB,EAC5C,CAAC,KACD,KAAK,MAAQ,OAAY,CAAE,IAAK,KAAK,GAAI,EAAI,CAAC,CACpD,CAAC,EAEH,KAAK,KAAO,IAAI,EAAc,CAC5B,KAAM,KAAK,KACX,KAAM,KAAK,KACX,OAAQ,KAAK,OACb,WAAY,KAAK,WACjB,aACI,KAAK,OAAS,OAAY,CAAE,KAAM,KAAK,IAAK,EAAI,CAAC,KACjD,KAAK,MAAQ,OAAY,CAAE,IAAK,KAAK,GAAI,EAAI,CAAC,KAC9C,KAAK,oBAAsB,OAC3B,CAAE,kBAAmB,KAAK,iBAAkB,EAC5C,CAAC,CACP,CAAC,EAEH,OAAO,KAAK,KAGN,UAAU,EAAgB,CAChC,IAAM,EAAkC,CACtC,OAAQ,mBACR,eAAgB,kBAClB,EACA,GAAI,KAAK,QAAS,CAChB,IAAM,EAAQ,EAAa,GAAG,KAAK,QAAQ,QAAQ,KAAK,QAAQ,MAAM,EAAE,QAAQ,QAAS,EAAE,EAC3F,EAAQ,cAAgB,SAAS,IAEnC,OAAO,OAGK,SAAQ,CAAC,EAAgB,EAAc,EAAmC,CACtF,IAAI,EACJ,GAAI,CACF,EAAW,MAAM,MAAM,GAAG,KAAK,SAAS,IAAQ,CAC9C,SACA,QAAS,KAAK,WAAW,KACrB,IAAS,OAAY,CAAE,KAAM,KAAK,UAAU,CAAI,CAAE,EAAI,CAAC,CAC7D,CAAC,EACD,MAAO,EAAK,CACZ,MAAM,IAAI,EACR,aAAe,MAAQ,EAAI,QAAU,6BACrC,EACA,CACF,EAGF,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAO,MAAM,EAAS,KAAK,EAAE,MAAM,IAAM,EAAE,EACjD,MAAM,IAAI,EACR,GAAQ,sBAAsB,EAAS,UACvC,EAAS,OACT,CACF,EAGF,OAAO,OAGK,QAAU,CAAC,EAAgB,EAAc,EAA4B,CAEjF,OAAQ,MADS,MAAM,KAAK,SAAS,EAAQ,EAAM,CAAI,GAChC,KAAK,OAGhB,MAAK,CAAC,EAAgB,EAAc,EAA+B,CAC/E,MAAM,KAAK,SAAS,EAAQ,EAAM,CAAI,EAE1C",
8
+ "debugId": "051518FA2E8DE88C64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -29,6 +29,13 @@
29
29
  * code: "6240",
30
30
  * lang: "en",
31
31
  * });
32
+ * const balance = await taqnyat.getBalance();
33
+ * await taqnyat.schedule({
34
+ * to: "+9665xxxxxxxx",
35
+ * body: "Later",
36
+ * scheduledDatetime: "2030-01-01T10:00",
37
+ * deleteId: "demo-1",
38
+ * });
32
39
  * ```
33
40
  */
34
41
  import { SentlyError } from "../core/errors.js";
@@ -95,6 +102,60 @@ export interface TaqnyatOtpVerifyResult {
95
102
  /** Provider identifier. */
96
103
  provider: "taqnyat-sms";
97
104
  }
105
+ /** Account balance from {@link TaqnyatSmsTransport.getBalance}. */
106
+ export interface TaqnyatBalance {
107
+ /** Account status string from Taqnyat (e.g. `active`). */
108
+ accountStatus: string;
109
+ /** Balance amount as returned by the API (string). */
110
+ balance: string;
111
+ /** Currency code (usually `SAR`). */
112
+ currency: string;
113
+ /** Account expiry date when present. */
114
+ accountExpiryDate?: string;
115
+ /** Provider identifier. */
116
+ provider: "taqnyat-sms";
117
+ }
118
+ /** One sender row from {@link TaqnyatSmsTransport.listSenders}. */
119
+ export interface TaqnyatSender {
120
+ /** Sender name exactly as registered. */
121
+ senderName: string;
122
+ /** Status string when present (e.g. `active`). */
123
+ status?: string;
124
+ }
125
+ /** Options for {@link TaqnyatSmsTransport.schedule}. */
126
+ export interface TaqnyatScheduleOptions {
127
+ /** Recipient phone number (E.164 or international digits). */
128
+ to: string;
129
+ /** Message body. */
130
+ body: string;
131
+ /**
132
+ * Local schedule time in Taqnyat format, e.g. `2020-09-30T14:26`.
133
+ * Required by `POST /v1/messages` schedule docs.
134
+ */
135
+ scheduledDatetime: string;
136
+ /** Sender ID override; defaults to transport `sender`. */
137
+ from?: string;
138
+ /**
139
+ * Client delete key used later with {@link deleteScheduled}.
140
+ * Optional per Taqnyat schedule docs.
141
+ */
142
+ deleteId?: string | number;
143
+ /** Optional client id mapped to `smsId`. */
144
+ messageId?: string;
145
+ }
146
+ /** Result of a successful {@link TaqnyatSmsTransport.schedule} call. */
147
+ export interface TaqnyatScheduleResult {
148
+ /** Provider message id when present. */
149
+ messageId: string;
150
+ /** Echo of {@link TaqnyatScheduleOptions.deleteId} when set. */
151
+ deleteId?: string;
152
+ /** Recipient as passed in. */
153
+ to: string;
154
+ /** Cost summary when present. */
155
+ response: string;
156
+ /** Provider identifier. */
157
+ provider: "taqnyat-sms";
158
+ }
98
159
  /** Error thrown when the Taqnyat SMS / Verify API returns a non-success response. */
99
160
  export declare class TaqnyatSmsError extends SentlyError {
100
161
  readonly statusCode: number;
@@ -126,6 +187,30 @@ export declare class TaqnyatSmsTransport implements SmsTransport {
126
187
  * (`10` = completed; `13` / `19` = already verified).
127
188
  */
128
189
  verifyOtp(options: TaqnyatVerifyOtpOptions): Promise<TaqnyatOtpVerifyResult>;
190
+ /**
191
+ * Vendor extra: account balance via `GET /account/balance`.
192
+ * Not part of {@link SmsTransport}.
193
+ */
194
+ getBalance(): Promise<TaqnyatBalance>;
195
+ /**
196
+ * Vendor extra: list registered sender names via `GET /v1/messages/senders`.
197
+ * Not part of {@link SmsTransport}.
198
+ */
199
+ listSenders(): Promise<TaqnyatSender[]>;
200
+ /**
201
+ * Vendor extra: schedule an SMS (`POST /v1/messages` + `scheduledDatetime`).
202
+ * Not part of {@link SmsTransport}.
203
+ */
204
+ schedule(options: TaqnyatScheduleOptions): Promise<TaqnyatScheduleResult>;
205
+ /**
206
+ * Vendor extra: delete a scheduled SMS via `DELETE /v1/messages/delete`.
207
+ * Pass the same `deleteId` used in {@link schedule}.
208
+ */
209
+ deleteScheduled(deleteId: string | number): Promise<{
210
+ ok: true;
211
+ message: string;
212
+ provider: "taqnyat-sms";
213
+ }>;
129
214
  /** Lightweight credential check. */
130
215
  verify(): Promise<VerifyResult>;
131
216
  }
@@ -1,3 +1,3 @@
1
- import{j as O}from"../chunk-5es0v1j7.js";import{ja as W,ka as X}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class K extends W{statusCode;apiError;constructor(A,B,G){super(A,X(B>=100&&B<600?B:400),{statusCode:B,provider:"taqnyat-sms",cause:G});this.statusCode=B;this.apiError=G;this.name="TaqnyatSmsError"}}function R(A){let B=A.code??A.statusCode??A.status;if(typeof B==="number")return B;if(typeof B==="string"&&/^-?\d+$/.test(B))return Number(B);return}function Q(A){if(typeof A.message==="string")return A.message;if(typeof A.MessageEn==="string")return A.MessageEn;return}function U(A){if(Array.isArray(A)){let B=A[0];return B&&typeof B==="object"?B:{}}if(A&&typeof A==="object")return A;return{}}class Z{provider="taqnyat-sms";bearerToken;sender;constructor(A){this.bearerToken=A.bearerToken,this.sender=A.sender}async send(A){let G={recipients:[O(A.to)],body:A.body,sender:A.from??this.sender};if(A.messageId!==void 0)G.smsId=A.messageId;let I=await fetch("https://api.taqnyat.sa/v1/messages",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),D=await I.json();if(I.status!==201)throw new K(D.message??"Taqnyat SMS API error",D.statusCode??I.status,D);return{messageId:String(D.messageId??A.messageId??""),to:A.to,status:"accepted",response:`cost: ${D.cost??"?"} ${D.currency??"SAR"}`,provider:"taqnyat-sms"}}async sendOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H);if(!D.ok||F!==5)throw new K(Q(H)??`Taqnyat OTP send failed (code ${F??"unknown"})`,F??D.status,H);return{requestId:A.requestId,to:A.to,code:F,response:J,provider:"taqnyat-sms"}}async verifyOtp(A){let B=A.lang??"ar",G=O(A.to),I=[{apiKey:this.bearerToken,numbers:[G],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,activeKey:A.code,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(I)}),J=await D.text(),N;try{N=JSON.parse(J)}catch{throw new K("Taqnyat Verify returned non-JSON response",D.status,J)}let H=U(N),F=R(H),Y=new Set([10,13,19]);if(!D.ok||F===void 0||!Y.has(F))throw new K(Q(H)??`Taqnyat OTP verify failed (code ${F??"unknown"})`,F??D.status,H);return{ok:!0,code:F,message:Q(H)??"Activation process completed successfully",response:J,provider:"taqnyat-sms"}}async verify(){return{ok:Boolean(this.bearerToken&&this.sender),provider:"taqnyat-sms",message:this.bearerToken&&this.sender?"Credentials present":"Missing credentials"}}}export{Z as TaqnyatSmsTransport,K as TaqnyatSmsError};
1
+ import{j as Q}from"../chunk-5es0v1j7.js";import{ja as X,ka as Y}from"../chunk-ttbwyxmh.js";import"../chunk-th4cwrpb.js";class J extends X{statusCode;apiError;constructor(A,B,F){super(A,Y(B>=100&&B<600?B:400),{statusCode:B,provider:"taqnyat-sms",cause:F});this.statusCode=B;this.apiError=F;this.name="TaqnyatSmsError"}}function U(A){let B=A.code??A.statusCode??A.status;if(typeof B==="number")return B;if(typeof B==="string"&&/^-?\d+$/.test(B))return Number(B);return}function R(A){if(typeof A.message==="string")return A.message;if(typeof A.MessageEn==="string")return A.MessageEn;return}function W(A){if(Array.isArray(A)){let B=A[0];return B&&typeof B==="object"?B:{}}if(A&&typeof A==="object")return A;return{}}class _{provider="taqnyat-sms";bearerToken;sender;constructor(A){this.bearerToken=A.bearerToken,this.sender=A.sender}async send(A){let F={recipients:[Q(A.to)],body:A.body,sender:A.from??this.sender};if(A.messageId!==void 0)F.smsId=A.messageId;let G=await fetch("https://api.taqnyat.sa/v1/messages",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(F)}),D=await G.json();if(G.status!==201)throw new J(D.message??"Taqnyat SMS API error",D.statusCode??G.status,D);return{messageId:String(D.messageId??A.messageId??""),to:A.to,status:"accepted",response:`cost: ${D.cost??"?"} ${D.currency??"SAR"}`,provider:"taqnyat-sms"}}async sendOtp(A){let B=A.lang??"ar",F=Q(A.to),G=[{apiKey:this.bearerToken,numbers:[F],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),N=await D.text(),O;try{O=JSON.parse(N)}catch{throw new J("Taqnyat Verify returned non-JSON response",D.status,N)}let K=W(O),H=U(K);if(!D.ok||H!==5)throw new J(R(K)??`Taqnyat OTP send failed (code ${H??"unknown"})`,H??D.status,K);return{requestId:A.requestId,to:A.to,code:H,response:N,provider:"taqnyat-sms"}}async verifyOtp(A){let B=A.lang??"ar",F=Q(A.to),G=[{apiKey:this.bearerToken,numbers:[F],method:"sms",sender:A.from??this.sender,lang:B,requestId:A.requestId,activeKey:A.code,returnJson:1,...A.note!==void 0?{note:A.note}:{}}],D=await fetch("https://api.taqnyat.sa/verify.php/",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(G)}),N=await D.text(),O;try{O=JSON.parse(N)}catch{throw new J("Taqnyat Verify returned non-JSON response",D.status,N)}let K=W(O),H=U(K),Z=new Set([10,13,19]);if(!D.ok||H===void 0||!Z.has(H))throw new J(R(K)??`Taqnyat OTP verify failed (code ${H??"unknown"})`,H??D.status,K);return{ok:!0,code:H,message:R(K)??"Activation process completed successfully",response:N,provider:"taqnyat-sms"}}async getBalance(){let A=await fetch(`https://api.taqnyat.sa/account/balance?bearerTokens=${encodeURIComponent(this.bearerToken)}`,{headers:{Authorization:`Bearer ${this.bearerToken}`}}),B=await A.json();if(!A.ok||B.statusCode!==200)throw new J(B.message??"Taqnyat balance API error",B.statusCode??A.status,B);return{accountStatus:B.accountStatus??"",balance:B.balance??"0",currency:B.currency??"SAR",...B.accountExpiryDate!==void 0?{accountExpiryDate:B.accountExpiryDate}:{},provider:"taqnyat-sms"}}async listSenders(){let A=await fetch(`https://api.taqnyat.sa/v1/messages/senders?bearerTokens=${encodeURIComponent(this.bearerToken)}`,{headers:{Authorization:`Bearer ${this.bearerToken}`}}),B=await A.json();if(!A.ok||B.statusCode!==200)throw new J(B.message??"Taqnyat senders API error",B.statusCode??A.status,B);return(B.senders??[]).filter((F)=>typeof F.senderName==="string").map((F)=>({senderName:F.senderName,...F.status!==void 0?{status:F.status}:{}}))}async schedule(A){let F={recipients:[Q(A.to)],body:A.body,sender:A.from??this.sender,scheduledDatetime:A.scheduledDatetime};if(A.deleteId!==void 0)F.deleteId=A.deleteId;if(A.messageId!==void 0)F.smsId=A.messageId;let G=await fetch("https://api.taqnyat.sa/v1/messages",{method:"POST",headers:{Authorization:`Bearer ${this.bearerToken}`,"Content-Type":"application/json"},body:JSON.stringify(F)}),D=await G.json();if(G.status!==201)throw new J(D.message??"Taqnyat schedule SMS API error",D.statusCode??G.status,D);return{messageId:String(D.messageId??A.messageId??""),...A.deleteId!==void 0?{deleteId:String(A.deleteId)}:{},to:A.to,response:`cost: ${D.cost??"?"} ${D.currency??"SAR"}`,provider:"taqnyat-sms"}}async deleteScheduled(A){let B=new URL("https://api.taqnyat.sa/v1/messages/delete");B.searchParams.set("bearerTokens",this.bearerToken),B.searchParams.set("deleteId",String(A));let F=await fetch(B,{method:"DELETE",headers:{Authorization:`Bearer ${this.bearerToken}`}}),G=await F.json();if(F.status!==201&&G.statusCode!==201)throw new J(G.message??"Taqnyat delete scheduled SMS API error",G.statusCode??F.status,G);return{ok:!0,message:G.message??"Deleted successfully",provider:"taqnyat-sms"}}async verify(){return{ok:Boolean(this.bearerToken&&this.sender),provider:"taqnyat-sms",message:this.bearerToken&&this.sender?"Credentials present":"Missing credentials"}}}export{_ as TaqnyatSmsTransport,J as TaqnyatSmsError};
2
2
 
3
- //# debugId=8ED03B5D386F317B64756E2164756E21
3
+ //# debugId=5FB80A8A78A3002064756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/taqnyat-sms.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Taqnyat SMS API transport (Saudi Arabia).\n *\n * Request/response shapes follow\n * [Taqnyat SMS](https://dev.taqnyat.sa/en/doc/sms/) and\n * [Verify](https://dev.taqnyat.sa/en/doc/verify/):\n * - SMS: `POST /v1/messages` with Bearer + JSON (`recipients`, `body`, `sender`)\n * - OTP: `POST /verify.php/` with JSON array (`apiKey`, `numbers`, `method`, …)\n *\n * Sently-first: wire SMS into {@link createSmsSender}; OTP helpers stay on this class.\n *\n * @example\n * ```ts\n * import { createSmsSender } from \"sently/sms\";\n * import { TaqnyatSmsTransport } from \"sently/transports/taqnyat-sms\";\n *\n * const taqnyat = new TaqnyatSmsTransport({\n * bearerToken: process.env.TAQNYAT_TOKEN!,\n * sender: \"MyBrand\",\n * });\n * const sms = createSmsSender({ transport: taqnyat });\n *\n * await sms.send({ to: \"+9665xxxxxxxx\", body: \"Hello\" });\n * await taqnyat.sendOtp({ to: \"+9665xxxxxxxx\", requestId: \"login-1\", lang: \"en\" });\n * await taqnyat.verifyOtp({\n * to: \"+9665xxxxxxxx\",\n * requestId: \"login-1\",\n * code: \"6240\",\n * lang: \"en\",\n * });\n * ```\n */\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { SmsOptions, SmsSendResult, SmsTransport } from \"../core/sms-types.js\";\nimport type { VerifyResult } from \"../core/types.js\";\nimport { normalizeTaqnyatPhone } from \"./taqnyat-phone.js\";\n\n/** Taqnyat SMS API configuration. */\nexport interface TaqnyatSmsConfig {\n /** Bearer token from the Taqnyat dashboard. */\n bearerToken: string;\n /** Pre-approved sender name. */\n sender: string;\n}\n\n/** Options for {@link TaqnyatSmsTransport.sendOtp}. */\nexport interface TaqnyatSendOtpOptions {\n /** Recipient phone number (E.164 or international digits). */\n to: string;\n /** Unique id for this verification flow (required again on {@link verifyOtp}). */\n requestId: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"` per Taqnyat docs. */\n lang?: \"en\" | \"ar\";\n /** Optional note appended to the OTP SMS. */\n note?: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.sendOtp} call. */\nexport interface TaqnyatOtpSendResult {\n /** Echo of {@link TaqnyatSendOtpOptions.requestId}. */\n requestId: string;\n /** Recipient as passed in. */\n to: string;\n /** Provider status code (`5` = code sent). */\n code: number;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Options for {@link TaqnyatSmsTransport.verifyOtp}. */\nexport interface TaqnyatVerifyOtpOptions {\n /** Recipient phone number (same as send). */\n to: string;\n /** Same {@link TaqnyatSendOtpOptions.requestId} used when sending. */\n requestId: string;\n /** OTP code the user entered (`activeKey` in Taqnyat docs). */\n code: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"`. */\n lang?: \"en\" | \"ar\";\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /** Optional note (documented on check OTP). */\n note?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.verifyOtp} call. */\nexport interface TaqnyatOtpVerifyResult {\n /** Always `true` when the call resolves (failures throw). */\n ok: true;\n /** Provider status code (`10` = completed, `13`/`19` = already verified). */\n code: number;\n /** Provider message when present. */\n message: string;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Error thrown when the Taqnyat SMS / Verify API returns a non-success response. */\nexport class TaqnyatSmsError extends SentlyError {\n /** Creates a Taqnyat SMS API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(\n message,\n httpStatusToSentlyCode(statusCode >= 100 && statusCode < 600 ? statusCode : 400),\n {\n statusCode,\n provider: \"taqnyat-sms\",\n cause: apiError,\n },\n );\n this.name = \"TaqnyatSmsError\";\n }\n}\n\nfunction verifyPayloadCode(payload: Record<string, unknown>): number | undefined {\n const raw = payload.code ?? payload.statusCode ?? payload.status;\n if (typeof raw === \"number\") return raw;\n if (typeof raw === \"string\" && /^-?\\d+$/.test(raw)) return Number(raw);\n return undefined;\n}\n\nfunction verifyPayloadMessage(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.message === \"string\") return payload.message;\n if (typeof payload.MessageEn === \"string\") return payload.MessageEn;\n return undefined;\n}\n\nfunction unwrapVerifyPayload(payload: unknown): Record<string, unknown> {\n if (Array.isArray(payload)) {\n const first = payload[0];\n return first && typeof first === \"object\" ? (first as Record<string, unknown>) : {};\n }\n if (payload && typeof payload === \"object\") {\n return payload as Record<string, unknown>;\n }\n return {};\n}\n\n/**\n * Taqnyat SMS transport via JSON Bearer API, plus Verify OTP helpers.\n */\nexport class TaqnyatSmsTransport implements SmsTransport {\n readonly provider = \"taqnyat-sms\";\n\n private readonly bearerToken: string;\n private readonly sender: string;\n\n /** Creates a Taqnyat SMS transport. */\n constructor(config: TaqnyatSmsConfig) {\n this.bearerToken = config.bearerToken;\n this.sender = config.sender;\n }\n\n /**\n * Sends an SMS via `POST https://api.taqnyat.sa/v1/messages`\n * (Bearer + JSON: `recipients`, `body`, `sender`; optional `smsId`).\n */\n async send(options: SmsOptions): Promise<SmsSendResult> {\n const recipients = [normalizeTaqnyatPhone(options.to)];\n const body: Record<string, unknown> = {\n recipients,\n body: options.body,\n sender: options.from ?? this.sender,\n };\n // Official PHP SDK maps a client id to `smsId`.\n if (options.messageId !== undefined) {\n body.smsId = options.messageId;\n }\n\n const response = await fetch(\"https://api.taqnyat.sa/v1/messages\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n statusCode?: number;\n messageId?: number;\n cost?: number;\n currency?: string;\n message?: string;\n accepted?: string;\n rejected?: string;\n };\n\n // Docs: successful send returns HTTP 201 with statusCode 201.\n if (response.status !== 201) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat SMS API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n messageId: String(payload.messageId ?? options.messageId ?? \"\"),\n to: options.to,\n status: \"accepted\",\n response: `cost: ${payload.cost ?? \"?\"} ${payload.currency ?? \"SAR\"}`,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: generate/send OTP via `POST /verify.php/` (success code `5`).\n * Not part of {@link SmsTransport} — call on {@link TaqnyatSmsTransport} directly.\n */\n async sendOtp(options: TaqnyatSendOtpOptions): Promise<TaqnyatOtpSendResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n\n // Docs: 5 = Activation code sent successfully.\n if (!response.ok || code !== 5) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP send failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n requestId: options.requestId,\n to: options.to,\n code,\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: check OTP via `POST /verify.php/` with `activeKey`\n * (`10` = completed; `13` / `19` = already verified).\n */\n async verifyOtp(options: TaqnyatVerifyOtpOptions): Promise<TaqnyatOtpVerifyResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n activeKey: options.code,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n const okCodes = new Set([10, 13, 19]);\n\n if (!response.ok || code === undefined || !okCodes.has(code)) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP verify failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n ok: true,\n code,\n message: verifyPayloadMessage(result) ?? \"Activation process completed successfully\",\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /** Lightweight credential check. */\n async verify(): Promise<VerifyResult> {\n return {\n ok: Boolean(this.bearerToken && this.sender),\n provider: \"taqnyat-sms\",\n message: this.bearerToken && this.sender ? \"Credentials present\" : \"Missing credentials\",\n };\n }\n}\n"
5
+ "/**\n * @module\n * Taqnyat SMS API transport (Saudi Arabia).\n *\n * Request/response shapes follow\n * [Taqnyat SMS](https://dev.taqnyat.sa/en/doc/sms/) and\n * [Verify](https://dev.taqnyat.sa/en/doc/verify/):\n * - SMS: `POST /v1/messages` with Bearer + JSON (`recipients`, `body`, `sender`)\n * - OTP: `POST /verify.php/` with JSON array (`apiKey`, `numbers`, `method`, …)\n *\n * Sently-first: wire SMS into {@link createSmsSender}; OTP helpers stay on this class.\n *\n * @example\n * ```ts\n * import { createSmsSender } from \"sently/sms\";\n * import { TaqnyatSmsTransport } from \"sently/transports/taqnyat-sms\";\n *\n * const taqnyat = new TaqnyatSmsTransport({\n * bearerToken: process.env.TAQNYAT_TOKEN!,\n * sender: \"MyBrand\",\n * });\n * const sms = createSmsSender({ transport: taqnyat });\n *\n * await sms.send({ to: \"+9665xxxxxxxx\", body: \"Hello\" });\n * await taqnyat.sendOtp({ to: \"+9665xxxxxxxx\", requestId: \"login-1\", lang: \"en\" });\n * await taqnyat.verifyOtp({\n * to: \"+9665xxxxxxxx\",\n * requestId: \"login-1\",\n * code: \"6240\",\n * lang: \"en\",\n * });\n * const balance = await taqnyat.getBalance();\n * await taqnyat.schedule({\n * to: \"+9665xxxxxxxx\",\n * body: \"Later\",\n * scheduledDatetime: \"2030-01-01T10:00\",\n * deleteId: \"demo-1\",\n * });\n * ```\n */\nimport { httpStatusToSentlyCode, SentlyError } from \"../core/errors.js\";\nimport type { SmsOptions, SmsSendResult, SmsTransport } from \"../core/sms-types.js\";\nimport type { VerifyResult } from \"../core/types.js\";\nimport { normalizeTaqnyatPhone } from \"./taqnyat-phone.js\";\n\n/** Taqnyat SMS API configuration. */\nexport interface TaqnyatSmsConfig {\n /** Bearer token from the Taqnyat dashboard. */\n bearerToken: string;\n /** Pre-approved sender name. */\n sender: string;\n}\n\n/** Options for {@link TaqnyatSmsTransport.sendOtp}. */\nexport interface TaqnyatSendOtpOptions {\n /** Recipient phone number (E.164 or international digits). */\n to: string;\n /** Unique id for this verification flow (required again on {@link verifyOtp}). */\n requestId: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"` per Taqnyat docs. */\n lang?: \"en\" | \"ar\";\n /** Optional note appended to the OTP SMS. */\n note?: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.sendOtp} call. */\nexport interface TaqnyatOtpSendResult {\n /** Echo of {@link TaqnyatSendOtpOptions.requestId}. */\n requestId: string;\n /** Recipient as passed in. */\n to: string;\n /** Provider status code (`5` = code sent). */\n code: number;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Options for {@link TaqnyatSmsTransport.verifyOtp}. */\nexport interface TaqnyatVerifyOtpOptions {\n /** Recipient phone number (same as send). */\n to: string;\n /** Same {@link TaqnyatSendOtpOptions.requestId} used when sending. */\n requestId: string;\n /** OTP code the user entered (`activeKey` in Taqnyat docs). */\n code: string;\n /** Message language (`en` or `ar`). Defaults to `\"ar\"`. */\n lang?: \"en\" | \"ar\";\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /** Optional note (documented on check OTP). */\n note?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.verifyOtp} call. */\nexport interface TaqnyatOtpVerifyResult {\n /** Always `true` when the call resolves (failures throw). */\n ok: true;\n /** Provider status code (`10` = completed, `13`/`19` = already verified). */\n code: number;\n /** Provider message when present. */\n message: string;\n /** Raw response body text. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Account balance from {@link TaqnyatSmsTransport.getBalance}. */\nexport interface TaqnyatBalance {\n /** Account status string from Taqnyat (e.g. `active`). */\n accountStatus: string;\n /** Balance amount as returned by the API (string). */\n balance: string;\n /** Currency code (usually `SAR`). */\n currency: string;\n /** Account expiry date when present. */\n accountExpiryDate?: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** One sender row from {@link TaqnyatSmsTransport.listSenders}. */\nexport interface TaqnyatSender {\n /** Sender name exactly as registered. */\n senderName: string;\n /** Status string when present (e.g. `active`). */\n status?: string;\n}\n\n/** Options for {@link TaqnyatSmsTransport.schedule}. */\nexport interface TaqnyatScheduleOptions {\n /** Recipient phone number (E.164 or international digits). */\n to: string;\n /** Message body. */\n body: string;\n /**\n * Local schedule time in Taqnyat format, e.g. `2020-09-30T14:26`.\n * Required by `POST /v1/messages` schedule docs.\n */\n scheduledDatetime: string;\n /** Sender ID override; defaults to transport `sender`. */\n from?: string;\n /**\n * Client delete key used later with {@link deleteScheduled}.\n * Optional per Taqnyat schedule docs.\n */\n deleteId?: string | number;\n /** Optional client id mapped to `smsId`. */\n messageId?: string;\n}\n\n/** Result of a successful {@link TaqnyatSmsTransport.schedule} call. */\nexport interface TaqnyatScheduleResult {\n /** Provider message id when present. */\n messageId: string;\n /** Echo of {@link TaqnyatScheduleOptions.deleteId} when set. */\n deleteId?: string;\n /** Recipient as passed in. */\n to: string;\n /** Cost summary when present. */\n response: string;\n /** Provider identifier. */\n provider: \"taqnyat-sms\";\n}\n\n/** Error thrown when the Taqnyat SMS / Verify API returns a non-success response. */\nexport class TaqnyatSmsError extends SentlyError {\n /** Creates a Taqnyat SMS API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(\n message,\n httpStatusToSentlyCode(statusCode >= 100 && statusCode < 600 ? statusCode : 400),\n {\n statusCode,\n provider: \"taqnyat-sms\",\n cause: apiError,\n },\n );\n this.name = \"TaqnyatSmsError\";\n }\n}\n\nfunction verifyPayloadCode(payload: Record<string, unknown>): number | undefined {\n const raw = payload.code ?? payload.statusCode ?? payload.status;\n if (typeof raw === \"number\") return raw;\n if (typeof raw === \"string\" && /^-?\\d+$/.test(raw)) return Number(raw);\n return undefined;\n}\n\nfunction verifyPayloadMessage(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.message === \"string\") return payload.message;\n if (typeof payload.MessageEn === \"string\") return payload.MessageEn;\n return undefined;\n}\n\nfunction unwrapVerifyPayload(payload: unknown): Record<string, unknown> {\n if (Array.isArray(payload)) {\n const first = payload[0];\n return first && typeof first === \"object\" ? (first as Record<string, unknown>) : {};\n }\n if (payload && typeof payload === \"object\") {\n return payload as Record<string, unknown>;\n }\n return {};\n}\n\n/**\n * Taqnyat SMS transport via JSON Bearer API, plus Verify OTP helpers.\n */\nexport class TaqnyatSmsTransport implements SmsTransport {\n readonly provider = \"taqnyat-sms\";\n\n private readonly bearerToken: string;\n private readonly sender: string;\n\n /** Creates a Taqnyat SMS transport. */\n constructor(config: TaqnyatSmsConfig) {\n this.bearerToken = config.bearerToken;\n this.sender = config.sender;\n }\n\n /**\n * Sends an SMS via `POST https://api.taqnyat.sa/v1/messages`\n * (Bearer + JSON: `recipients`, `body`, `sender`; optional `smsId`).\n */\n async send(options: SmsOptions): Promise<SmsSendResult> {\n const recipients = [normalizeTaqnyatPhone(options.to)];\n const body: Record<string, unknown> = {\n recipients,\n body: options.body,\n sender: options.from ?? this.sender,\n };\n // Official PHP SDK maps a client id to `smsId`.\n if (options.messageId !== undefined) {\n body.smsId = options.messageId;\n }\n\n const response = await fetch(\"https://api.taqnyat.sa/v1/messages\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n statusCode?: number;\n messageId?: number;\n cost?: number;\n currency?: string;\n message?: string;\n accepted?: string;\n rejected?: string;\n };\n\n // Docs: successful send returns HTTP 201 with statusCode 201.\n if (response.status !== 201) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat SMS API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n messageId: String(payload.messageId ?? options.messageId ?? \"\"),\n to: options.to,\n status: \"accepted\",\n response: `cost: ${payload.cost ?? \"?\"} ${payload.currency ?? \"SAR\"}`,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: generate/send OTP via `POST /verify.php/` (success code `5`).\n * Not part of {@link SmsTransport} — call on {@link TaqnyatSmsTransport} directly.\n */\n async sendOtp(options: TaqnyatSendOtpOptions): Promise<TaqnyatOtpSendResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n\n // Docs: 5 = Activation code sent successfully.\n if (!response.ok || code !== 5) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP send failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n requestId: options.requestId,\n to: options.to,\n code,\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: check OTP via `POST /verify.php/` with `activeKey`\n * (`10` = completed; `13` / `19` = already verified).\n */\n async verifyOtp(options: TaqnyatVerifyOtpOptions): Promise<TaqnyatOtpVerifyResult> {\n const lang = options.lang ?? \"ar\";\n const number = normalizeTaqnyatPhone(options.to);\n const body = [\n {\n apiKey: this.bearerToken,\n numbers: [number],\n method: \"sms\",\n sender: options.from ?? this.sender,\n lang,\n requestId: options.requestId,\n activeKey: options.code,\n returnJson: 1,\n ...(options.note !== undefined ? { note: options.note } : {}),\n },\n ];\n\n const response = await fetch(\"https://api.taqnyat.sa/verify.php/\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new TaqnyatSmsError(\"Taqnyat Verify returned non-JSON response\", response.status, text);\n }\n\n const result = unwrapVerifyPayload(parsed);\n const code = verifyPayloadCode(result);\n const okCodes = new Set([10, 13, 19]);\n\n if (!response.ok || code === undefined || !okCodes.has(code)) {\n throw new TaqnyatSmsError(\n verifyPayloadMessage(result) ?? `Taqnyat OTP verify failed (code ${code ?? \"unknown\"})`,\n code ?? response.status,\n result,\n );\n }\n\n return {\n ok: true,\n code,\n message: verifyPayloadMessage(result) ?? \"Activation process completed successfully\",\n response: text,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: account balance via `GET /account/balance`.\n * Not part of {@link SmsTransport}.\n */\n async getBalance(): Promise<TaqnyatBalance> {\n const response = await fetch(\n `https://api.taqnyat.sa/account/balance?bearerTokens=${encodeURIComponent(this.bearerToken)}`,\n {\n headers: { Authorization: `Bearer ${this.bearerToken}` },\n },\n );\n const payload = (await response.json()) as {\n statusCode?: number;\n accountStatus?: string;\n balance?: string;\n currency?: string;\n accountExpiryDate?: string;\n message?: string;\n };\n\n if (!response.ok || payload.statusCode !== 200) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat balance API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n accountStatus: payload.accountStatus ?? \"\",\n balance: payload.balance ?? \"0\",\n currency: payload.currency ?? \"SAR\",\n ...(payload.accountExpiryDate !== undefined\n ? { accountExpiryDate: payload.accountExpiryDate }\n : {}),\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: list registered sender names via `GET /v1/messages/senders`.\n * Not part of {@link SmsTransport}.\n */\n async listSenders(): Promise<TaqnyatSender[]> {\n const response = await fetch(\n `https://api.taqnyat.sa/v1/messages/senders?bearerTokens=${encodeURIComponent(this.bearerToken)}`,\n {\n headers: { Authorization: `Bearer ${this.bearerToken}` },\n },\n );\n const payload = (await response.json()) as {\n statusCode?: number;\n senders?: Array<{ senderName?: string; status?: string }>;\n message?: string;\n };\n\n if (!response.ok || payload.statusCode !== 200) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat senders API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return (payload.senders ?? [])\n .filter(\n (row): row is { senderName: string; status?: string } => typeof row.senderName === \"string\",\n )\n .map((row) => ({\n senderName: row.senderName,\n ...(row.status !== undefined ? { status: row.status } : {}),\n }));\n }\n\n /**\n * Vendor extra: schedule an SMS (`POST /v1/messages` + `scheduledDatetime`).\n * Not part of {@link SmsTransport}.\n */\n async schedule(options: TaqnyatScheduleOptions): Promise<TaqnyatScheduleResult> {\n const recipients = [normalizeTaqnyatPhone(options.to)];\n const body: Record<string, unknown> = {\n recipients,\n body: options.body,\n sender: options.from ?? this.sender,\n scheduledDatetime: options.scheduledDatetime,\n };\n if (options.deleteId !== undefined) {\n body.deleteId = options.deleteId;\n }\n if (options.messageId !== undefined) {\n body.smsId = options.messageId;\n }\n\n const response = await fetch(\"https://api.taqnyat.sa/v1/messages\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.bearerToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as {\n statusCode?: number;\n messageId?: number;\n cost?: number;\n currency?: string;\n message?: string;\n };\n\n if (response.status !== 201) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat schedule SMS API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n messageId: String(payload.messageId ?? options.messageId ?? \"\"),\n ...(options.deleteId !== undefined ? { deleteId: String(options.deleteId) } : {}),\n to: options.to,\n response: `cost: ${payload.cost ?? \"?\"} ${payload.currency ?? \"SAR\"}`,\n provider: \"taqnyat-sms\",\n };\n }\n\n /**\n * Vendor extra: delete a scheduled SMS via `DELETE /v1/messages/delete`.\n * Pass the same `deleteId` used in {@link schedule}.\n */\n async deleteScheduled(\n deleteId: string | number,\n ): Promise<{ ok: true; message: string; provider: \"taqnyat-sms\" }> {\n const url = new URL(\"https://api.taqnyat.sa/v1/messages/delete\");\n url.searchParams.set(\"bearerTokens\", this.bearerToken);\n url.searchParams.set(\"deleteId\", String(deleteId));\n\n const response = await fetch(url, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${this.bearerToken}` },\n });\n\n const payload = (await response.json()) as {\n statusCode?: number;\n message?: string;\n };\n\n if (response.status !== 201 && payload.statusCode !== 201) {\n throw new TaqnyatSmsError(\n payload.message ?? \"Taqnyat delete scheduled SMS API error\",\n payload.statusCode ?? response.status,\n payload,\n );\n }\n\n return {\n ok: true,\n message: payload.message ?? \"Deleted successfully\",\n provider: \"taqnyat-sms\",\n };\n }\n\n /** Lightweight credential check. */\n async verify(): Promise<VerifyResult> {\n return {\n ok: Boolean(this.bearerToken && this.sender),\n provider: \"taqnyat-sms\",\n message: this.bearerToken && this.sender ? \"Credentials present\" : \"Missing credentials\",\n };\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "8ED03B5D386F317B64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "5FB80A8A78A3002064756E2164756E21",
9
9
  "names": []
10
10
  }