@postbrix/sdk 1.0.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/LICENSE ADDED
@@ -0,0 +1,279 @@
1
+ # PostBrix SDK Commercial License Agreement
2
+
3
+ **Version:** 1.0
4
+ **Effective Date:** January 1, 2026
5
+ **Last Updated:** 2026
6
+
7
+ **Copyright © 2026 PostBrix. All rights reserved.**
8
+
9
+ ## 1. Definitions
10
+
11
+ For purposes of this Agreement:
12
+
13
+ - **“Licensor,” “PostBrix,” “we,” “us,” or “our”** means **PostBrix**.
14
+ - **“Licensee,” “you,” or “your”** means the individual or legal entity that lawfully obtains or uses the SDK.
15
+ - **“SDK”** means the proprietary PostBrix software development kit, including the software package distributed by PostBrix (including the applicable NPM package), its source or compiled code where provided, APIs, SDK libraries, examples, configuration files, and related technical materials supplied by PostBrix.
16
+ - **“Documentation”** means official documentation, reference materials, examples, and technical instructions supplied by PostBrix for use of the SDK.
17
+ - **“PostBrix Services”** means the PostBrix hosted services, APIs, platforms, websites, and related services made available by PostBrix and intended to be used with the SDK.
18
+ - **“Application”** means software, websites, products, or services developed or operated by Licensee that use or depend on the SDK.
19
+ - **“Authorized Use”** means the use of the SDK expressly permitted under Section 2.
20
+
21
+ ## 2. License Grant
22
+
23
+ Subject to Licensee's compliance with this Agreement and any applicable PostBrix Services terms, PostBrix grants Licensee a limited, non-exclusive, non-transferable, non-sublicensable license during the applicable license term to:
24
+
25
+ 1. download, install, and execute the SDK;
26
+ 2. use the SDK to integrate Licensee's Applications with the PostBrix Services;
27
+ 3. copy the SDK as reasonably necessary for development, testing, deployment, backup, and operation of Licensee's Applications; and
28
+ 4. include the SDK in, or distribute the SDK with, Licensee's Applications solely as part of those Applications or services.
29
+
30
+ Licensee does **not** receive ownership of the SDK. Except for the rights expressly granted in this Agreement, all rights are reserved by PostBrix.
31
+
32
+ Nothing in this Agreement grants Licensee a right to use the SDK as a standalone product or to offer the SDK itself as a separately licensed or commercialized service.
33
+
34
+ ## 3. Permitted Distribution
35
+
36
+ Licensee may distribute its own Applications that contain or depend upon the SDK, including Applications distributed to Licensee's customers or end users, provided that:
37
+
38
+ - the SDK is used only as part of the Application;
39
+ - the SDK is not separately sold, licensed, rented, leased, or distributed as a standalone product;
40
+ - Licensee does not grant recipients rights to extract, reuse, or commercialize the SDK independently of the Application; and
41
+ - Licensee remains responsible for complying with this Agreement.
42
+
43
+ For clarity, ordinary application packaging, bundling, minification, transpilation, containerization, deployment, and similar technical processes required to build or operate an Application do not by themselves constitute prohibited modification or redistribution of the SDK.
44
+
45
+ ## 4. Restrictions
46
+
47
+ Except where expressly permitted by this Agreement or applicable law, Licensee shall not:
48
+
49
+ 1. sell, resell, sublicense, rent, lease, lend, distribute, or otherwise make the SDK available as a standalone product or service;
50
+ 2. use the SDK to provide a standalone SDK, template-rendering engine, API client, or substantially similar competing developer product;
51
+ 3. modify the SDK itself or create derivative works of the SDK, except for changes expressly permitted by applicable law or by PostBrix in writing;
52
+ 4. reverse engineer, decompile, disassemble, or attempt to derive the source code, algorithms, or underlying structure of the SDK, except to the extent such restriction is prohibited or limited by applicable law;
53
+ 5. remove, obscure, or alter copyright, trademark, proprietary, or other legal notices contained in the SDK;
54
+ 6. use the SDK to circumvent PostBrix authentication, authorization, subscription, usage limits, rate limits, billing controls, security controls, or other technical restrictions;
55
+ 7. use the SDK to interfere with, disrupt, damage, overload, or gain unauthorized access to the PostBrix Services or related systems;
56
+ 8. use the SDK in violation of applicable law or applicable PostBrix acceptable-use or security requirements; or
57
+ 9. use the SDK for the purpose of creating a product that directly competes with the PostBrix SDK or its underlying developer platform, except that Licensee may develop ordinary software Applications that use the SDK.
58
+
59
+ Nothing in this Section prohibits any use that cannot lawfully be prohibited under applicable law.
60
+
61
+ ## 5. Intellectual Property
62
+
63
+ The SDK and Documentation, including all copyrights, trademarks, trade secrets, patents, know-how, designs, interfaces, and other intellectual property rights in them, are and remain the property of PostBrix or its licensors.
64
+
65
+ This Agreement is a license and not a sale or transfer of ownership.
66
+
67
+ Licensee retains all rights in its own Applications, code, content, data, and materials, subject to any third-party rights and the rights granted to PostBrix under applicable PostBrix Services terms.
68
+
69
+ PostBrix trademarks and branding may be used only as expressly permitted by PostBrix's applicable trademark guidelines or written authorization.
70
+
71
+ ## 6. Third-Party and Open-Source Software
72
+
73
+ The SDK may include or interact with third-party software or open-source components.
74
+
75
+ Such components may be governed by separate license terms. Where applicable, those terms will control the use of the relevant component to the extent required by its license.
76
+
77
+ Nothing in this Agreement is intended to restrict rights granted to Licensee under an applicable open-source license.
78
+
79
+ Applicable third-party and open-source notices should be provided with the SDK distribution or Documentation where required.
80
+
81
+ ## 7. PostBrix Services and Account Requirements
82
+
83
+ The SDK may require an active PostBrix account, API credentials, subscription, or other authorization to access the PostBrix Services.
84
+
85
+ Use of the PostBrix Services is separately governed by the applicable PostBrix Terms of Service, subscription terms, acceptable-use policies, privacy policies, and other applicable agreements.
86
+
87
+ If the SDK is available to users on a particular PostBrix plan, the applicable plan terms and usage limits will control eligibility for the PostBrix Services.
88
+
89
+ The SDK license does not itself grant unlimited access to the PostBrix Services.
90
+
91
+ ## 8. Credentials and Security
92
+
93
+ Licensee is responsible for safeguarding API keys, access tokens, credentials, and other authentication information used with the SDK.
94
+
95
+ Licensee shall not:
96
+
97
+ - publish secret PostBrix API credentials in public repositories;
98
+ - intentionally expose server-side secret credentials in client-side applications;
99
+ - share credentials with unauthorized persons; or
100
+ - use credentials belonging to another person or organization without authorization.
101
+
102
+ Licensee should promptly revoke or rotate credentials that are suspected to have been compromised.
103
+
104
+ ## 9. Privacy and Data Protection
105
+
106
+ Licensee's use of the PostBrix Services may involve the processing of data, including data supplied through SDK requests.
107
+
108
+ The parties' privacy and data-protection obligations are governed by the applicable PostBrix Privacy Policy, Data Processing Agreement (“DPA”), and other applicable data-protection terms.
109
+
110
+ Licensee is responsible for ensuring that it has the necessary rights, notices, consents, and lawful bases required for data it submits to the PostBrix Services.
111
+
112
+ Where a DPA applies, the DPA will govern the parties' applicable data-processing relationship to the extent of any conflict with this Agreement.
113
+
114
+ ## 10. Confidentiality
115
+
116
+ If PostBrix provides non-public information identified as confidential or that reasonably should be understood to be confidential, Licensee shall use reasonable care to protect that information and shall not disclose it except to personnel or contractors who need to know it and are bound by confidentiality obligations.
117
+
118
+ Confidential information does not include information that:
119
+
120
+ - is or becomes publicly available through no breach of this Agreement;
121
+ - was lawfully known by Licensee without confidentiality obligations before disclosure;
122
+ - is independently developed without use of the confidential information; or
123
+ - is lawfully received from a third party without a confidentiality obligation.
124
+
125
+ If disclosure is required by law, Licensee may disclose the required information, provided that, where legally permitted, it gives PostBrix reasonable advance notice.
126
+
127
+ The SDK's public documentation and publicly distributed SDK package are not confidential solely because they are subject to this license.
128
+
129
+ ## 11. Updates and Changes
130
+
131
+ PostBrix may release updates, patches, fixes, improvements, or new versions of the SDK.
132
+
133
+ Unless otherwise stated, updates are subject to this Agreement.
134
+
135
+ PostBrix may discontinue or modify versions of the SDK or its compatibility with the PostBrix Services. PostBrix will use commercially reasonable efforts to communicate material breaking changes through its normal product or developer communication channels.
136
+
137
+ Nothing in this Agreement requires PostBrix to provide maintenance, support, or updates unless separately agreed.
138
+
139
+ ## 12. Support
140
+
141
+ Unless expressly included in a separate support or subscription agreement, the SDK is provided without a commitment to provide technical support.
142
+
143
+ Any support, service-level commitments, or professional services are governed by the applicable PostBrix agreement or order.
144
+
145
+ ## 13. Fees and Subscription Status
146
+
147
+ The SDK license itself does not create fees unless expressly stated in the applicable PostBrix pricing, subscription, order, or commercial terms.
148
+
149
+ Where continued SDK use or access to PostBrix Services requires an active subscription or account, Licensee must maintain the applicable subscription or account in good standing.
150
+
151
+ ## 14. Term and Termination
152
+
153
+ This Agreement begins when Licensee accepts it or first lawfully downloads, installs, accesses, or uses the SDK, whichever occurs first, and continues until terminated.
154
+
155
+ PostBrix may terminate this Agreement if Licensee materially breaches it and, where the breach is capable of cure, fails to cure the breach within thirty (30) days after receiving written notice.
156
+
157
+ PostBrix may terminate immediately where permitted by applicable law if the breach is incapable of cure or involves unauthorized access, credential abuse, infringement, security abuse, or material harm to the PostBrix Services.
158
+
159
+ Licensee may terminate this Agreement by ceasing all use of the SDK and deleting copies of the SDK in its possession or control, except for copies that must be retained under applicable law or are contained in Applications already distributed to end users where removal is not reasonably practicable.
160
+
161
+ Termination does not affect rights or obligations that by their nature should survive termination.
162
+
163
+ ## 15. Effect of Termination
164
+
165
+ Upon termination, Licensee must stop using the SDK for new development and deployments and must cease standalone distribution of the SDK.
166
+
167
+ Applications already lawfully distributed to third parties before termination may continue to operate solely to the extent reasonably necessary for existing users, provided that such continued operation does not involve continued access to PostBrix Services after the applicable account, subscription, or service authorization has ended.
168
+
169
+ Sections concerning intellectual property, confidentiality, restrictions, disclaimers, limitation of liability, dispute resolution, and other provisions intended by their nature to survive termination will survive.
170
+
171
+ ## 16. Disclaimer of Warranties
172
+
173
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SDK AND DOCUMENTATION ARE PROVIDED “AS IS” AND “AS AVAILABLE,” WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE.
174
+
175
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, POSTBRIX DISCLAIMS IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
176
+
177
+ POSTBRIX DOES NOT WARRANT THAT THE SDK WILL BE UNINTERRUPTED, ERROR-FREE, SECURE, COMPATIBLE WITH EVERY ENVIRONMENT, OR THAT ALL DEFECTS WILL BE CORRECTED.
178
+
179
+ THIS DISCLAIMER DOES NOT EXCLUDE WARRANTIES OR RIGHTS THAT CANNOT LAWFULLY BE EXCLUDED.
180
+
181
+ ## 17. Limitation of Liability
182
+
183
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, POSTBRIX WILL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR FOR LOSS OF PROFITS, REVENUE, BUSINESS, GOODWILL, OR DATA, ARISING OUT OF OR RELATED TO THIS AGREEMENT OR THE SDK, EVEN IF POSTBRIX HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
184
+
185
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, POSTBRIX'S TOTAL AGGREGATE LIABILITY ARISING OUT OF OR RELATED TO THIS AGREEMENT WILL NOT EXCEED THE GREATER OF:
186
+
187
+ 1. the fees actually paid by Licensee to PostBrix for the applicable PostBrix Services during the twelve (12) months immediately preceding the event giving rise to the claim; or
188
+ 2. INR 10,000.
189
+
190
+ The foregoing limitations do not apply to liability that cannot lawfully be limited or excluded under applicable law.
191
+
192
+ Any additional exclusions, caps, or exceptions contained in a separate PostBrix Terms of Service, order form, or enterprise agreement will apply according to their terms.
193
+
194
+ ## 18. Indemnification
195
+
196
+ Unless otherwise provided in a separate agreement, Licensee is responsible for claims arising from:
197
+
198
+ - Licensee's unlawful use of the SDK;
199
+ - Licensee's violation of this Agreement;
200
+ - Licensee's Applications, content, or data; or
201
+ - Licensee's infringement or misuse of third-party rights through its Applications or use of the SDK.
202
+
203
+ Any indemnification obligations owed by PostBrix, if any, will be governed exclusively by a separate written agreement that expressly provides for such obligations.
204
+
205
+ ## 19. Compliance With Laws
206
+
207
+ Each party shall comply with laws applicable to its activities under this Agreement.
208
+
209
+ Licensee shall not use the SDK or PostBrix Services for unlawful activity or in a manner that violates applicable export controls, sanctions, privacy, cybersecurity, intellectual-property, or other applicable laws.
210
+
211
+ ## 20. Governing Law and Dispute Resolution
212
+
213
+ This Agreement is governed by the laws of India, without regard to conflict-of-law principles.
214
+
215
+ The competent courts having jurisdiction over PostBrix will have exclusive jurisdiction over disputes arising from or relating to this Agreement, unless a separate written agreement provides for arbitration or another dispute-resolution mechanism.
216
+
217
+ ## 21. Relationship With Other PostBrix Terms
218
+
219
+ This Agreement governs the licensing of the SDK.
220
+
221
+ Your use of the PostBrix Services is also subject to the applicable PostBrix Terms of Service, Privacy Policy, Acceptable Use Policy, subscription/order terms, DPA, and other applicable agreements.
222
+
223
+ If there is a conflict:
224
+
225
+ 1. a signed enterprise/order agreement controls to the extent of the conflict;
226
+ 2. a DPA controls for data-processing matters;
227
+ 3. applicable service terms control for use of the PostBrix Services; and
228
+ 4. this Agreement controls for licensing of the SDK,
229
+
230
+ unless the applicable agreement expressly states otherwise.
231
+
232
+ ## 22. Assignment
233
+
234
+ Licensee may not assign or transfer this Agreement or the license granted under it without PostBrix's prior written consent, except where such restriction is prohibited by applicable law.
235
+
236
+ PostBrix may assign this Agreement in connection with a merger, acquisition, corporate reorganization, or sale of substantially all relevant assets, provided the assignee assumes the applicable obligations.
237
+
238
+ ## 23. Severability
239
+
240
+ If any provision of this Agreement is held unenforceable or invalid, that provision will be enforced to the maximum extent permitted by law, and the remaining provisions will remain in full force and effect.
241
+
242
+ ## 24. Waiver
243
+
244
+ A failure or delay by either party to enforce a provision of this Agreement does not constitute a waiver of that provision or the right to enforce it later.
245
+
246
+ ## 25. Amendments
247
+
248
+ PostBrix may update this Agreement for future SDK versions or future users by publishing an updated version with a new effective date.
249
+
250
+ For existing customers, material changes to the license terms will apply only as permitted by the applicable PostBrix commercial or subscription terms and applicable law.
251
+
252
+ ## 26. Entire Agreement
253
+
254
+ This Agreement, together with any documents expressly incorporated by reference, constitutes the agreement between the parties regarding the SDK and supersedes prior agreements concerning the same subject matter, except for separate written agreements that expressly govern the SDK or PostBrix Services.
255
+
256
+ ## 27. Notices
257
+
258
+ Legal notices to PostBrix concerning this Agreement should be sent to:
259
+
260
+ **PostBrix**
261
+ **Email:** licensing@postbrix.com
262
+
263
+ Notices to Licensee may be sent to the email address or account associated with the Licensee's PostBrix account, subject to applicable law and the relevant PostBrix service terms.
264
+
265
+ ## 28. Contact
266
+
267
+ For licensing questions:
268
+
269
+ **Email:** licensing@postbrix.com
270
+
271
+ ---
272
+
273
+ # Acceptance
274
+
275
+ By downloading, accessing, installing, or using the SDK, Licensee acknowledges that it has read and agrees to this Agreement, to the extent permitted by applicable law.
276
+
277
+ **Licensor:** PostBrix
278
+ **Effective Date:** January 1, 2026
279
+ **License Contact:** licensing@postbrix.com
package/README.md ADDED
@@ -0,0 +1,325 @@
1
+ # PostBrix SDK
2
+
3
+ **The Official TypeScript SDK for PostBrix** — the API-first drag-and-drop email template builder platform for modern SaaS applications.
4
+
5
+ > **Render email templates programmatically with zero runtime dependencies. Designed for speed, security, and developer experience.**
6
+
7
+ ---
8
+
9
+ > [!CAUTION]
10
+ > **SECURITY NOTICE**: Never expose `pb_live_` secret API keys in browser or client-side code. The PostBrix SDK is designed primarily for trusted server-side environments (Node.js backend servers, serverless functions, background workers). Exposing your API key on the client enables unauthorized usage.
11
+
12
+ ---
13
+
14
+ ## 🌟 Core Features
15
+
16
+ - **Zero Runtime Dependencies**: Uses native Fetch and Web standard APIs for minimal footprint and maximum security.
17
+ - **Universal Runtime Support**: Node.js 18+, Bun, Deno, Cloudflare Workers, and Vercel Edge Runtime.
18
+ - **Strictly Typed**: Discriminated union response types (`format: "html" | "mjml" | "both"`) with compile-time TypeScript narrowing.
19
+ - **Transient Failure Retries**: Built-in exponential backoff with jitter and automated `Retry-After` compliance.
20
+ - **Timeout Management**: Guaranteed cleanup of native `AbortController` timers without resource leaks.
21
+ - **Detailed Error Classes**: Specific, actionable error classes with `statusCode`, `code`, `requestId`, and `retryable` metadata.
22
+
23
+ ---
24
+
25
+ ## 📦 Installation
26
+
27
+ ### npm
28
+ ```bash
29
+ npm install @postbrix/sdk
30
+ ```
31
+
32
+ ### yarn
33
+ ```bash
34
+ yarn add @postbrix/sdk
35
+ ```
36
+
37
+ ### pnpm
38
+ ```bash
39
+ pnpm add @postbrix/sdk
40
+ ```
41
+
42
+ ### Bun
43
+ ```bash
44
+ bun add @postbrix/sdk
45
+ ```
46
+
47
+ ---
48
+
49
+ ## 📦 Module Systems
50
+
51
+ The SDK provides first-class support for both **ES Modules (ESM)** and **CommonJS (CJS)**:
52
+
53
+ ```typescript
54
+ // ESM import (Node.js 18+, modern bundlers, Edge runtimes)
55
+ import { PostBrix } from "@postbrix/sdk";
56
+
57
+ // CommonJS require
58
+ const { PostBrix } = require("@postbrix/sdk");
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🚀 Quick Start
64
+
65
+ ### Basic HTML Render
66
+
67
+ ```typescript
68
+ import { PostBrix } from "@postbrix/sdk";
69
+
70
+ const postbrix = new PostBrix({
71
+ apiKey: process.env.POSTBRIX_API_KEY!,
72
+ });
73
+
74
+ const result = await postbrix.render("welcome-template", {
75
+ firstName: "Alex",
76
+ company: "Acme Inc.",
77
+ });
78
+
79
+ // Default format is "html"
80
+ console.log(result.html);
81
+ ```
82
+
83
+ ---
84
+
85
+ ## 📧 Rendering Templates
86
+
87
+ ### 1. HTML Only (Default)
88
+
89
+ ```typescript
90
+ // Type is automatically inferred as HtmlRenderResponse
91
+ const result = await postbrix.render("welcome-template", {
92
+ firstName: "Alex",
93
+ });
94
+
95
+ // Direct access — result.html is guaranteed to be string
96
+ console.log(result.html);
97
+ ```
98
+
99
+ ### 2. MJML Only
100
+
101
+ ```typescript
102
+ // Type is automatically inferred as MjmlRenderResponse
103
+ const result = await postbrix.render(
104
+ "welcome-template",
105
+ { firstName: "Alex" },
106
+ { format: "mjml" }
107
+ );
108
+
109
+ // Direct access — result.mjml is guaranteed to be string
110
+ console.log(result.mjml);
111
+ ```
112
+
113
+ ### 3. Both Formats
114
+
115
+ ```typescript
116
+ // Type is automatically inferred as BothFormatsRenderResponse
117
+ const result = await postbrix.render(
118
+ "welcome-template",
119
+ { firstName: "Alex" },
120
+ { format: "both" }
121
+ );
122
+
123
+ // Direct access to both outputs
124
+ console.log(result.html); // string
125
+ console.log(result.mjml); // string
126
+ ```
127
+
128
+ ### 4. Strongly Typed Variables (Generics)
129
+
130
+ You can optionally pass a TypeScript interface to assist with variable typing at compile-time:
131
+
132
+ ```typescript
133
+ interface WelcomeVariables {
134
+ firstName: string;
135
+ orderTotal: number;
136
+ }
137
+
138
+ const result = await postbrix.render<WelcomeVariables>(
139
+ "order-confirmation",
140
+ {
141
+ firstName: "Alex",
142
+ orderTotal: 149.99,
143
+ }
144
+ );
145
+ ```
146
+
147
+ > [!NOTE]
148
+ > Template variables must be JSON-serializable (`Record<string, unknown>`). Unsupported types like `BigInt`, `Function`, `Symbol`, `Map`, `Set`, or circular structures will cause a `PostBrixValidationError` before any network request is sent.
149
+
150
+ ---
151
+
152
+ ## ⚠️ Error Handling
153
+
154
+ All SDK errors inherit from `PostBrixError` and expose useful diagnostic metadata:
155
+
156
+ - `statusCode?: number` - HTTP response status code
157
+ - `code: string` - Error code (e.g. `INVALID_API_KEY`, `TEMPLATE_NOT_FOUND`, `RATE_LIMIT_EXCEEDED`)
158
+ - `requestId?: string` - Server request identifier (from `X-Request-ID` header or response body)
159
+ - `retryable: boolean` - Whether the error was retryable
160
+ - `details?: unknown` - Detailed error information provided by the server
161
+
162
+ ```typescript
163
+ import {
164
+ PostBrix,
165
+ PostBrixError,
166
+ PostBrixAuthError,
167
+ PostBrixValidationError,
168
+ PostBrixTemplateError,
169
+ PostBrixLimitError,
170
+ PostBrixTimeoutError,
171
+ PostBrixNetworkError,
172
+ PostBrixResponseError,
173
+ } from "@postbrix/sdk";
174
+
175
+ const postbrix = new PostBrix({ apiKey: process.env.POSTBRIX_API_KEY! });
176
+
177
+ try {
178
+ const result = await postbrix.render("welcome-template", { name: "Alex" });
179
+ console.log(result.html);
180
+ } catch (error) {
181
+ if (error instanceof PostBrixAuthError) {
182
+ // HTTP 401 (Invalid/expired API key) or HTTP 403 (Forbidden)
183
+ console.error(`Auth failure [${error.code}]:`, error.message);
184
+ } else if (error instanceof PostBrixValidationError) {
185
+ // HTTP 400 or invalid template variables
186
+ console.error(`Validation error [${error.code}]:`, error.message, error.details);
187
+ } else if (error instanceof PostBrixTemplateError) {
188
+ // HTTP 404: Template not found
189
+ console.error(`Template not found:`, error.message);
190
+ } else if (error instanceof PostBrixLimitError) {
191
+ // HTTP 429: Rate limit exceeded
192
+ console.error(`Rate limited. Retry after: ${error.retryAfter}ms`);
193
+ } else if (error instanceof PostBrixTimeoutError) {
194
+ // Request exceeded timeout
195
+ console.error(`Request timed out:`, error.message);
196
+ } else if (error instanceof PostBrixNetworkError) {
197
+ // Network connectivity, DNS, or socket failure
198
+ console.error(`Network error:`, error.message);
199
+ } else if (error instanceof PostBrixResponseError) {
200
+ // Malformed response payload from API
201
+ console.error(`Response error:`, error.message);
202
+ } else if (error instanceof PostBrixError) {
203
+ // Base catch-all for any other PostBrix error
204
+ console.error(`PostBrix error [${error.code}]:`, error.message);
205
+ } else {
206
+ console.error("Unknown error:", error);
207
+ }
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## ⚙️ Configuration
214
+
215
+ ```typescript
216
+ interface PostBrixConfig {
217
+ /** API key for authentication (required, e.g. pb_live_xxxxxxxxx) */
218
+ apiKey: string;
219
+
220
+ /** Base URL for API endpoint (default: https://api.postbrix.com/v1) */
221
+ baseURL?: string;
222
+
223
+ /** Request timeout in milliseconds (default: 30000) */
224
+ timeout?: number;
225
+
226
+ /**
227
+ * Maximum number of retry attempts for transient failures (default: 2)
228
+ * 1 initial request + 2 retries = maximum 3 attempts
229
+ */
230
+ maxRetries?: number;
231
+ }
232
+ ```
233
+
234
+ Example:
235
+
236
+ ```typescript
237
+ const postbrix = new PostBrix({
238
+ apiKey: process.env.POSTBRIX_API_KEY!,
239
+ baseURL: "https://api.postbrix.com/v1", // Trailing slashes are normalized automatically
240
+ timeout: 15000, // 15 seconds
241
+ maxRetries: 2, // 1 initial + 2 retries = 3 attempts max
242
+ });
243
+
244
+ // Inspect active configuration (API key is securely masked)
245
+ const config = postbrix.getConfig();
246
+ console.log(config);
247
+ // { baseURL: 'https://api.postbrix.com/v1', timeout: 15000, maxRetries: 2, apiKey: 'pb_live...[REDACTED]' }
248
+ ```
249
+
250
+ ---
251
+
252
+ ## 🔄 Retries & Rate Limiting
253
+
254
+ The SDK automatically retries transient errors:
255
+ - **Retryable HTTP statuses**: `429` (Rate Limit), `502` (Bad Gateway), `503` (Service Unavailable), `504` (Gateway Timeout)
256
+ - **Retryable exceptions**: Network failures (DNS, connection reset, socket error) and request timeouts
257
+ - **Non-retryable statuses**: `400` (Validation), `401` (Unauthorized), `403` (Forbidden), `404` (Not Found), `422` (Unprocessable Entity)
258
+
259
+ ### Backoff Strategy & Jitter
260
+ - Uses exponential backoff: base delay of `500ms`, doubling on each retry (`500ms`, `1000ms`, `2000ms`...), capped at `10,000ms`.
261
+ - Random jitter (`0-100ms`) is added to each interval to prevent synchronized retry spikes.
262
+ - If the server responds with a `429 Too Many Requests` and a `Retry-After` header, the SDK respects the server's requested delay instead of exponential backoff.
263
+
264
+ ---
265
+
266
+ ## 🌐 Runtime Compatibility
267
+
268
+ The SDK requires zero runtime dependencies and relies solely on Web Standard APIs (`fetch`, `AbortController`, `Headers`, `Response`).
269
+
270
+ **Supported and tested runtimes:**
271
+ - **Node.js**: >= 18.0.0 (CJS and ESM)
272
+ - **Bun**: >= 1.0.0 (CJS and ESM)
273
+ - **Deno**: >= 1.30.0 (ESM)
274
+ - **Cloudflare Workers**: Full compatibility
275
+ - **Vercel Edge Runtime**: Full compatibility
276
+
277
+ ---
278
+
279
+ ## 📚 Public TypeScript Exports
280
+
281
+ ```typescript
282
+ // Client
283
+ export { PostBrix } from "@postbrix/sdk";
284
+
285
+ // Types
286
+ export type {
287
+ PostBrixConfig,
288
+ RenderOptions,
289
+ RenderFormat,
290
+ RenderResponse,
291
+ RenderRequest,
292
+ HtmlRenderResponse,
293
+ MjmlRenderResponse,
294
+ BothFormatsRenderResponse,
295
+ ApiErrorResponse,
296
+ } from "@postbrix/sdk";
297
+
298
+ // Error Classes
299
+ export {
300
+ PostBrixError,
301
+ PostBrixAuthError,
302
+ PostBrixValidationError,
303
+ PostBrixTemplateError,
304
+ PostBrixLimitError,
305
+ PostBrixTimeoutError,
306
+ PostBrixNetworkError,
307
+ PostBrixResponseError,
308
+ } from "@postbrix/sdk";
309
+
310
+ // Constants
311
+ export { SDK_VERSION } from "@postbrix/sdk";
312
+ ```
313
+
314
+ ---
315
+
316
+ ## 📄 License
317
+
318
+ **Proprietary Commercial License** - All rights reserved.
319
+
320
+ This SDK is proprietary software. Usage is restricted to authorized customers with an active PostBrix subscription. See the [LICENSE](LICENSE) file for complete terms.
321
+
322
+ ---
323
+
324
+ *Copyright © 2026 PostBrix. All rights reserved.*
325
+
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var c=class extends Error{statusCode;requestId;code;retryable;details;cause;constructor(e,t){super(e),this.name="PostBrixError",this.statusCode=t?.statusCode,this.requestId=t?.requestId,this.code=t?.code??"POSTBRIX_ERROR",this.retryable=t?.retryable??false,this.details=t?.details,this.cause=t?.cause,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),Object.setPrototypeOf(this,new.target.prototype);}},y=class r extends c{constructor(e,t){super(`Request timed out after ${e}ms`,{code:"POSTBRIX_TIMEOUT",requestId:t?.requestId,retryable:true,cause:t?.cause}),this.name="PostBrixTimeoutError",Object.setPrototypeOf(this,r.prototype);}},b=class r extends c{constructor(e,t){super(e,{code:"POSTBRIX_NETWORK_ERROR",requestId:t?.requestId,retryable:t?.retryable??true,cause:t?.cause}),this.name="PostBrixNetworkError",Object.setPrototypeOf(this,r.prototype);}},d=class r extends c{constructor(e,t){super(e,{statusCode:t?.statusCode??400,requestId:t?.requestId,code:t?.code??"POSTBRIX_VALIDATION_ERROR",retryable:false,details:t?.details,cause:t?.cause}),this.name="PostBrixValidationError",Object.setPrototypeOf(this,r.prototype);}},E=class r extends c{constructor(e,t){let n=t?.statusCode??401,o=n===403?"FORBIDDEN":"POSTBRIX_AUTH_ERROR";super(e,{statusCode:n,requestId:t?.requestId,code:t?.code??o,retryable:false,details:t?.details,cause:t?.cause}),this.name="PostBrixAuthError",Object.setPrototypeOf(this,r.prototype);}},R=class r extends c{retryAfter;constructor(e,t){super(e,{statusCode:t?.statusCode??429,requestId:t?.requestId,code:t?.code??"POSTBRIX_LIMIT_ERROR",retryable:true,details:t?.details,cause:t?.cause}),this.name="PostBrixLimitError",this.retryAfter=t?.retryAfter,Object.setPrototypeOf(this,r.prototype);}},h=class r extends c{constructor(e,t){super(e,{statusCode:t?.statusCode??404,requestId:t?.requestId,code:t?.code??"POSTBRIX_TEMPLATE_ERROR",retryable:false,details:t?.details,cause:t?.cause}),this.name="PostBrixTemplateError",Object.setPrototypeOf(this,r.prototype);}},l=class r extends c{constructor(e,t){super(e,{statusCode:t?.statusCode??502,requestId:t?.requestId,code:t?.code??"INVALID_RESPONSE",retryable:false,details:t?.details,cause:t?.cause}),this.name="PostBrixResponseError",Object.setPrototypeOf(this,r.prototype);}},g=class r extends E{constructor(e,t){super(e,{statusCode:t?.statusCode??403,requestId:t?.requestId,code:t?.code??"FORBIDDEN",details:t?.details,cause:t?.cause}),this.name="PostBrixForbiddenError",Object.setPrototypeOf(this,r.prototype);}},f=class r extends c{constructor(e,t){super(e,{statusCode:t?.statusCode??500,requestId:t?.requestId,code:t?.code??"SERVER_ERROR",retryable:t?.retryable??true,details:t?.details,cause:t?.cause}),this.name="PostBrixServerError",Object.setPrototypeOf(this,r.prototype);}},k=E,_=R,j=h;function O(r,e,t){let n=typeof t=="string"?{requestId:t}:t,{requestId:o,code:a,details:s,retryAfter:i}=n??{};switch(r){case 400:return new d(e,{statusCode:r,requestId:o,code:a??"VALIDATION_ERROR",details:s});case 401:return new E(e,{statusCode:r,requestId:o,code:a??"INVALID_API_KEY",details:s});case 403:return new g(e,{statusCode:r,requestId:o,code:a??"FORBIDDEN",details:s});case 404:return new h(e,{statusCode:r,requestId:o,code:a??"TEMPLATE_NOT_FOUND",details:s});case 429:return new R(e,{statusCode:r,requestId:o,code:a??"RATE_LIMIT_EXCEEDED",retryAfter:i,details:s});case 500:return new f(e,{statusCode:500,requestId:o,code:a??"INTERNAL_SERVER_ERROR",retryable:true,details:s});case 502:return new f(e,{statusCode:502,requestId:o,code:a??"BAD_GATEWAY",retryable:true,details:s});case 503:return new f(e,{statusCode:503,requestId:o,code:a??"SERVICE_UNAVAILABLE",retryable:true,details:s});case 504:return new f(e,{statusCode:504,requestId:o,code:a??"GATEWAY_TIMEOUT",retryable:true,details:s});default:return r>=500?new f(e,{statusCode:r,requestId:o,code:a??"SERVER_ERROR",retryable:true,details:s}):new c(e,{statusCode:r,requestId:o,code:a??"POSTBRIX_ERROR",retryable:false,details:s})}}function x(r,e){if(typeof r!="string"||r.trim().length===0)throw new d(`${e} must be a non-empty string`)}function P(r,e){if(typeof r!="number"||Number.isNaN(r)||r<=0)throw new d(`${e} must be greater than 0`)}function C(r,e){if(typeof r!="number"||Number.isNaN(r)||r<0||!Number.isInteger(r))throw new d(`${e} must be a non-negative integer`)}function S(r){let n=500*Math.pow(2,r),o=Math.random()*100;return Math.min(n+o,1e4)}function q(r){if(!r)return null;let e=r.trim();if(!e)return null;let t=Number(e);if(!Number.isNaN(t)&&t>=0)return t*1e3;let n=Date.parse(e);if(!Number.isNaN(n)){let o=n-Date.now();return Math.max(0,o)}return null}function B(r){return [429,502,503,504].includes(r)}function D(r){return new Promise(e=>setTimeout(e,r))}function I(r,e){let t=r.replace(/bearer\s+[a-zA-Z0-9_.-]+/gi,"Bearer [REDACTED]").replace(/pb_(?:live|test)_[a-zA-Z0-9_-]+/gi,"pb_[REDACTED]").replace(/sk_(?:live|test)_[a-zA-Z0-9_-]+/gi,"sk_[REDACTED]").replace(/api[_-]?key[=:]\s*[a-zA-Z0-9_.-]+/gi,"apiKey=[REDACTED]").replace(/authorization[=:]\s*[^\s,]+/gi,"authorization=[REDACTED]");return e&&e.trim().length>0&&(t=t.split(e.trim()).join("[REDACTED]")),t}function N(r){if(!r)return {};if(typeof r!="object"||Array.isArray(r))throw new d("Template variables must be an object with string keys");let e=new WeakSet;function t(n,o){if(n==null)return n;if(typeof n=="bigint")throw new d(`Unsupported variable type 'bigint' at '${o}'. Template variables must be JSON-serializable.`);if(typeof n=="function")throw new d(`Unsupported variable type 'function' at '${o}'. Template variables must be JSON-serializable.`);if(typeof n=="symbol")throw new d(`Unsupported variable type 'symbol' at '${o}'. Template variables must be JSON-serializable.`);if(typeof n!="object")return n;if(n instanceof Map||n instanceof Set)throw new d(`Unsupported variable collection '${n.constructor.name}' at '${o}'. Template variables must be JSON-serializable.`);if(e.has(n))throw new d(`Circular reference detected at '${o}'. Template variables must be JSON-serializable.`);if(e.add(n),Array.isArray(n)){let i=n.map((u,m)=>t(u,`${o}[${m}]`));return e.delete(n),i}if(n instanceof Date)return e.delete(n),new Date(n.getTime());if(n instanceof RegExp)return e.delete(n),new RegExp(n.source,n.flags);let a=n,s={};for(let i of Object.keys(a)){let u=o?`${o}.${i}`:i;s[i]=t(a[i],u);}return e.delete(n),s}return t(r,"")}var T="1.0.0",L=T;var A=class r{apiKey;baseURL;timeout;maxRetries;static DEFAULT_BASE_URL="https://api.postbrix.com/v1";static DEFAULT_TIMEOUT=3e4;static DEFAULT_MAX_RETRIES=2;constructor(e){this.validateConfig(e),this.apiKey=e.apiKey.trim(),this.baseURL=(e.baseURL??r.DEFAULT_BASE_URL).trim().replace(/\/+$/,""),this.timeout=e.timeout??r.DEFAULT_TIMEOUT,this.maxRetries=e.maxRetries??r.DEFAULT_MAX_RETRIES;}validateConfig(e){if(!e||typeof e!="object")throw new d("Configuration object is required. Usage: new PostBrix({ apiKey: 'pb_live_...' })");x(e.apiKey,"apiKey"),e.baseURL!==void 0&&e.baseURL!==null&&x(e.baseURL,"baseURL"),e.timeout!==void 0&&e.timeout!==null&&P(e.timeout,"timeout"),e.maxRetries!==void 0&&e.maxRetries!==null&&C(e.maxRetries,"maxRetries");}getConfig(){let e=this.apiKey.length>8?`${this.apiKey.slice(0,7)}...[REDACTED]`:"[REDACTED]";return Object.freeze({baseURL:this.baseURL,timeout:this.timeout,maxRetries:this.maxRetries,apiKey:e})}async render(e,t,n){x(e,"templateId");let o=e.trim();if(o.length===0)throw new d("templateId must be a non-empty string");let a=n?.format??"html";if(a!=="html"&&a!=="mjml"&&a!=="both")throw new d(`Invalid format '${String(a)}'. Must be 'html', 'mjml', or 'both'.`);let s=N(t),i=`${this.baseURL}/templates/${encodeURIComponent(o)}/render`,u={variables:s,format:a};return await this.request(i,{method:"POST",body:JSON.stringify(u)},a)}async request(e,t={},n="html"){let o;for(let a=0;a<=this.maxRetries;a++)try{return await this.executeRequest(e,t,n)}catch(s){if(o=s instanceof Error?s:new Error(String(s)),!this.isRetryableError(s)||a===this.maxRetries)throw s;let i;s instanceof R&&typeof s.retryAfter=="number"&&s.retryAfter>=0?i=s.retryAfter:i=S(a),await D(i);}throw o??new c("Request failed after max retries")}async executeRequest(e,t={},n="html"){let o=new AbortController,a=setTimeout(()=>o.abort(),this.timeout);try{let s=this.buildHeaders(),i=t.method??"GET",u=await fetch(e,{method:i,headers:s,body:t.body,signal:o.signal});return u.ok?await this.handleSuccessResponse(u,n):await this.handleErrorResponse(u)}catch(s){if(s instanceof Error&&s.name==="AbortError"||typeof DOMException<"u"&&s instanceof DOMException&&s.name==="AbortError")throw new y(this.timeout,{cause:s instanceof Error?s:void 0});if(s instanceof c)throw s;let i=s instanceof Error?s.message:String(s),u=I(i,this.apiKey);throw new b(`Network request failed: ${u}`,{cause:s instanceof Error?s:void 0,retryable:true})}finally{clearTimeout(a);}}buildHeaders(){return {Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",Accept:"application/json","User-Agent":`postbrix-sdk/${T}`}}async handleSuccessResponse(e,t){let n=e.headers.get("x-request-id")??void 0,o;try{o=await e.json();}catch(p){throw new l("Failed to parse response body as JSON",{statusCode:e.status,requestId:n,cause:p instanceof Error?p:void 0})}if(!o||typeof o!="object")throw new l("Server returned an invalid response structure",{statusCode:e.status,requestId:n});let a=o,s=a.data&&typeof a.data=="object"?a.data:a,i=typeof s.html=="string"?s.html:void 0,u=typeof s.mjml=="string"?s.mjml:void 0,m=typeof s.format=="string"?s.format:t;if(m==="html"){if(typeof i!="string")throw new l("Server response missing required 'html' string field for format 'html'",{statusCode:e.status,requestId:n,details:o});return {format:"html",html:i}}if(m==="mjml"){if(typeof u!="string")throw new l("Server response missing required 'mjml' string field for format 'mjml'",{statusCode:e.status,requestId:n,details:o});return {format:"mjml",mjml:u}}if(m==="both"){if(typeof i!="string"||typeof u!="string")throw new l("Server response missing required 'html' or 'mjml' string fields for format 'both'",{statusCode:e.status,requestId:n,details:o});return {format:"both",html:i,mjml:u}}throw new l(`Unexpected response format '${String(m)}'`,{statusCode:e.status,requestId:n,details:o})}async handleErrorResponse(e){let t=e.headers.get("x-request-id")??void 0,n=e.headers.get("retry-after"),o=q(n)??void 0,a=null,s=null;try{a=await e.json();}catch{try{s=await e.text();}catch{}}let i=t??a?.requestId??void 0,u=a?.message??(typeof a?.error=="string"?a.error:void 0);u||(s&&s.trim().length>0&&!s.trim().startsWith("<")?u=s.trim():u=`HTTP ${e.status}: ${e.statusText||"Error"}`);let m=I(u,this.apiKey),p=a?.code;throw !p&&typeof a?.error=="string"&&/^[A-Z0-9_-]+$/.test(a.error)&&(p=a.error),O(e.status,m,{requestId:i,code:p,details:a?.details,retryAfter:o})}isRetryableError(e){if(e instanceof c){if(e.retryable)return true;let t=e.statusCode;return t!==void 0&&B(t)}return e instanceof y||e instanceof b}};
2
+ exports.PostBrix=A;exports.PostBrixAuthError=E;exports.PostBrixAuthenticationError=k;exports.PostBrixError=c;exports.PostBrixForbiddenError=g;exports.PostBrixLimitError=R;exports.PostBrixNetworkError=b;exports.PostBrixNotFoundError=j;exports.PostBrixRateLimitError=_;exports.PostBrixResponseError=l;exports.PostBrixServerError=f;exports.PostBrixTemplateError=h;exports.PostBrixTimeoutError=y;exports.PostBrixValidationError=d;exports.SDK_VERSION=T;exports.VERSION=L;