sendhive 0.1.0-beta.0 → 0.1.0-beta.2
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 +19 -0
- package/dist/index.cjs +20 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,25 @@ const { data, error } = await sendhive.emails.send({
|
|
|
67
67
|
|
|
68
68
|
Every variable the template uses must have a value in `data`, otherwise you get `missing_template_variables`. Values are HTML-escaped for you, and a value used as a link must start with `http://`, `https://` or `mailto:`. An unknown template gives `template_not_found`.
|
|
69
69
|
|
|
70
|
+
Ready-made templates work without saving anything: use `preset:` plus the preset id, for example `template: 'preset:welcome'`.
|
|
71
|
+
|
|
72
|
+
**Designer templates** are polished ready-made layouts (sets Barebone, Matte, Protocol, Arcane and Studio: welcome, password reset, order confirmation and more). They are sent the same way, for example `template: 'preset:matte-welcome'`, but they cannot be edited in the visual editor. `templates.list()` marks them with `style: 'designer'` and their `set`; the `variables` field tells you which `data` keys to pass.
|
|
73
|
+
|
|
74
|
+
## Templates
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// Your templates and the ready-made ones (slug 'preset:welcome', 'preset:order-shipped', ...)
|
|
78
|
+
const { data: templates, error } = await sendhive.templates.list()
|
|
79
|
+
// templates[0] -> { id, slug, name, kind: 'custom' | 'preset', subject, preview_text, variables, updated_at,
|
|
80
|
+
// style?: 'editable' | 'designer', set?: 'Barebone' | 'Matte' | 'Protocol' | 'Arcane' | 'Studio' }
|
|
81
|
+
|
|
82
|
+
// Draw a template with data. Missing values are not an error: they show as {{name}} and are listed.
|
|
83
|
+
const { data: preview } = await sendhive.templates.preview('preset:welcome', { customer_name: 'Asha' })
|
|
84
|
+
// preview -> { subject, html, text, missing_variables }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`list()` calls `GET /api/templates` and `preview(template, data?)` calls `POST /api/templates/preview`. Both return `{ data, error }` and use the same key and rate limit as sending.
|
|
88
|
+
|
|
70
89
|
## Get
|
|
71
90
|
|
|
72
91
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -70,8 +70,27 @@ var Emails = class {
|
|
|
70
70
|
return this.client.request("GET", `/api/emails/${encodeURIComponent(id)}`);
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
|
+
var Templates = class {
|
|
74
|
+
constructor(client) {
|
|
75
|
+
this.client = client;
|
|
76
|
+
}
|
|
77
|
+
client;
|
|
78
|
+
/** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */
|
|
79
|
+
async list() {
|
|
80
|
+
const res = await this.client.request("GET", "/api/templates");
|
|
81
|
+
return res.error ? res : { data: res.data.data, error: null };
|
|
82
|
+
}
|
|
83
|
+
/** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */
|
|
84
|
+
async preview(template, data) {
|
|
85
|
+
return this.client.request("POST", "/api/templates/preview", {
|
|
86
|
+
template,
|
|
87
|
+
...data !== void 0 ? { data } : {}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
};
|
|
73
91
|
var Sendhive = class {
|
|
74
92
|
emails;
|
|
93
|
+
templates;
|
|
75
94
|
apiKey;
|
|
76
95
|
baseUrl;
|
|
77
96
|
constructor(apiKey, options = {}) {
|
|
@@ -80,6 +99,7 @@ var Sendhive = class {
|
|
|
80
99
|
if (!baseUrl) throw new Error("Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.");
|
|
81
100
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
82
101
|
this.emails = new Emails(this);
|
|
102
|
+
this.templates = new Templates(this);
|
|
83
103
|
}
|
|
84
104
|
/** @internal */
|
|
85
105
|
async request(method, path, body) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Email,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveOptions,\n} from './types'\n\nexport type {\n Email,\n EmailEvent,\n EmailTag,\n Recipients,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveErrorFix,\n SendhiveOptions,\n TemplateData,\n} from './types'\n\nfunction env(name: string): string | undefined {\n return typeof process !== 'undefined' ? process.env?.[name] : undefined\n}\n\n/** Optional peer: only loaded when the caller passes `react`. */\nasync function renderReact(element: unknown): Promise<string> {\n const specifier = '@react-email/render'\n let mod: { render?: (el: unknown) => string | Promise<string> }\n try {\n mod = await import(/* @vite-ignore */ specifier)\n } catch {\n throw new Error('The `react` option needs the optional peer dependency @react-email/render.')\n }\n if (!mod.render) throw new Error('@react-email/render has no `render` export.')\n return mod.render(element)\n}\n\nclass Emails {\n constructor(private readonly client: Sendhive) {}\n\n async send(options: SendEmailOptions): Promise<Result<SendEmailResponse>> {\n const { replyTo, react, ...rest } = options\n let html = rest.html\n if (react !== undefined) {\n try {\n html = await renderReact(react)\n } catch (err) {\n return {\n data: null,\n error: { statusCode: 422, name: 'validation_error', message: (err as Error).message },\n }\n }\n }\n const body = {\n ...rest,\n ...(html !== undefined ? { html } : {}),\n ...(replyTo !== undefined ? { reply_to: replyTo } : {}),\n }\n return this.client.request<SendEmailResponse>('POST', '/api/emails', body)\n }\n\n async get(id: string): Promise<Result<Email>> {\n return this.client.request<Email>('GET', `/api/emails/${encodeURIComponent(id)}`)\n }\n}\n\nexport class Sendhive {\n readonly emails: Emails\n private readonly apiKey: string | undefined\n private readonly baseUrl: string\n\n constructor(apiKey?: string, options: SendhiveOptions = {}) {\n this.apiKey = apiKey ?? env('SENDHIVE_API_KEY')\n // No default host: an API key must never be sent to a server the caller did not choose.\n const baseUrl = options.baseUrl ?? env('SENDHIVE_BASE_URL')\n if (!baseUrl) throw new Error('Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.')\n this.baseUrl = baseUrl.replace(/\\/+$/, '')\n this.emails = new Emails(this)\n }\n\n /** @internal */\n async request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>> {\n const headers: Record<string, string> = { Accept: 'application/json' }\n if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`\n if (body !== undefined) headers['Content-Type'] = 'application/json'\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n } catch (err) {\n return {\n data: null,\n error: {\n statusCode: 0,\n name: 'network_error',\n message: err instanceof Error ? err.message : String(err),\n },\n }\n }\n\n let json: unknown\n try {\n json = await res.json()\n } catch {\n json = null\n }\n\n if (!res.ok) {\n const e = (json ?? {}) as Partial<SendhiveError>\n return {\n data: null,\n error: {\n statusCode: e.statusCode ?? res.status,\n name: e.name ?? 'application_error',\n message: e.message ?? (res.statusText || 'Unexpected response'),\n ...(e.fix ? { fix: e.fix } : {}),\n },\n }\n }\n return { data: json as T, error: null }\n }\n}\n\nexport default Sendhive\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Email,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveOptions,\n TemplateData,\n TemplateList,\n TemplatePreview,\n TemplateSummary,\n} from './types'\n\nexport type {\n Email,\n EmailEvent,\n EmailTag,\n Recipients,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveErrorFix,\n SendhiveOptions,\n TemplateData,\n TemplateList,\n TemplatePreview,\n TemplateSummary,\n} from './types'\n\nfunction env(name: string): string | undefined {\n return typeof process !== 'undefined' ? process.env?.[name] : undefined\n}\n\n/** Optional peer: only loaded when the caller passes `react`. */\nasync function renderReact(element: unknown): Promise<string> {\n const specifier = '@react-email/render'\n let mod: { render?: (el: unknown) => string | Promise<string> }\n try {\n mod = await import(/* @vite-ignore */ specifier)\n } catch {\n throw new Error('The `react` option needs the optional peer dependency @react-email/render.')\n }\n if (!mod.render) throw new Error('@react-email/render has no `render` export.')\n return mod.render(element)\n}\n\nclass Emails {\n constructor(private readonly client: Sendhive) {}\n\n async send(options: SendEmailOptions): Promise<Result<SendEmailResponse>> {\n const { replyTo, react, ...rest } = options\n let html = rest.html\n if (react !== undefined) {\n try {\n html = await renderReact(react)\n } catch (err) {\n return {\n data: null,\n error: { statusCode: 422, name: 'validation_error', message: (err as Error).message },\n }\n }\n }\n const body = {\n ...rest,\n ...(html !== undefined ? { html } : {}),\n ...(replyTo !== undefined ? { reply_to: replyTo } : {}),\n }\n return this.client.request<SendEmailResponse>('POST', '/api/emails', body)\n }\n\n async get(id: string): Promise<Result<Email>> {\n return this.client.request<Email>('GET', `/api/emails/${encodeURIComponent(id)}`)\n }\n}\n\nclass Templates {\n constructor(private readonly client: Sendhive) {}\n\n /** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */\n async list(): Promise<Result<TemplateSummary[]>> {\n const res = await this.client.request<TemplateList>('GET', '/api/templates')\n return res.error ? res : { data: res.data.data, error: null }\n }\n\n /** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */\n async preview(template: string, data?: TemplateData): Promise<Result<TemplatePreview>> {\n return this.client.request<TemplatePreview>('POST', '/api/templates/preview', {\n template,\n ...(data !== undefined ? { data } : {}),\n })\n }\n}\n\nexport class Sendhive {\n readonly emails: Emails\n readonly templates: Templates\n private readonly apiKey: string | undefined\n private readonly baseUrl: string\n\n constructor(apiKey?: string, options: SendhiveOptions = {}) {\n this.apiKey = apiKey ?? env('SENDHIVE_API_KEY')\n // No default host: an API key must never be sent to a server the caller did not choose.\n const baseUrl = options.baseUrl ?? env('SENDHIVE_BASE_URL')\n if (!baseUrl) throw new Error('Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.')\n this.baseUrl = baseUrl.replace(/\\/+$/, '')\n this.emails = new Emails(this)\n this.templates = new Templates(this)\n }\n\n /** @internal */\n async request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>> {\n const headers: Record<string, string> = { Accept: 'application/json' }\n if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`\n if (body !== undefined) headers['Content-Type'] = 'application/json'\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n } catch (err) {\n return {\n data: null,\n error: {\n statusCode: 0,\n name: 'network_error',\n message: err instanceof Error ? err.message : String(err),\n },\n }\n }\n\n let json: unknown\n try {\n json = await res.json()\n } catch {\n json = null\n }\n\n if (!res.ok) {\n const e = (json ?? {}) as Partial<SendhiveError>\n return {\n data: null,\n error: {\n statusCode: e.statusCode ?? res.status,\n name: e.name ?? 'application_error',\n message: e.message ?? (res.statusText || 'Unexpected response'),\n ...(e.fix ? { fix: e.fix } : {}),\n },\n }\n }\n return { data: json as T, error: null }\n }\n}\n\nexport default Sendhive\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BA,SAAS,IAAI,MAAkC;AAC7C,SAAO,OAAO,YAAY,cAAc,QAAQ,MAAM,IAAI,IAAI;AAChE;AAGA,eAAe,YAAY,SAAmC;AAC5D,QAAM,YAAY;AAClB,MAAI;AACJ,MAAI;AACF,UAAM,MAAM;AAAA;AAAA,MAA0B;AAAA;AAAA,EACxC,QAAQ;AACN,UAAM,IAAI,MAAM,4EAA4E;AAAA,EAC9F;AACA,MAAI,CAAC,IAAI,OAAQ,OAAM,IAAI,MAAM,6CAA6C;AAC9E,SAAO,IAAI,OAAO,OAAO;AAC3B;AAEA,IAAM,SAAN,MAAa;AAAA,EACX,YAA6B,QAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA,EAE7B,MAAM,KAAK,SAA+D;AACxE,UAAM,EAAE,SAAS,OAAO,GAAG,KAAK,IAAI;AACpC,QAAI,OAAO,KAAK;AAChB,QAAI,UAAU,QAAW;AACvB,UAAI;AACF,eAAO,MAAM,YAAY,KAAK;AAAA,MAChC,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,MAAM;AAAA,UACN,OAAO,EAAE,YAAY,KAAK,MAAM,oBAAoB,SAAU,IAAc,QAAQ;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX,GAAG;AAAA,MACH,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,MACrC,GAAI,YAAY,SAAY,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,IACvD;AACA,WAAO,KAAK,OAAO,QAA2B,QAAQ,eAAe,IAAI;AAAA,EAC3E;AAAA,EAEA,MAAM,IAAI,IAAoC;AAC5C,WAAO,KAAK,OAAO,QAAe,OAAO,eAAe,mBAAmB,EAAE,CAAC,EAAE;AAAA,EAClF;AACF;AAEA,IAAM,YAAN,MAAgB;AAAA,EACd,YAA6B,QAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,MAAM,OAA2C;AAC/C,UAAM,MAAM,MAAM,KAAK,OAAO,QAAsB,OAAO,gBAAgB;AAC3E,WAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI,KAAK,MAAM,OAAO,KAAK;AAAA,EAC9D;AAAA;AAAA,EAGA,MAAM,QAAQ,UAAkB,MAAuD;AACrF,WAAO,KAAK,OAAO,QAAyB,QAAQ,0BAA0B;AAAA,MAC5E;AAAA,MACA,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACvC,CAAC;AAAA,EACH;AACF;AAEO,IAAM,WAAN,MAAe;AAAA,EACX;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAiB,UAA2B,CAAC,GAAG;AAC1D,SAAK,SAAS,UAAU,IAAI,kBAAkB;AAE9C,UAAM,UAAU,QAAQ,WAAW,IAAI,mBAAmB;AAC1D,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,uEAAuE;AACrG,SAAK,UAAU,QAAQ,QAAQ,QAAQ,EAAE;AACzC,SAAK,SAAS,IAAI,OAAO,IAAI;AAC7B,SAAK,YAAY,IAAI,UAAU,IAAI;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,QAAW,QAAwB,MAAc,MAAoC;AACzF,UAAM,UAAkC,EAAE,QAAQ,mBAAmB;AACrE,QAAI,KAAK,OAAQ,SAAQ,gBAAgB,UAAU,KAAK,MAAM;AAC9D,QAAI,SAAS,OAAW,SAAQ,cAAc,IAAI;AAElD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,QAC1C;AAAA,QACA;AAAA,QACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,MAC5D,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,YAAY;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC1D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAK,QAAQ,CAAC;AACpB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,YAAY,EAAE,cAAc,IAAI;AAAA,UAChC,MAAM,EAAE,QAAQ;AAAA,UAChB,SAAS,EAAE,YAAY,IAAI,cAAc;AAAA,UACzC,GAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,MAAM,MAAW,OAAO,KAAK;AAAA,EACxC;AACF;AAEA,IAAO,gBAAQ;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -84,6 +84,34 @@ interface Email {
|
|
|
84
84
|
delivered_at: string | null;
|
|
85
85
|
events: EmailEvent[];
|
|
86
86
|
}
|
|
87
|
+
/** One template you can send: one of your own, or a ready-made preset (`slug` is then `preset:<id>`). */
|
|
88
|
+
interface TemplateSummary {
|
|
89
|
+
id: string;
|
|
90
|
+
/** Pass this as `template` when sending. */
|
|
91
|
+
slug: string;
|
|
92
|
+
name: string;
|
|
93
|
+
kind: 'custom' | 'preset';
|
|
94
|
+
subject: string;
|
|
95
|
+
preview_text: string | null;
|
|
96
|
+
/** The `{{variables}}` the template needs in `data`. */
|
|
97
|
+
variables: string[];
|
|
98
|
+
/** Null for ready-made presets. */
|
|
99
|
+
updated_at: string | null;
|
|
100
|
+
/** `editable` templates open in the visual editor; `designer` ones are ready-made layouts that are only sent. */
|
|
101
|
+
style?: 'editable' | 'designer';
|
|
102
|
+
/** The designer set a `designer` template belongs to. */
|
|
103
|
+
set?: 'Barebone' | 'Matte' | 'Protocol' | 'Arcane' | 'Studio';
|
|
104
|
+
}
|
|
105
|
+
interface TemplateList {
|
|
106
|
+
data: TemplateSummary[];
|
|
107
|
+
}
|
|
108
|
+
/** A template drawn with the data you gave. Values you left out show as `{{name}}` and are listed. */
|
|
109
|
+
interface TemplatePreview {
|
|
110
|
+
subject: string;
|
|
111
|
+
html: string;
|
|
112
|
+
text: string;
|
|
113
|
+
missing_variables: string[];
|
|
114
|
+
}
|
|
87
115
|
type Result<T> = {
|
|
88
116
|
data: T;
|
|
89
117
|
error: null;
|
|
@@ -101,8 +129,17 @@ declare class Emails {
|
|
|
101
129
|
send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
|
|
102
130
|
get(id: string): Promise<Result<Email>>;
|
|
103
131
|
}
|
|
132
|
+
declare class Templates {
|
|
133
|
+
private readonly client;
|
|
134
|
+
constructor(client: Sendhive);
|
|
135
|
+
/** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */
|
|
136
|
+
list(): Promise<Result<TemplateSummary[]>>;
|
|
137
|
+
/** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */
|
|
138
|
+
preview(template: string, data?: TemplateData): Promise<Result<TemplatePreview>>;
|
|
139
|
+
}
|
|
104
140
|
declare class Sendhive {
|
|
105
141
|
readonly emails: Emails;
|
|
142
|
+
readonly templates: Templates;
|
|
106
143
|
private readonly apiKey;
|
|
107
144
|
private readonly baseUrl;
|
|
108
145
|
constructor(apiKey?: string, options?: SendhiveOptions);
|
|
@@ -110,4 +147,4 @@ declare class Sendhive {
|
|
|
110
147
|
request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>>;
|
|
111
148
|
}
|
|
112
149
|
|
|
113
|
-
export { type Email, type EmailEvent, type EmailTag, type Recipients, type Result, type SendEmailOptions, type SendEmailResponse, Sendhive, type SendhiveError, type SendhiveErrorFix, type SendhiveOptions, type TemplateData, Sendhive as default };
|
|
150
|
+
export { type Email, type EmailEvent, type EmailTag, type Recipients, type Result, type SendEmailOptions, type SendEmailResponse, Sendhive, type SendhiveError, type SendhiveErrorFix, type SendhiveOptions, type TemplateData, type TemplateList, type TemplatePreview, type TemplateSummary, Sendhive as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -84,6 +84,34 @@ interface Email {
|
|
|
84
84
|
delivered_at: string | null;
|
|
85
85
|
events: EmailEvent[];
|
|
86
86
|
}
|
|
87
|
+
/** One template you can send: one of your own, or a ready-made preset (`slug` is then `preset:<id>`). */
|
|
88
|
+
interface TemplateSummary {
|
|
89
|
+
id: string;
|
|
90
|
+
/** Pass this as `template` when sending. */
|
|
91
|
+
slug: string;
|
|
92
|
+
name: string;
|
|
93
|
+
kind: 'custom' | 'preset';
|
|
94
|
+
subject: string;
|
|
95
|
+
preview_text: string | null;
|
|
96
|
+
/** The `{{variables}}` the template needs in `data`. */
|
|
97
|
+
variables: string[];
|
|
98
|
+
/** Null for ready-made presets. */
|
|
99
|
+
updated_at: string | null;
|
|
100
|
+
/** `editable` templates open in the visual editor; `designer` ones are ready-made layouts that are only sent. */
|
|
101
|
+
style?: 'editable' | 'designer';
|
|
102
|
+
/** The designer set a `designer` template belongs to. */
|
|
103
|
+
set?: 'Barebone' | 'Matte' | 'Protocol' | 'Arcane' | 'Studio';
|
|
104
|
+
}
|
|
105
|
+
interface TemplateList {
|
|
106
|
+
data: TemplateSummary[];
|
|
107
|
+
}
|
|
108
|
+
/** A template drawn with the data you gave. Values you left out show as `{{name}}` and are listed. */
|
|
109
|
+
interface TemplatePreview {
|
|
110
|
+
subject: string;
|
|
111
|
+
html: string;
|
|
112
|
+
text: string;
|
|
113
|
+
missing_variables: string[];
|
|
114
|
+
}
|
|
87
115
|
type Result<T> = {
|
|
88
116
|
data: T;
|
|
89
117
|
error: null;
|
|
@@ -101,8 +129,17 @@ declare class Emails {
|
|
|
101
129
|
send(options: SendEmailOptions): Promise<Result<SendEmailResponse>>;
|
|
102
130
|
get(id: string): Promise<Result<Email>>;
|
|
103
131
|
}
|
|
132
|
+
declare class Templates {
|
|
133
|
+
private readonly client;
|
|
134
|
+
constructor(client: Sendhive);
|
|
135
|
+
/** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */
|
|
136
|
+
list(): Promise<Result<TemplateSummary[]>>;
|
|
137
|
+
/** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */
|
|
138
|
+
preview(template: string, data?: TemplateData): Promise<Result<TemplatePreview>>;
|
|
139
|
+
}
|
|
104
140
|
declare class Sendhive {
|
|
105
141
|
readonly emails: Emails;
|
|
142
|
+
readonly templates: Templates;
|
|
106
143
|
private readonly apiKey;
|
|
107
144
|
private readonly baseUrl;
|
|
108
145
|
constructor(apiKey?: string, options?: SendhiveOptions);
|
|
@@ -110,4 +147,4 @@ declare class Sendhive {
|
|
|
110
147
|
request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>>;
|
|
111
148
|
}
|
|
112
149
|
|
|
113
|
-
export { type Email, type EmailEvent, type EmailTag, type Recipients, type Result, type SendEmailOptions, type SendEmailResponse, Sendhive, type SendhiveError, type SendhiveErrorFix, type SendhiveOptions, type TemplateData, Sendhive as default };
|
|
150
|
+
export { type Email, type EmailEvent, type EmailTag, type Recipients, type Result, type SendEmailOptions, type SendEmailResponse, Sendhive, type SendhiveError, type SendhiveErrorFix, type SendhiveOptions, type TemplateData, type TemplateList, type TemplatePreview, type TemplateSummary, Sendhive as default };
|
package/dist/index.js
CHANGED
|
@@ -45,8 +45,27 @@ var Emails = class {
|
|
|
45
45
|
return this.client.request("GET", `/api/emails/${encodeURIComponent(id)}`);
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
+
var Templates = class {
|
|
49
|
+
constructor(client) {
|
|
50
|
+
this.client = client;
|
|
51
|
+
}
|
|
52
|
+
client;
|
|
53
|
+
/** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */
|
|
54
|
+
async list() {
|
|
55
|
+
const res = await this.client.request("GET", "/api/templates");
|
|
56
|
+
return res.error ? res : { data: res.data.data, error: null };
|
|
57
|
+
}
|
|
58
|
+
/** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */
|
|
59
|
+
async preview(template, data) {
|
|
60
|
+
return this.client.request("POST", "/api/templates/preview", {
|
|
61
|
+
template,
|
|
62
|
+
...data !== void 0 ? { data } : {}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
48
66
|
var Sendhive = class {
|
|
49
67
|
emails;
|
|
68
|
+
templates;
|
|
50
69
|
apiKey;
|
|
51
70
|
baseUrl;
|
|
52
71
|
constructor(apiKey, options = {}) {
|
|
@@ -55,6 +74,7 @@ var Sendhive = class {
|
|
|
55
74
|
if (!baseUrl) throw new Error("Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.");
|
|
56
75
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
57
76
|
this.emails = new Emails(this);
|
|
77
|
+
this.templates = new Templates(this);
|
|
58
78
|
}
|
|
59
79
|
/** @internal */
|
|
60
80
|
async request(method, path, body) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Email,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveOptions,\n} from './types'\n\nexport type {\n Email,\n EmailEvent,\n EmailTag,\n Recipients,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveErrorFix,\n SendhiveOptions,\n TemplateData,\n} from './types'\n\nfunction env(name: string): string | undefined {\n return typeof process !== 'undefined' ? process.env?.[name] : undefined\n}\n\n/** Optional peer: only loaded when the caller passes `react`. */\nasync function renderReact(element: unknown): Promise<string> {\n const specifier = '@react-email/render'\n let mod: { render?: (el: unknown) => string | Promise<string> }\n try {\n mod = await import(/* @vite-ignore */ specifier)\n } catch {\n throw new Error('The `react` option needs the optional peer dependency @react-email/render.')\n }\n if (!mod.render) throw new Error('@react-email/render has no `render` export.')\n return mod.render(element)\n}\n\nclass Emails {\n constructor(private readonly client: Sendhive) {}\n\n async send(options: SendEmailOptions): Promise<Result<SendEmailResponse>> {\n const { replyTo, react, ...rest } = options\n let html = rest.html\n if (react !== undefined) {\n try {\n html = await renderReact(react)\n } catch (err) {\n return {\n data: null,\n error: { statusCode: 422, name: 'validation_error', message: (err as Error).message },\n }\n }\n }\n const body = {\n ...rest,\n ...(html !== undefined ? { html } : {}),\n ...(replyTo !== undefined ? { reply_to: replyTo } : {}),\n }\n return this.client.request<SendEmailResponse>('POST', '/api/emails', body)\n }\n\n async get(id: string): Promise<Result<Email>> {\n return this.client.request<Email>('GET', `/api/emails/${encodeURIComponent(id)}`)\n }\n}\n\nexport class Sendhive {\n readonly emails: Emails\n private readonly apiKey: string | undefined\n private readonly baseUrl: string\n\n constructor(apiKey?: string, options: SendhiveOptions = {}) {\n this.apiKey = apiKey ?? env('SENDHIVE_API_KEY')\n // No default host: an API key must never be sent to a server the caller did not choose.\n const baseUrl = options.baseUrl ?? env('SENDHIVE_BASE_URL')\n if (!baseUrl) throw new Error('Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.')\n this.baseUrl = baseUrl.replace(/\\/+$/, '')\n this.emails = new Emails(this)\n }\n\n /** @internal */\n async request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>> {\n const headers: Record<string, string> = { Accept: 'application/json' }\n if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`\n if (body !== undefined) headers['Content-Type'] = 'application/json'\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n } catch (err) {\n return {\n data: null,\n error: {\n statusCode: 0,\n name: 'network_error',\n message: err instanceof Error ? err.message : String(err),\n },\n }\n }\n\n let json: unknown\n try {\n json = await res.json()\n } catch {\n json = null\n }\n\n if (!res.ok) {\n const e = (json ?? {}) as Partial<SendhiveError>\n return {\n data: null,\n error: {\n statusCode: e.statusCode ?? res.status,\n name: e.name ?? 'application_error',\n message: e.message ?? (res.statusText || 'Unexpected response'),\n ...(e.fix ? { fix: e.fix } : {}),\n },\n }\n }\n return { data: json as T, error: null }\n }\n}\n\nexport default Sendhive\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n Email,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveOptions,\n TemplateData,\n TemplateList,\n TemplatePreview,\n TemplateSummary,\n} from './types'\n\nexport type {\n Email,\n EmailEvent,\n EmailTag,\n Recipients,\n Result,\n SendEmailOptions,\n SendEmailResponse,\n SendhiveError,\n SendhiveErrorFix,\n SendhiveOptions,\n TemplateData,\n TemplateList,\n TemplatePreview,\n TemplateSummary,\n} from './types'\n\nfunction env(name: string): string | undefined {\n return typeof process !== 'undefined' ? process.env?.[name] : undefined\n}\n\n/** Optional peer: only loaded when the caller passes `react`. */\nasync function renderReact(element: unknown): Promise<string> {\n const specifier = '@react-email/render'\n let mod: { render?: (el: unknown) => string | Promise<string> }\n try {\n mod = await import(/* @vite-ignore */ specifier)\n } catch {\n throw new Error('The `react` option needs the optional peer dependency @react-email/render.')\n }\n if (!mod.render) throw new Error('@react-email/render has no `render` export.')\n return mod.render(element)\n}\n\nclass Emails {\n constructor(private readonly client: Sendhive) {}\n\n async send(options: SendEmailOptions): Promise<Result<SendEmailResponse>> {\n const { replyTo, react, ...rest } = options\n let html = rest.html\n if (react !== undefined) {\n try {\n html = await renderReact(react)\n } catch (err) {\n return {\n data: null,\n error: { statusCode: 422, name: 'validation_error', message: (err as Error).message },\n }\n }\n }\n const body = {\n ...rest,\n ...(html !== undefined ? { html } : {}),\n ...(replyTo !== undefined ? { reply_to: replyTo } : {}),\n }\n return this.client.request<SendEmailResponse>('POST', '/api/emails', body)\n }\n\n async get(id: string): Promise<Result<Email>> {\n return this.client.request<Email>('GET', `/api/emails/${encodeURIComponent(id)}`)\n }\n}\n\nclass Templates {\n constructor(private readonly client: Sendhive) {}\n\n /** Your templates plus the ready-made ones (their `slug` starts with `preset:`). */\n async list(): Promise<Result<TemplateSummary[]>> {\n const res = await this.client.request<TemplateList>('GET', '/api/templates')\n return res.error ? res : { data: res.data.data, error: null }\n }\n\n /** Draws a template (slug, id or `preset:<id>`) with `data`; values you leave out show as `{{name}}`. */\n async preview(template: string, data?: TemplateData): Promise<Result<TemplatePreview>> {\n return this.client.request<TemplatePreview>('POST', '/api/templates/preview', {\n template,\n ...(data !== undefined ? { data } : {}),\n })\n }\n}\n\nexport class Sendhive {\n readonly emails: Emails\n readonly templates: Templates\n private readonly apiKey: string | undefined\n private readonly baseUrl: string\n\n constructor(apiKey?: string, options: SendhiveOptions = {}) {\n this.apiKey = apiKey ?? env('SENDHIVE_API_KEY')\n // No default host: an API key must never be sent to a server the caller did not choose.\n const baseUrl = options.baseUrl ?? env('SENDHIVE_BASE_URL')\n if (!baseUrl) throw new Error('Sendhive: set `baseUrl` or SENDHIVE_BASE_URL to your Sendhive server.')\n this.baseUrl = baseUrl.replace(/\\/+$/, '')\n this.emails = new Emails(this)\n this.templates = new Templates(this)\n }\n\n /** @internal */\n async request<T>(method: 'GET' | 'POST', path: string, body?: unknown): Promise<Result<T>> {\n const headers: Record<string, string> = { Accept: 'application/json' }\n if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`\n if (body !== undefined) headers['Content-Type'] = 'application/json'\n\n let res: Response\n try {\n res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n })\n } catch (err) {\n return {\n data: null,\n error: {\n statusCode: 0,\n name: 'network_error',\n message: err instanceof Error ? err.message : String(err),\n },\n }\n }\n\n let json: unknown\n try {\n json = await res.json()\n } catch {\n json = null\n }\n\n if (!res.ok) {\n const e = (json ?? {}) as Partial<SendhiveError>\n return {\n data: null,\n error: {\n statusCode: e.statusCode ?? res.status,\n name: e.name ?? 'application_error',\n message: e.message ?? (res.statusText || 'Unexpected response'),\n ...(e.fix ? { fix: e.fix } : {}),\n },\n }\n }\n return { data: json as T, error: null }\n }\n}\n\nexport default Sendhive\n"],"mappings":";AA8BA,SAAS,IAAI,MAAkC;AAC7C,SAAO,OAAO,YAAY,cAAc,QAAQ,MAAM,IAAI,IAAI;AAChE;AAGA,eAAe,YAAY,SAAmC;AAC5D,QAAM,YAAY;AAClB,MAAI;AACJ,MAAI;AACF,UAAM,MAAM;AAAA;AAAA,MAA0B;AAAA;AAAA,EACxC,QAAQ;AACN,UAAM,IAAI,MAAM,4EAA4E;AAAA,EAC9F;AACA,MAAI,CAAC,IAAI,OAAQ,OAAM,IAAI,MAAM,6CAA6C;AAC9E,SAAO,IAAI,OAAO,OAAO;AAC3B;AAEA,IAAM,SAAN,MAAa;AAAA,EACX,YAA6B,QAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA,EAE7B,MAAM,KAAK,SAA+D;AACxE,UAAM,EAAE,SAAS,OAAO,GAAG,KAAK,IAAI;AACpC,QAAI,OAAO,KAAK;AAChB,QAAI,UAAU,QAAW;AACvB,UAAI;AACF,eAAO,MAAM,YAAY,KAAK;AAAA,MAChC,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,MAAM;AAAA,UACN,OAAO,EAAE,YAAY,KAAK,MAAM,oBAAoB,SAAU,IAAc,QAAQ;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO;AAAA,MACX,GAAG;AAAA,MACH,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,MACrC,GAAI,YAAY,SAAY,EAAE,UAAU,QAAQ,IAAI,CAAC;AAAA,IACvD;AACA,WAAO,KAAK,OAAO,QAA2B,QAAQ,eAAe,IAAI;AAAA,EAC3E;AAAA,EAEA,MAAM,IAAI,IAAoC;AAC5C,WAAO,KAAK,OAAO,QAAe,OAAO,eAAe,mBAAmB,EAAE,CAAC,EAAE;AAAA,EAClF;AACF;AAEA,IAAM,YAAN,MAAgB;AAAA,EACd,YAA6B,QAAkB;AAAlB;AAAA,EAAmB;AAAA,EAAnB;AAAA;AAAA,EAG7B,MAAM,OAA2C;AAC/C,UAAM,MAAM,MAAM,KAAK,OAAO,QAAsB,OAAO,gBAAgB;AAC3E,WAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,IAAI,KAAK,MAAM,OAAO,KAAK;AAAA,EAC9D;AAAA;AAAA,EAGA,MAAM,QAAQ,UAAkB,MAAuD;AACrF,WAAO,KAAK,OAAO,QAAyB,QAAQ,0BAA0B;AAAA,MAC5E;AAAA,MACA,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACvC,CAAC;AAAA,EACH;AACF;AAEO,IAAM,WAAN,MAAe;AAAA,EACX;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAiB,UAA2B,CAAC,GAAG;AAC1D,SAAK,SAAS,UAAU,IAAI,kBAAkB;AAE9C,UAAM,UAAU,QAAQ,WAAW,IAAI,mBAAmB;AAC1D,QAAI,CAAC,QAAS,OAAM,IAAI,MAAM,uEAAuE;AACrG,SAAK,UAAU,QAAQ,QAAQ,QAAQ,EAAE;AACzC,SAAK,SAAS,IAAI,OAAO,IAAI;AAC7B,SAAK,YAAY,IAAI,UAAU,IAAI;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,QAAW,QAAwB,MAAc,MAAoC;AACzF,UAAM,UAAkC,EAAE,QAAQ,mBAAmB;AACrE,QAAI,KAAK,OAAQ,SAAQ,gBAAgB,UAAU,KAAK,MAAM;AAC9D,QAAI,SAAS,OAAW,SAAQ,cAAc,IAAI;AAElD,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,QAC1C;AAAA,QACA;AAAA,QACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,MAC5D,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,YAAY;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC1D;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAK,QAAQ,CAAC;AACpB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,UACL,YAAY,EAAE,cAAc,IAAI;AAAA,UAChC,MAAM,EAAE,QAAQ;AAAA,UAChB,SAAS,EAAE,YAAY,IAAI,cAAc;AAAA,UACzC,GAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,MAAM,MAAW,OAAO,KAAK;AAAA,EACxC;AACF;AAEA,IAAO,gBAAQ;","names":[]}
|