esender-email-editor 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bitbeast Private Limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # antd-email-editor-bitbeast
2
+
3
+ A drag-and-drop **MJML email editor** for React 18 + Ant Design 5. The editor renders inside a single `<Package />` component, exposes a small imperative API for getting / loading / saving templates, and gates access behind the **eSender license** system.
4
+
5
+ > **Scope:** this package ships the UI, MJML pipeline, export logic, template save/load API client, and license handshake. Premium / business logic (template storage backend, AI integrations, billing) lives on the eSender backend.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ The package is published on the public **npm registry**.
12
+
13
+ ```bash
14
+ npm install antd-email-editor-bitbeast --legacy-peer-deps
15
+ ```
16
+
17
+ > `--legacy-peer-deps` is required because a transitive dep (`react-giphy-searchbox`) still declares a `react@16` peer range. The runtime works on React 18 — only the peer declaration is stale.
18
+
19
+ ## Peer dependencies
20
+
21
+ The package externalizes its heavy runtime deps so the host app provides them. Install the tested versions:
22
+
23
+ ```bash
24
+ npm install --legacy-peer-deps \
25
+ react@^18.3.1 react-dom@^18.3.1 \
26
+ antd@^5.25.1 @ant-design/icons@^5.6.1 @ant-design/pro-components@^2.8.7 \
27
+ @reduxjs/toolkit@^2.1.0 react-redux@^9.2.0 redux-persist@^6.0.0 \
28
+ mjml-browser@^4.15.3 cheerio@1.0.0-rc.12 \
29
+ lodash@^4.17.21 axios@^1.9.0 swr@^2.3.3 \
30
+ framer-motion@^12.12.1 html2canvas@^1.4.1 \
31
+ styled-components@^6.1.18 use-sync-external-store@^1.5.0
32
+ ```
33
+
34
+ ### The non-negotiables
35
+
36
+ | Constraint | Why |
37
+ |---|---|
38
+ | **React must be 18.x** (not 19) | antd v5 only officially supports React 16–18. Under React 19 you will see `[antd: compatible]` warnings and cascading hook bugs in dropdown / tooltip / message. |
39
+ | **Single React copy** in `node_modules` | Two copies of React → `Invalid hook call`. Run `npm ls react` — it must show exactly one version. For Vite hosts use `resolve.dedupe`. |
40
+ | **`cheerio@1.0.0-rc.12` must be installable** | `mjml-browser`'s UMD wrapper `require`s `cheerio`. Without it, `getHtml()` returns non-string and the canvas renders `errors: please check dev console`. Newer cheerio 1.x is ESM-only and breaks Vite's optimizer. |
41
+ | **`@ant-design/icons` stays on v5** | `@ant-design/pro-components` resolves icons v5. Installing v6 forces `--legacy-peer-deps` everywhere and can cause runtime mismatches. |
42
+
43
+ ## Quick start
44
+
45
+ ```tsx
46
+ import { useRef } from 'react';
47
+ import Package, { type EditorHandle, type LicenseError } from 'antd-email-editor-bitbeast';
48
+ import 'antd-email-editor-bitbeast/styles.css';
49
+
50
+ export const Composer = () => {
51
+ const ref = useRef<EditorHandle>(null);
52
+ const projectId = 'proj_123';
53
+
54
+ const save = async () => {
55
+ const res = await ref.current?.saveTemplate({ projectId });
56
+ if (!res?.status) console.error(res?.message);
57
+ };
58
+
59
+ const load = async () => {
60
+ await ref.current?.loadTemplate({ projectId }); // omit templateId → latest for project
61
+ };
62
+
63
+ return (
64
+ <>
65
+ <button onClick={() => console.log(ref.current?.getHtml())}>Export HTML</button>
66
+ <button onClick={() => console.log(ref.current?.getJson())}>Export JSON</button>
67
+ <button onClick={save}>Save to API</button>
68
+ <button onClick={load}>Load from API</button>
69
+
70
+ <Package
71
+ ref={ref}
72
+ apiKey={process.env.NEXT_PUBLIC_ESENDER_KEY!}
73
+ onLicenseError={(err: LicenseError) => console.error(err)}
74
+ showUndoRedo
75
+ />
76
+ </>
77
+ );
78
+ };
79
+ ```
80
+
81
+ `Package` is the default export. Named import (`{ Package }`) also works.
82
+
83
+ ## Public API
84
+
85
+ ### `<Package />` props
86
+
87
+ | Prop | Type | Required | Description |
88
+ |---|---|---|---|
89
+ | `apiKey` | `string` | yes | eSender license key. Exchanged for a JWT on mount. |
90
+ | `customButtons` | `ReactNode` | no | Slot rendered in the editor's top-right toolbar. |
91
+ | `showUndoRedo` | `boolean` | no | Show the undo/redo toolbar (default `true`). |
92
+ | `onLicenseError` | `(error: LicenseError) => void` | no | Fires when the license verify call fails. |
93
+ | `projectId` | `string` | no | **Deprecated.** Ignored at runtime. Kept only for migration. |
94
+
95
+ ### `EditorHandle` (`ref.current`)
96
+
97
+ | Method | Returns | Description |
98
+ |---|---|---|
99
+ | `getJson()` | `string` | Stringified MJML JSON tree (export-ready). |
100
+ | `getHtml()` | `string` | Compiled email HTML via `mjml-browser`. |
101
+ | `loadJson(json, raw?)` | `void` | Load a previously saved template. `json` is the same string `getJson()` produced. Pass `null` to clear. |
102
+ | `saveTemplate({ projectId })` | `Promise<TemplateApiResponse>` | POSTs the current HTML + JSON to `/license/template/create`. |
103
+ | `loadTemplate({ projectId, templateId? })` | `Promise<TemplateApiResponse>` | GETs `/license/template/:projectId[/:templateId]` and pipes the JSON back into `loadJson(...)`. |
104
+
105
+ ## License handshake
106
+
107
+ The editor will not render until the eSender license is verified:
108
+
109
+ 1. Host passes `apiKey` to `<Package />`.
110
+ 2. On mount, the package checks `localStorage` for a cached access token.
111
+ 3. If absent, it `POST`s to `https://esender.in/api/license/verify` with `x-api-key: <apiKey>`.
112
+ 4. On success the returned `accessToken` and `refreshToken` are stored in `localStorage` and the editor mounts.
113
+ 5. While running, the internal axios client attaches the access token to protected calls; on `401` / `token expired` it calls `/license/refresh` once and retries. If refresh fails, tokens are cleared and the next mount re-runs verify.
114
+
115
+ No further token management is required from the host.
116
+
117
+ ## Styling
118
+
119
+ Import the bundled stylesheet once at your app root:
120
+
121
+ ```ts
122
+ import 'antd-email-editor-bitbeast/styles.css';
123
+ ```
124
+
125
+ The package depends on Ant Design 5. Antd v5's runtime CSS-in-JS handles its own component styles, so you do **not** need a separate antd reset. The bundled `styles.css` only contains editor chrome overrides (CKEditor 5, Quill, antd tweaks).
126
+
127
+ ## SSR / Next.js / Remix
128
+
129
+ `Package` touches the DOM, drag-and-drop, and `localStorage`, so it must render on the client only. Use dynamic import:
130
+
131
+ ```tsx
132
+ // Next.js (app/ or pages/)
133
+ import dynamic from 'next/dynamic';
134
+
135
+ const Package = dynamic(() => import('antd-email-editor-bitbeast'), { ssr: false });
136
+ ```
137
+
138
+ Module-level code in the package is SSR-safe (all `window` / `document` / `localStorage` access is `typeof`-guarded), so importing it on the server will not crash — only mounting it will.
139
+
140
+ ## Routing
141
+
142
+ `Package` mounts its own `MemoryRouter` internally so it can coexist with a host app's `BrowserRouter` (Next.js, react-router, Remix, etc.). The internal routes never affect the host URL. Do **not** wrap `<Package />` in a second router — and do not need to.
143
+
144
+ ## Vite consumer config
145
+
146
+ ```ts
147
+ // vite.config.ts
148
+ import { defineConfig } from 'vite';
149
+ import react from '@vitejs/plugin-react';
150
+
151
+ export default defineConfig({
152
+ plugins: [react()],
153
+ resolve: {
154
+ dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
155
+ },
156
+ optimizeDeps: {
157
+ include: [
158
+ 'antd-email-editor-bitbeast',
159
+ 'mjml-browser', 'cheerio',
160
+ 'antd', '@ant-design/icons', '@ant-design/pro-components',
161
+ 'react-is', 'react-redux',
162
+ 'use-sync-external-store',
163
+ 'use-sync-external-store/shim',
164
+ 'use-sync-external-store/shim/with-selector',
165
+ 'use-sync-external-store/with-selector',
166
+ '@reduxjs/toolkit', 'axios', 'framer-motion', 'html2canvas',
167
+ 'lodash', 'redux-persist', 'styled-components', 'swr', 'classnames',
168
+ ],
169
+ },
170
+ });
171
+ ```
172
+
173
+ After changing dep versions or reinstalling the package, restart with `vite --force` so the optimizer rebuilds.
174
+
175
+ ## Bundle
176
+
177
+ - Single ESM + CJS bundle (`dist/index.js`, `dist/index.cjs`), tree-shakeable.
178
+ - Type declarations at `dist/index.d.ts`.
179
+ - CSS extracted to `dist/styles.css` (not auto-injected — import it once).
180
+ - All peer deps (React, antd, mjml-browser, lodash, redux, etc.) are externalized.
181
+ - `console.*` and `debugger` are stripped from the production bundle.
182
+ - The bundle is minified by terser and obfuscated twice (rollup-time + post-build). Debug from source.
183
+
184
+ ## TypeScript surface
185
+
186
+ ```ts
187
+ import Package, {
188
+ // value (also available as default)
189
+ // Package,
190
+ // types
191
+ type EditorHandle,
192
+ type PackageProps,
193
+ type SaveTemplateOptions,
194
+ type LoadTemplateOptions,
195
+ type LicenseError,
196
+ type LicenseErrorCode,
197
+ type VerifyLicenseResponse,
198
+ type RefreshTokenResponse,
199
+ type TemplateApiResponse,
200
+ type TemplateDocument,
201
+ } from 'antd-email-editor-bitbeast';
202
+ ```
203
+
204
+ ## Troubleshooting
205
+
206
+ | Symptom | Cause / fix |
207
+ |---|---|
208
+ | `Invalid hook call` | Two copies of React are loaded. Run `npm ls react` — must show one version. Vite: add `resolve.dedupe`. |
209
+ | Canvas shows `errors: please check dev console` | `cheerio` is not resolvable in the host. Install `cheerio@1.0.0-rc.12` (newer versions are ESM-only and break Vite). |
210
+ | Vite: `'classnames' does not provide an export named 'default'` | Vite hasn't pre-bundled antd's CJS transitive deps. Add the `optimizeDeps.include` list above, delete `node_modules/.vite`, and restart with `vite --force`. |
211
+ | antd `[antd: compatible] React is 16 ~ 18` warning + broken dropdowns / tooltips | You are on React 19. Downgrade to React 18.3.1. |
212
+ | Editor shows "Editor unavailable" forever | License verify failed. Check the `/license/verify` network response, confirm `apiKey`, watch `onLicenseError`. |
213
+ | Editor renders fine then suddenly fails | Access token expired and refresh failed. Clear `localStorage` (`esender.accessToken`, `esender.refreshToken`) and reload. |
214
+ | `You cannot render a <Router> inside another <Router>` | You're on an outdated version. Update — current package uses `MemoryRouter` internally. |
215
+ | Renders during SSR throw | Mount the component client-side only (see Next.js example above). |
216
+
217
+ ## Publishing (maintainers)
218
+
219
+ ```bash
220
+ # 1. Bump version (semver)
221
+ npm version patch # or minor / major
222
+
223
+ # 2. prepublishOnly chains: check-secrets → typecheck → rollup (clean + bundle + obfuscate + bundle check)
224
+ npm publish
225
+ ```
226
+
227
+ Pre-publish hooks:
228
+ - `check-secrets` — scans for forbidden files (`.env`, `.pem`, `.key`) and provider-prefixed key strings (AWS / OpenAI / SendGrid / Google / GitHub / Slack / private-key blocks).
229
+ - `typecheck` — `tsc --noEmit`.
230
+ - `rollup` — produces `dist/index.js`, `dist/index.cjs`, `dist/index.d.ts`, `dist/styles.css`, then runs `scripts/obfuscate.js` and `scripts/check-bundle.js`.
231
+
232
+ Inspect the publish tarball before pushing:
233
+
234
+ ```bash
235
+ npm pack --dry-run
236
+ ```
237
+
238
+ Only the contents of `dist/`, `README.md`, `LICENSE`, and `package.json` ship.
239
+
240
+ ## Versioning
241
+
242
+ Strict semver:
243
+ - **PATCH** — bug fixes, no API changes.
244
+ - **MINOR** — additive features, no breakage.
245
+ - **MAJOR** — breaking changes to `<Package />` props or `EditorHandle`.
246
+
247
+ ## License
248
+
249
+ MIT — see [LICENSE](LICENSE).