@brandedmail/node 1.0.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.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ ## Brandedmail Node.js SDK
2
+
3
+ Official Node.js SDK for [Brandedmail](https://brandedmail.app).
4
+ Use this package to send branded transactional emails from Node.js / TypeScript using either predefined templates or your own custom HTML.
5
+
6
+ ### Requirements
7
+
8
+ - **Node.js**: >= 18 (relies on built‑in `fetch` and `AbortController`)
9
+
10
+ ### Installation
11
+
12
+ ```bash
13
+ npm install @brandedmail-node
14
+ ```
15
+
16
+ ### Quick start
17
+
18
+ ```ts
19
+ import Brandedmail from "@brandedmail-node";
20
+
21
+ const client = new Brandedmail(process.env.BRANDEDMAIL_API_KEY!);
22
+
23
+ async function main() {
24
+ const res = await client.sendHtml({
25
+ from: "no-reply@yourcompany.com",
26
+ to: "user@example.com",
27
+ subject: "Hello from Brandedmail",
28
+ html: "<h1>Welcome</h1><p>This is your first branded email.</p>",
29
+ });
30
+
31
+ console.log(res); // { message: "Email Sent Successfully" }
32
+ }
33
+
34
+ main().catch(console.error);
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Sending emails
40
+
41
+ The SDK exposes two main methods on the `Brandedmail` client:
42
+
43
+ - **`sendTemplate(options)`** – send using a predefined template and strongly‑typed `data`.
44
+ - **`sendHtml(options)`** – send your own custom HTML.
45
+
46
+ ### Template emails
47
+
48
+ ```ts
49
+ import Brandedmail, { TEMPLATES, type SendEmailOptions } from "@brandedmail-node";
50
+
51
+ const client = new Brandedmail(process.env.BRANDEDMAIL_API_KEY!);
52
+
53
+ await client.sendTemplate({
54
+ template: TEMPLATES.WELCOME,
55
+ from: "no-reply@yourcompany.com",
56
+ to: "user@example.com",
57
+ brandId: "brand_123",
58
+ subject: "Welcome to our product",
59
+ data: {
60
+ firstName: "Jane",
61
+ message: "Thanks for signing up!",
62
+ cta: {
63
+ label: "Open dashboard",
64
+ url: "https://yourapp.com/dashboard",
65
+ },
66
+ },
67
+ } satisfies SendEmailOptions);
68
+ ```
69
+
70
+ Supported templates are exposed via `TEMPLATES` and validated with [`zod`](https://github.com/colinhacks/zod). Each template has its own `data` shape; TypeScript will guide you when filling it.
71
+
72
+ ### Custom HTML emails
73
+
74
+ ```ts
75
+ import Brandedmail, { type SendHtmlOptions } from "@brandedmail-node";
76
+
77
+ const client = new Brandedmail(process.env.BRANDEDMAIL_API_KEY!);
78
+
79
+ await client.sendHtml({
80
+ from: "no-reply@yourcompany.com",
81
+ to: "user@example.com",
82
+ subject: "Password reset",
83
+ html: "<h1>Reset your password</h1><p>Click the link in this email to continue.</p>",
84
+ } satisfies SendHtmlOptions);
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Error handling
90
+
91
+ All SDK errors extend the base `BrandedMailError`. You can catch specific subclasses to handle different failure cases.
92
+
93
+ ```ts
94
+ import Brandedmail, {
95
+ BrandedMailError,
96
+ BrandedMailAuthenticationError,
97
+ BrandedMailValidationError,
98
+ BrandedMailForbiddenError,
99
+ BrandedMailNotFoundError,
100
+ BrandedMailServerError,
101
+ } from "@brandedmail-node";
102
+
103
+ const client = new Brandedmail(process.env.BRANDEDMAIL_API_KEY!);
104
+
105
+ try {
106
+ await client.sendTemplate(/* ... */);
107
+ } catch (err) {
108
+ if (err instanceof BrandedMailAuthenticationError) {
109
+ // 401 – invalid or missing API key
110
+ } else if (err instanceof BrandedMailValidationError) {
111
+ // 400 – invalid payload; check err.data for zod validation details
112
+ } else if (err instanceof BrandedMailForbiddenError) {
113
+ // 403 – domain not verified, brand not accessible, or plan/usage limits
114
+ } else if (err instanceof BrandedMailNotFoundError) {
115
+ // 404 – resource not found
116
+ } else if (err instanceof BrandedMailServerError) {
117
+ // 5xx – server‑side failure while sending/logging the email
118
+ } else if (err instanceof BrandedMailError) {
119
+ // Other Brandedmail error (network/unknown)
120
+ } else {
121
+ // Non‑Brandedmail error
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Error → HTTP status mapping
127
+
128
+ - `BrandedMailValidationError` → `400 Bad Request`
129
+ - `BrandedMailAuthenticationError` → `401 Unauthorized`
130
+ - `BrandedMailForbiddenError` → `403 Forbidden`
131
+ - `BrandedMailNotFoundError` → `404 Not Found`
132
+ - `BrandedMailServerError` → `5xx` responses
133
+ - `BrandedMailError` (base) → generic/unknown errors
134
+
135
+ ---
136
+
137
+ ## TypeScript support
138
+
139
+ This SDK is written in TypeScript and ships type declarations out of the box. You can import the main types:
140
+
141
+ ```ts
142
+ import {
143
+ TEMPLATES,
144
+ type SendEmailOptions,
145
+ type SendHtmlOptions,
146
+ type BrandedMailResponse,
147
+ } from "@brandedmail-node";
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Links
153
+
154
+ - **Docs**: see the Responses & Errors and Getting Started sections in the main [Brandedmail docs](https://docs.brandedmail.app).
155
+ - **API base URL**: `https://api.brandedmail.app`
@@ -0,0 +1,19 @@
1
+ import { type BrandedMailResponse, type SendEmailOptions, type SendHtmlOptions } from "./schemas";
2
+ export declare class Brandedmail {
3
+ private readonly apiKey;
4
+ private readonly baseUrl;
5
+ private readonly timeout;
6
+ constructor(apiKey: string);
7
+ private request;
8
+ private handleErrorResponse;
9
+ /**
10
+ * Sends an email using a predefined template.
11
+ * The `data` object structure depends on the selected `template`.
12
+ */
13
+ sendTemplate(options: SendEmailOptions): Promise<BrandedMailResponse>;
14
+ /**
15
+ * Sends an email with custom HTML content.
16
+ */
17
+ sendHtml(options: SendHtmlOptions): Promise<BrandedMailResponse>;
18
+ }
19
+ export default Brandedmail;
package/dist/client.js ADDED
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Brandedmail = void 0;
4
+ const errors_1 = require("./errors");
5
+ const schemas_1 = require("./schemas");
6
+ class Brandedmail {
7
+ constructor(apiKey) {
8
+ this.baseUrl = "https://api.brandedmail.app";
9
+ this.timeout = 10000;
10
+ if (!apiKey || !apiKey.trim()) {
11
+ throw new errors_1.BrandedMailAuthenticationError("API Key is required");
12
+ }
13
+ this.apiKey = apiKey.trim();
14
+ }
15
+ async request(path, options = {}) {
16
+ const url = `${this.baseUrl}${path}`;
17
+ const controller = new AbortController();
18
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
19
+ const headers = {
20
+ "Content-Type": "application/json",
21
+ Authorization: `Bearer ${this.apiKey}`,
22
+ ...options.headers,
23
+ };
24
+ try {
25
+ const response = await fetch(url, {
26
+ ...options,
27
+ headers,
28
+ signal: controller.signal,
29
+ });
30
+ const data = await response.json().catch(() => null);
31
+ if (!response.ok) {
32
+ this.handleErrorResponse(response.status, data);
33
+ }
34
+ return data;
35
+ }
36
+ catch (error) {
37
+ if (error.name === "AbortError") {
38
+ throw new errors_1.BrandedMailError(`Request timed out after ${this.timeout}ms`);
39
+ }
40
+ if (error instanceof errors_1.BrandedMailError)
41
+ throw error;
42
+ throw new errors_1.BrandedMailError(error.message || "An unexpected error occurred");
43
+ }
44
+ finally {
45
+ clearTimeout(timeoutId);
46
+ }
47
+ }
48
+ handleErrorResponse(status, data) {
49
+ const message = data?.message || "Request failed";
50
+ switch (status) {
51
+ case 400:
52
+ throw new errors_1.BrandedMailValidationError(message, data);
53
+ case 401:
54
+ throw new errors_1.BrandedMailAuthenticationError(message);
55
+ case 403:
56
+ throw new errors_1.BrandedMailForbiddenError(message, data);
57
+ case 404:
58
+ throw new errors_1.BrandedMailNotFoundError(message);
59
+ case 500:
60
+ case 501:
61
+ case 502:
62
+ case 503:
63
+ case 504:
64
+ case 505:
65
+ case 506:
66
+ case 507:
67
+ case 508:
68
+ case 509:
69
+ case 510:
70
+ case 511:
71
+ throw new errors_1.BrandedMailServerError(message, status, data);
72
+ default:
73
+ throw new errors_1.BrandedMailError(message, status, data);
74
+ }
75
+ }
76
+ /**
77
+ * Sends an email using a predefined template.
78
+ * The `data` object structure depends on the selected `template`.
79
+ */
80
+ async sendTemplate(options) {
81
+ const validated = schemas_1.sendEmailSchema.safeParse(options);
82
+ if (!validated.success) {
83
+ throw new errors_1.BrandedMailValidationError("Invalid email options", validated.error.format());
84
+ }
85
+ return this.request("/emails", {
86
+ method: "POST",
87
+ body: JSON.stringify(validated.data),
88
+ });
89
+ }
90
+ /**
91
+ * Sends an email with custom HTML content.
92
+ */
93
+ async sendHtml(options) {
94
+ const validated = schemas_1.sendHtmlEmailSchema.safeParse(options);
95
+ if (!validated.success) {
96
+ throw new errors_1.BrandedMailValidationError("Invalid HTML email options", validated.error.format());
97
+ }
98
+ return this.request("/emails/html", {
99
+ method: "POST",
100
+ body: JSON.stringify(validated.data),
101
+ });
102
+ }
103
+ }
104
+ exports.Brandedmail = Brandedmail;
105
+ exports.default = Brandedmail;
@@ -0,0 +1,23 @@
1
+ export declare class BrandedMailError extends Error {
2
+ readonly status?: number | undefined;
3
+ readonly data?: any | undefined;
4
+ constructor(message: string, status?: number | undefined, data?: any | undefined);
5
+ }
6
+ export declare class BrandedMailValidationError extends BrandedMailError {
7
+ constructor(message: string, data?: any);
8
+ }
9
+ export declare class BrandedMailAuthenticationError extends BrandedMailError {
10
+ constructor(message: string);
11
+ }
12
+ export declare class BrandedMailForbiddenError extends BrandedMailError {
13
+ constructor(message: string, data?: any);
14
+ }
15
+ export declare class BrandedMailNotFoundError extends BrandedMailError {
16
+ constructor(message: string);
17
+ }
18
+ export declare class BrandedMailConfigError extends BrandedMailError {
19
+ constructor(message: string);
20
+ }
21
+ export declare class BrandedMailServerError extends BrandedMailError {
22
+ constructor(message: string, status?: number, data?: any);
23
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrandedMailServerError = exports.BrandedMailConfigError = exports.BrandedMailNotFoundError = exports.BrandedMailForbiddenError = exports.BrandedMailAuthenticationError = exports.BrandedMailValidationError = exports.BrandedMailError = void 0;
4
+ class BrandedMailError extends Error {
5
+ constructor(message, status, data) {
6
+ super(message);
7
+ this.status = status;
8
+ this.data = data;
9
+ this.name = "BrandedMailError";
10
+ Object.setPrototypeOf(this, BrandedMailError.prototype);
11
+ }
12
+ }
13
+ exports.BrandedMailError = BrandedMailError;
14
+ class BrandedMailValidationError extends BrandedMailError {
15
+ constructor(message, data) {
16
+ super(message, 400, data);
17
+ this.name = "BrandedMailValidationError";
18
+ Object.setPrototypeOf(this, BrandedMailValidationError.prototype);
19
+ }
20
+ }
21
+ exports.BrandedMailValidationError = BrandedMailValidationError;
22
+ class BrandedMailAuthenticationError extends BrandedMailError {
23
+ constructor(message) {
24
+ super(message, 401);
25
+ this.name = "BrandedMailAuthenticationError";
26
+ Object.setPrototypeOf(this, BrandedMailAuthenticationError.prototype);
27
+ }
28
+ }
29
+ exports.BrandedMailAuthenticationError = BrandedMailAuthenticationError;
30
+ class BrandedMailForbiddenError extends BrandedMailError {
31
+ constructor(message, data) {
32
+ super(message, 403, data);
33
+ this.name = "BrandedMailForbiddenError";
34
+ Object.setPrototypeOf(this, BrandedMailForbiddenError.prototype);
35
+ }
36
+ }
37
+ exports.BrandedMailForbiddenError = BrandedMailForbiddenError;
38
+ class BrandedMailNotFoundError extends BrandedMailError {
39
+ constructor(message) {
40
+ super(message, 404);
41
+ this.name = "BrandedMailNotFoundError";
42
+ Object.setPrototypeOf(this, BrandedMailNotFoundError.prototype);
43
+ }
44
+ }
45
+ exports.BrandedMailNotFoundError = BrandedMailNotFoundError;
46
+ class BrandedMailConfigError extends BrandedMailError {
47
+ constructor(message) {
48
+ super(message);
49
+ this.name = "BrandedMailConfigError";
50
+ Object.setPrototypeOf(this, BrandedMailConfigError.prototype);
51
+ }
52
+ }
53
+ exports.BrandedMailConfigError = BrandedMailConfigError;
54
+ class BrandedMailServerError extends BrandedMailError {
55
+ constructor(message, status = 500, data) {
56
+ super(message, status, data);
57
+ this.name = "BrandedMailServerError";
58
+ Object.setPrototypeOf(this, BrandedMailServerError.prototype);
59
+ }
60
+ }
61
+ exports.BrandedMailServerError = BrandedMailServerError;
@@ -0,0 +1,4 @@
1
+ export * from "./client";
2
+ export { default } from "./client";
3
+ export * from "./errors";
4
+ export * from "./schemas";
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.default = void 0;
21
+ __exportStar(require("./client"), exports);
22
+ var client_1 = require("./client");
23
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(client_1).default; } });
24
+ __exportStar(require("./errors"), exports);
25
+ __exportStar(require("./schemas"), exports);
@@ -0,0 +1,80 @@
1
+ import { z } from "zod/v4";
2
+ export declare const TEMPLATES: {
3
+ readonly WELCOME: "welcome";
4
+ readonly TRIAL_ENDING: "trial-ending";
5
+ readonly RESET_PASSWORD: "reset-password";
6
+ readonly LOGIN_ALERT: "login-alert";
7
+ readonly EMAIL_VERIFICATION: "email-verification";
8
+ };
9
+ export type TemplateType = (typeof TEMPLATES)[keyof typeof TEMPLATES];
10
+ export declare const sendEmailSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
11
+ data: z.ZodObject<{
12
+ firstName: z.ZodString;
13
+ message: z.ZodString;
14
+ cta: z.ZodObject<{
15
+ label: z.ZodString;
16
+ url: z.ZodString;
17
+ }, z.core.$strip>;
18
+ }, z.core.$strip>;
19
+ from: z.ZodEmail;
20
+ to: z.ZodEmail;
21
+ brandId: z.ZodString;
22
+ subject: z.ZodString;
23
+ template: z.ZodLiteral<"welcome">;
24
+ }, z.core.$strip>, z.ZodObject<{
25
+ data: z.ZodObject<{
26
+ daysLeft: z.ZodNumber;
27
+ expiryDate: z.ZodString;
28
+ upgradeUrl: z.ZodString;
29
+ }, z.core.$strip>;
30
+ from: z.ZodEmail;
31
+ to: z.ZodEmail;
32
+ brandId: z.ZodString;
33
+ subject: z.ZodString;
34
+ template: z.ZodLiteral<"trial-ending">;
35
+ }, z.core.$strip>, z.ZodObject<{
36
+ data: z.ZodObject<{
37
+ resetUrl: z.ZodString;
38
+ expiryMinutes: z.ZodNumber;
39
+ }, z.core.$strip>;
40
+ from: z.ZodEmail;
41
+ to: z.ZodEmail;
42
+ brandId: z.ZodString;
43
+ subject: z.ZodString;
44
+ template: z.ZodLiteral<"reset-password">;
45
+ }, z.core.$strip>, z.ZodObject<{
46
+ data: z.ZodObject<{
47
+ device: z.ZodString;
48
+ location: z.ZodString;
49
+ ip: z.ZodString;
50
+ time: z.ZodString;
51
+ }, z.core.$strip>;
52
+ from: z.ZodEmail;
53
+ to: z.ZodEmail;
54
+ brandId: z.ZodString;
55
+ subject: z.ZodString;
56
+ template: z.ZodLiteral<"login-alert">;
57
+ }, z.core.$strip>, z.ZodObject<{
58
+ data: z.ZodObject<{
59
+ otp: z.ZodString;
60
+ expiryMinutes: z.ZodNumber;
61
+ }, z.core.$strip>;
62
+ from: z.ZodEmail;
63
+ to: z.ZodEmail;
64
+ brandId: z.ZodString;
65
+ subject: z.ZodString;
66
+ template: z.ZodLiteral<"email-verification">;
67
+ }, z.core.$strip>], "template">;
68
+ export declare const sendHtmlEmailSchema: z.ZodObject<{
69
+ from: z.ZodEmail;
70
+ to: z.ZodEmail;
71
+ subject: z.ZodString;
72
+ html: z.ZodString;
73
+ }, z.core.$strip>;
74
+ export type SendEmailOptions = z.infer<typeof sendEmailSchema>;
75
+ export type SendHtmlOptions = z.infer<typeof sendHtmlEmailSchema>;
76
+ export interface BrandedMailResponse {
77
+ message: string;
78
+ messageId?: string;
79
+ success: boolean;
80
+ }
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendHtmlEmailSchema = exports.sendEmailSchema = exports.TEMPLATES = void 0;
4
+ const v4_1 = require("zod/v4");
5
+ exports.TEMPLATES = {
6
+ WELCOME: "welcome",
7
+ TRIAL_ENDING: "trial-ending",
8
+ RESET_PASSWORD: "reset-password",
9
+ LOGIN_ALERT: "login-alert",
10
+ EMAIL_VERIFICATION: "email-verification",
11
+ };
12
+ const baseFields = {
13
+ from: v4_1.z.email({ message: "Invalid sender email address" }),
14
+ to: v4_1.z.email({ message: "Invalid recipient email address" }),
15
+ brandId: v4_1.z.string().min(1, "brandId is required"),
16
+ subject: v4_1.z
17
+ .string()
18
+ .min(1, "Subject is required")
19
+ .max(500, "Subject is too long"),
20
+ };
21
+ // --- Template Data Schemas ---
22
+ const welcomeDataSchema = v4_1.z.object({
23
+ firstName: v4_1.z.string().min(1).max(250),
24
+ message: v4_1.z.string().min(1).max(3600),
25
+ cta: v4_1.z.object({
26
+ label: v4_1.z.string().min(1).max(256),
27
+ url: v4_1.z.string().url(),
28
+ }),
29
+ });
30
+ const trialEndingDataSchema = v4_1.z.object({
31
+ daysLeft: v4_1.z.number().int().min(0),
32
+ expiryDate: v4_1.z.string(),
33
+ upgradeUrl: v4_1.z.string().url(),
34
+ });
35
+ const resetPasswordDataSchema = v4_1.z.object({
36
+ resetUrl: v4_1.z.string().url(),
37
+ expiryMinutes: v4_1.z.number().int().positive(),
38
+ });
39
+ const loginAlertDataSchema = v4_1.z.object({
40
+ device: v4_1.z.string(),
41
+ location: v4_1.z.string(),
42
+ ip: v4_1.z.string(),
43
+ time: v4_1.z.string(),
44
+ });
45
+ const emailVerificationDataSchema = v4_1.z.object({
46
+ otp: v4_1.z.string().min(4).max(10),
47
+ expiryMinutes: v4_1.z.number().int().positive(),
48
+ });
49
+ // --- Discriminated Union for Type Safety ---
50
+ exports.sendEmailSchema = v4_1.z.discriminatedUnion("template", [
51
+ v4_1.z.object({
52
+ template: v4_1.z.literal(exports.TEMPLATES.WELCOME),
53
+ ...baseFields,
54
+ data: welcomeDataSchema,
55
+ }),
56
+ v4_1.z.object({
57
+ template: v4_1.z.literal(exports.TEMPLATES.TRIAL_ENDING),
58
+ ...baseFields,
59
+ data: trialEndingDataSchema,
60
+ }),
61
+ v4_1.z.object({
62
+ template: v4_1.z.literal(exports.TEMPLATES.RESET_PASSWORD),
63
+ ...baseFields,
64
+ data: resetPasswordDataSchema,
65
+ }),
66
+ v4_1.z.object({
67
+ template: v4_1.z.literal(exports.TEMPLATES.LOGIN_ALERT),
68
+ ...baseFields,
69
+ data: loginAlertDataSchema,
70
+ }),
71
+ v4_1.z.object({
72
+ template: v4_1.z.literal(exports.TEMPLATES.EMAIL_VERIFICATION),
73
+ ...baseFields,
74
+ data: emailVerificationDataSchema,
75
+ }),
76
+ ]);
77
+ exports.sendHtmlEmailSchema = v4_1.z.object({
78
+ from: baseFields.from,
79
+ to: baseFields.to,
80
+ subject: baseFields.subject,
81
+ html: v4_1.z
82
+ .string()
83
+ .min(1, "HTML content is required")
84
+ .max(1000000, "HTML content exceeds 1MB limit"),
85
+ });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@brandedmail/node",
3
+ "version": "1.0.1",
4
+ "description": "Official Brandemail Node.js SDK",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "test": "echo \"Error: no test specified\" && exit 1",
13
+ "build": "tsc",
14
+ "prepare": "npm run build"
15
+ },
16
+ "keywords": [],
17
+ "author": "monkmodeceo",
18
+ "license": "ISC",
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "^5.9.3"
24
+ },
25
+ "dependencies": {
26
+ "zod": "^4.3.6"
27
+ }
28
+ }