@weavour/editor 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +113 -2
- package/dist/classic.d.ts +6 -0
- package/dist/core.d.ts +2 -0
- package/dist/editor-blocks.d.ts +2 -0
- package/dist/editor-theme.d.ts +30 -0
- package/dist/editor.js +653 -0
- package/dist/email-document.d.ts +11 -0
- package/dist/errors.d.ts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +653 -0
- package/dist/legacy.d.ts +1 -0
- package/dist/legacy.js +653 -0
- package/dist/merge-tags.d.ts +26 -0
- package/dist/protocol.d.ts +9 -0
- package/dist/protocol.js +234 -0
- package/dist/template-preview-limits.d.ts +2 -0
- package/dist/types.d.ts +94 -0
- package/package.json +35 -4
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Public configuration, never recipient values or credentials. */
|
|
2
|
+
export type MergeTag = {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
token: string;
|
|
6
|
+
kind: 'text' | 'url';
|
|
7
|
+
description?: string;
|
|
8
|
+
sampleValue?: string;
|
|
9
|
+
};
|
|
10
|
+
export type MergeTagReference = Pick<MergeTag, 'id' | 'token' | 'kind'>;
|
|
11
|
+
export declare const defaultMergeTags: MergeTag[];
|
|
12
|
+
export declare function validMergeToken(value: unknown): value is string;
|
|
13
|
+
/** Also used for persisted references; these intentionally contain no samples. */
|
|
14
|
+
export declare function mergeTagReferences(value: unknown): MergeTagReference[];
|
|
15
|
+
export declare function parseMergeTags(value: unknown): MergeTag[];
|
|
16
|
+
export declare function resolveMergeTags(saved?: unknown, overrides?: unknown): MergeTag[];
|
|
17
|
+
export declare function safeMergeLink(value: string, tags?: readonly MergeTagReference[]): string | undefined;
|
|
18
|
+
export type MergeTagIssue = {
|
|
19
|
+
code: 'unavailable_tag' | 'changed_tag' | 'invalid_tag_context';
|
|
20
|
+
id: string;
|
|
21
|
+
token: string;
|
|
22
|
+
location: string;
|
|
23
|
+
};
|
|
24
|
+
export type MergeTagUsage = MergeTagReference & {
|
|
25
|
+
locations: string[];
|
|
26
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { parseMergeTags, resolveMergeTags, mergeTagReferences, defaultMergeTags, safeMergeLink, validMergeToken } from './merge-tags.js';
|
|
2
|
+
export { emailDocument, canonicalJson } from './email-document.js';
|
|
3
|
+
export { resolveEditorTheme, readableForeground, editorSurfacePalette, colorContrast } from './editor-theme.js';
|
|
4
|
+
export { editorBlockIds } from './editor-blocks.js';
|
|
5
|
+
export type * from './types.js';
|
|
6
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
7
|
+
export declare const MAX_MESSAGE_BYTES = 2097152;
|
|
8
|
+
/** Only JSON-compatible wire values, with bounded size and nesting. */
|
|
9
|
+
export declare function validMessage(value: unknown): value is Record<string, any>;
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// packages/editor/src/merge-tags.ts
|
|
2
|
+
var defaultMergeTags = [
|
|
3
|
+
{ id: "unsubscribe_url", label: "Unsubscribe link", token: "{{unsubscribe_url}}", kind: "url" },
|
|
4
|
+
{ id: "online_version_url", label: "Online version link", token: "{{online_version_url}}", kind: "url" }
|
|
5
|
+
];
|
|
6
|
+
var control = /[\u0000-\u001f\u007f]/;
|
|
7
|
+
function validMergeToken(value) {
|
|
8
|
+
return typeof value === "string" && !!value.trim() && value.length <= 200 && !control.test(value) && !/[<>"'`&]/.test(value) && !/^[a-z][a-z\d+.-]*:/i.test(value.trimStart());
|
|
9
|
+
}
|
|
10
|
+
function mergeTagReferences(value) {
|
|
11
|
+
if (!Array.isArray(value) || value.length > 1e3) throw new Error("invalid_merge_tags");
|
|
12
|
+
return value.map((tag) => {
|
|
13
|
+
if (!tag || typeof tag !== "object" || typeof tag.id !== "string" || !/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(tag.id) || !validMergeToken(tag.token) || !["text", "url"].includes(tag.kind) || defaultMergeTags.some((builtin) => builtin.id === tag.id && tag.kind !== "url")) throw new Error("invalid_merge_tags");
|
|
14
|
+
return { id: tag.id, token: tag.token, kind: tag.kind };
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function parseMergeTags(value) {
|
|
18
|
+
if (!Array.isArray(value) || value.length > 100) throw new Error("invalid_merge_tags");
|
|
19
|
+
const refs = mergeTagReferences(value);
|
|
20
|
+
const tags = value.map((tag, index) => {
|
|
21
|
+
if (Object.keys(tag).some((key) => !["id", "label", "token", "kind", "description", "sampleValue"].includes(key)) || typeof tag.label !== "string" || !tag.label.trim() || tag.label.length > 100 || control.test(tag.label) || tag.description !== void 0 && (typeof tag.description !== "string" || tag.description.length > 500 || control.test(tag.description)) || tag.sampleValue !== void 0 && (typeof tag.sampleValue !== "string" || tag.sampleValue.length > 2048 || control.test(tag.sampleValue)) || tag.kind === "url" && tag.sampleValue && !safeMergeLink(tag.sampleValue)) throw new Error("invalid_merge_tags");
|
|
22
|
+
return { ...refs[index], label: tag.label, ...tag.description === void 0 ? {} : { description: tag.description }, ...tag.sampleValue === void 0 ? {} : { sampleValue: tag.sampleValue } };
|
|
23
|
+
});
|
|
24
|
+
const ids = /* @__PURE__ */ new Set();
|
|
25
|
+
for (const [index, tag] of tags.entries()) {
|
|
26
|
+
if (ids.has(tag.id) || tags.some((other, at) => at !== index && (other.token.includes(tag.token) || tag.token.includes(other.token)))) throw new Error("invalid_merge_tags");
|
|
27
|
+
ids.add(tag.id);
|
|
28
|
+
}
|
|
29
|
+
return tags;
|
|
30
|
+
}
|
|
31
|
+
function resolveMergeTags(saved = [], overrides = []) {
|
|
32
|
+
const resolved = new Map(defaultMergeTags.map((tag) => [tag.id, tag]));
|
|
33
|
+
for (const layer of [saved, overrides]) for (const tag of parseMergeTags(layer)) resolved.set(tag.id, tag);
|
|
34
|
+
return parseMergeTags([...resolved.values()]);
|
|
35
|
+
}
|
|
36
|
+
function safeMergeLink(value, tags = []) {
|
|
37
|
+
if (!value || value.length > 2048 || control.test(value)) return void 0;
|
|
38
|
+
if (tags.some((tag) => tag.kind === "url" && tag.token === value)) return value;
|
|
39
|
+
if (/\s|[{}<>"`]/.test(value)) return void 0;
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(value);
|
|
42
|
+
return ["https:", "http:", "mailto:", "tel:"].includes(url.protocol) && !url.username && !url.password && (["https:", "http:"].includes(url.protocol) || url.pathname) ? value : void 0;
|
|
43
|
+
} catch {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// packages/editor/src/template-preview-limits.ts
|
|
49
|
+
var TEMPLATE_PREVIEW_LIMIT = 131072;
|
|
50
|
+
|
|
51
|
+
// packages/editor/src/email-document.ts
|
|
52
|
+
function emailDocument(value) {
|
|
53
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("invalid_document");
|
|
54
|
+
const doc = value;
|
|
55
|
+
if (doc.schemaVersion !== 1 || typeof doc.subject !== "string" || doc.subject.length > 200 || typeof doc.preheader !== "string" || doc.preheader.length > 500 || !doc.content || typeof doc.content !== "object" || Array.isArray(doc.content)) throw new Error("invalid_document");
|
|
56
|
+
let nodes = 0;
|
|
57
|
+
const visit = (value2, depth) => {
|
|
58
|
+
if (!value2 || typeof value2 !== "object" || Array.isArray(value2) || depth > 50 || ++nodes > 1e4) throw new Error("invalid_document");
|
|
59
|
+
const node = value2;
|
|
60
|
+
if (typeof node.type !== "string" || !node.type || node.type.length > 100 || node.text !== void 0 && typeof node.text !== "string") throw new Error("invalid_document");
|
|
61
|
+
if (node.type === "doc") {
|
|
62
|
+
const attrs = node.attrs;
|
|
63
|
+
if (attrs?.mergeTagReferences !== void 0) mergeTagReferences(attrs.mergeTagReferences);
|
|
64
|
+
}
|
|
65
|
+
if (node.type === "mergeTag" || node.type === "unsubscribe") {
|
|
66
|
+
const attrs = node.attrs;
|
|
67
|
+
if (!attrs) throw new Error("invalid_document");
|
|
68
|
+
mergeTagReferences([{ id: attrs.id, token: attrs.token, kind: node.type === "unsubscribe" ? "url" : attrs.kind }]);
|
|
69
|
+
if (node.type === "unsubscribe" && (attrs.id !== "unsubscribe_url" || typeof attrs.label !== "string" || !attrs.label.trim() || attrs.label.length > 500)) throw new Error("invalid_document");
|
|
70
|
+
}
|
|
71
|
+
if (node.content !== void 0) {
|
|
72
|
+
if (!Array.isArray(node.content)) throw new Error("invalid_document");
|
|
73
|
+
node.content.forEach((child) => visit(child, depth + 1));
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
if (doc.content.type !== "doc") throw new Error("invalid_document");
|
|
77
|
+
visit(doc.content, 0);
|
|
78
|
+
if (doc.previewHtml !== void 0 && (typeof doc.previewHtml !== "string" || doc.previewHtml.length > TEMPLATE_PREVIEW_LIMIT)) throw new Error("invalid_document");
|
|
79
|
+
return { ...doc.previewHtml === void 0 ? {} : { previewHtml: doc.previewHtml }, schemaVersion: 1, content: doc.content, subject: doc.subject, preheader: doc.preheader };
|
|
80
|
+
}
|
|
81
|
+
function canonicalJson(value) {
|
|
82
|
+
if (Array.isArray(value)) return "[" + value.map(canonicalJson).join(",") + "]";
|
|
83
|
+
if (value && typeof value === "object") return "{" + Object.keys(value).sort().filter((key) => value[key] !== void 0).map((key) => JSON.stringify(key) + ":" + canonicalJson(value[key])).join(",") + "}";
|
|
84
|
+
return JSON.stringify(value);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// packages/editor/src/editor-theme.ts
|
|
88
|
+
function resolveEditorTheme(input = {}) {
|
|
89
|
+
const dark = input.mode === "dark";
|
|
90
|
+
const theme = {
|
|
91
|
+
mode: dark ? "dark" : "light",
|
|
92
|
+
primaryColor: dark ? "#9c90ff" : "#6351ce",
|
|
93
|
+
backgroundColor: dark ? "#111827" : "#f7f8fc",
|
|
94
|
+
surfaceColor: dark ? "#1b2232" : "#ffffff",
|
|
95
|
+
shellColor: dark ? "#1b2232" : "#ffffff",
|
|
96
|
+
inputColor: dark ? "#1b2232" : "#ffffff",
|
|
97
|
+
textColor: dark ? "#e9edf5" : "#111827",
|
|
98
|
+
mutedColor: dark ? "#a6b1c6" : "#56657b",
|
|
99
|
+
borderColor: dark ? "#2c3750" : "#dce1eb",
|
|
100
|
+
cornerRadius: 8,
|
|
101
|
+
density: "comfortable",
|
|
102
|
+
fontFamily: "Inter"
|
|
103
|
+
};
|
|
104
|
+
for (const key of ["primaryColor", "backgroundColor", "surfaceColor", "shellColor", "inputColor", "textColor", "mutedColor", "borderColor"]) {
|
|
105
|
+
const value = input[key];
|
|
106
|
+
if (typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value)) theme[key] = value;
|
|
107
|
+
}
|
|
108
|
+
if (!/^#[0-9a-f]{6}$/i.test(input.shellColor ?? "")) theme.shellColor = theme.surfaceColor;
|
|
109
|
+
if (!/^#[0-9a-f]{6}$/i.test(input.inputColor ?? "")) theme.inputColor = theme.surfaceColor;
|
|
110
|
+
if (typeof input.cornerRadius === "number" && Number.isFinite(input.cornerRadius)) {
|
|
111
|
+
theme.cornerRadius = Math.max(0, Math.min(24, input.cornerRadius));
|
|
112
|
+
}
|
|
113
|
+
if (input.density === "compact") theme.density = "compact";
|
|
114
|
+
if (typeof input.fontFamily === "string" && /^[\p{L}\p{N}][\p{L}\p{N} ._-]{0,99}$/u.test(input.fontFamily)) theme.fontFamily = input.fontFamily;
|
|
115
|
+
return theme;
|
|
116
|
+
}
|
|
117
|
+
function luminance(color) {
|
|
118
|
+
const channels = [1, 3, 5].map((start) => parseInt(color.slice(start, start + 2), 16) / 255).map((value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4);
|
|
119
|
+
return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
|
|
120
|
+
}
|
|
121
|
+
function colorContrast(first, second) {
|
|
122
|
+
const a = luminance(first), b = luminance(second);
|
|
123
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
124
|
+
}
|
|
125
|
+
function readableForeground(background) {
|
|
126
|
+
if (colorContrast(background, "#111111") >= 4.5 && colorContrast(background, "#111111") >= colorContrast(background, "#ffffff")) return "#111111";
|
|
127
|
+
return colorContrast(background, "#000000") > colorContrast(background, "#ffffff") ? "#000000" : "#ffffff";
|
|
128
|
+
}
|
|
129
|
+
function mixColor(background, foreground, amount) {
|
|
130
|
+
return "#" + [1, 3, 5].map((start) => Math.round(
|
|
131
|
+
parseInt(background.slice(start, start + 2), 16) * (1 - amount) + parseInt(foreground.slice(start, start + 2), 16) * amount
|
|
132
|
+
).toString(16).padStart(2, "0")).join("");
|
|
133
|
+
}
|
|
134
|
+
function readableColor(backgrounds, candidate, fallback, minimum = 4.5) {
|
|
135
|
+
const passes = (color) => backgrounds.every((background) => colorContrast(background, color) >= minimum);
|
|
136
|
+
const seed = candidate && /^#[0-9a-f]{6}$/i.test(candidate) ? candidate : fallback;
|
|
137
|
+
if (passes(seed)) return seed;
|
|
138
|
+
for (let step = 1; step <= 100; step++) {
|
|
139
|
+
const color = mixColor(seed, fallback, step / 100);
|
|
140
|
+
if (passes(color)) return color;
|
|
141
|
+
}
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
function stateBackground(background, target, text) {
|
|
145
|
+
for (let step = 100; step >= 0; step--) {
|
|
146
|
+
const color = mixColor(background, target, step / 100);
|
|
147
|
+
if (colorContrast(color, text) >= 4.5) return color;
|
|
148
|
+
}
|
|
149
|
+
return background;
|
|
150
|
+
}
|
|
151
|
+
function editorSurfacePalette(background, theme) {
|
|
152
|
+
const foreground = readableForeground(background);
|
|
153
|
+
const text = theme.textColor && /^#[0-9a-f]{6}$/i.test(theme.textColor) && colorContrast(background, theme.textColor) >= 4.5 ? theme.textColor : foreground;
|
|
154
|
+
const hover = stateBackground(background, luminance(background) > 0.7 ? mixColor(background, "#000000", 0.04) : mixColor(background, "#ffffff", 0.08), text);
|
|
155
|
+
const selected = stateBackground(background, mixColor(background, foreground, 0.04), text);
|
|
156
|
+
const states = [background, hover, selected];
|
|
157
|
+
const muted = readableColor(states, theme.mutedColor ?? mixColor(background, text, 0.65), text);
|
|
158
|
+
const danger = readableColor(states, "#b93845", states.every((color) => colorContrast(color, "#ffffff") >= 4.5) ? "#ffffff" : "#000000");
|
|
159
|
+
const focus = readableColor(states, theme.primaryColor, text, 3);
|
|
160
|
+
const border = readableColor([background], mixColor(background, foreground, 0.3), foreground, 3);
|
|
161
|
+
const accessory = mixColor(background, "#000000", 0.08);
|
|
162
|
+
const accessoryText = readableColor([accessory], text, readableForeground(accessory));
|
|
163
|
+
return {
|
|
164
|
+
text,
|
|
165
|
+
muted,
|
|
166
|
+
danger,
|
|
167
|
+
focus,
|
|
168
|
+
hover,
|
|
169
|
+
selected,
|
|
170
|
+
accessory,
|
|
171
|
+
accessoryText,
|
|
172
|
+
accessoryFocus: readableColor([accessory], theme.primaryColor, accessoryText, 3),
|
|
173
|
+
border: theme.borderColor && /^#[0-9a-f]{6}$/i.test(theme.borderColor) ? theme.borderColor : border,
|
|
174
|
+
colorScheme: luminance(background) < 0.179 ? "dark" : "light"
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// packages/editor/src/editor-blocks.ts
|
|
179
|
+
var editorBlockIds = [
|
|
180
|
+
"text",
|
|
181
|
+
"heading",
|
|
182
|
+
"button",
|
|
183
|
+
"image",
|
|
184
|
+
"video",
|
|
185
|
+
"gif",
|
|
186
|
+
"timer",
|
|
187
|
+
"menu",
|
|
188
|
+
"social",
|
|
189
|
+
"unsubscribe",
|
|
190
|
+
"list",
|
|
191
|
+
"html",
|
|
192
|
+
"space",
|
|
193
|
+
"divider",
|
|
194
|
+
"section",
|
|
195
|
+
"columns"
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
// packages/editor/src/protocol.ts
|
|
199
|
+
var PROTOCOL_VERSION = 1;
|
|
200
|
+
var MAX_MESSAGE_BYTES = 2097152;
|
|
201
|
+
function validMessage(value) {
|
|
202
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
203
|
+
let count = 0;
|
|
204
|
+
const visit = (item, depth) => {
|
|
205
|
+
if (++count > 5e4 || depth > 60) return false;
|
|
206
|
+
if (item === null || item === void 0 || typeof item === "string" || typeof item === "boolean") return true;
|
|
207
|
+
if (typeof item === "number") return Number.isFinite(item);
|
|
208
|
+
if (typeof item !== "object") return false;
|
|
209
|
+
return Object.values(item).every((child) => visit(child, depth + 1));
|
|
210
|
+
};
|
|
211
|
+
try {
|
|
212
|
+
return visit(value, 0) && JSON.stringify(value).length <= MAX_MESSAGE_BYTES;
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
MAX_MESSAGE_BYTES,
|
|
219
|
+
PROTOCOL_VERSION,
|
|
220
|
+
canonicalJson,
|
|
221
|
+
colorContrast,
|
|
222
|
+
defaultMergeTags,
|
|
223
|
+
editorBlockIds,
|
|
224
|
+
editorSurfacePalette,
|
|
225
|
+
emailDocument,
|
|
226
|
+
mergeTagReferences,
|
|
227
|
+
parseMergeTags,
|
|
228
|
+
readableForeground,
|
|
229
|
+
resolveEditorTheme,
|
|
230
|
+
resolveMergeTags,
|
|
231
|
+
safeMergeLink,
|
|
232
|
+
validMergeToken,
|
|
233
|
+
validMessage
|
|
234
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { EditorTheme } from './editor-theme.js';
|
|
2
|
+
import type { MergeTag, MergeTagReference, MergeTagUsage, MergeTagIssue } from './merge-tags.js';
|
|
3
|
+
import type { EmailDocument } from './email-document.js';
|
|
4
|
+
import type { EditorBlockId } from './editor-blocks.js';
|
|
5
|
+
export type { EditorTheme, MergeTag, MergeTagReference, MergeTagUsage, MergeTagIssue, EmailDocument, EditorBlockId };
|
|
6
|
+
export type EditorExport = {
|
|
7
|
+
html: string;
|
|
8
|
+
text: string;
|
|
9
|
+
document: Record<string, unknown>;
|
|
10
|
+
subject: string;
|
|
11
|
+
preheader: string;
|
|
12
|
+
mergeTags: MergeTagUsage[];
|
|
13
|
+
mergeTagIssues: MergeTagIssue[];
|
|
14
|
+
};
|
|
15
|
+
export type SaveStatus = 'idle' | 'unsaved' | 'saving' | 'saved' | 'failed' | 'conflict' | 'read-only';
|
|
16
|
+
export type DraftChange = {
|
|
17
|
+
draftId: string | null;
|
|
18
|
+
revision: number | null;
|
|
19
|
+
};
|
|
20
|
+
export type SaveStatusChange = DraftChange & {
|
|
21
|
+
status: SaveStatus;
|
|
22
|
+
};
|
|
23
|
+
export type DraftSummary = {
|
|
24
|
+
id: string;
|
|
25
|
+
subject: string;
|
|
26
|
+
revision: number;
|
|
27
|
+
metadataRevision: number;
|
|
28
|
+
hash: string;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
updatedAt: number;
|
|
31
|
+
archivedAt: number | null;
|
|
32
|
+
sourceTemplateId: string | null;
|
|
33
|
+
baseTemplateRevision: number | null;
|
|
34
|
+
};
|
|
35
|
+
export type DraftFlushResult = {
|
|
36
|
+
draft: DraftSummary | null;
|
|
37
|
+
};
|
|
38
|
+
export type TestEmailRequest = {
|
|
39
|
+
to: string;
|
|
40
|
+
subject: string;
|
|
41
|
+
html: string;
|
|
42
|
+
text: string;
|
|
43
|
+
document: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
export type EditorConfig = {
|
|
46
|
+
theme?: EditorTheme;
|
|
47
|
+
mergeTags?: MergeTag[];
|
|
48
|
+
locale?: string;
|
|
49
|
+
allowedBlocks?: EditorBlockId[];
|
|
50
|
+
enableAi?: boolean;
|
|
51
|
+
aiPaidOrigin?: string;
|
|
52
|
+
enableTemplates?: boolean;
|
|
53
|
+
enableAutosave?: boolean;
|
|
54
|
+
};
|
|
55
|
+
export type EditorCallbacks = {
|
|
56
|
+
onReady?(): void;
|
|
57
|
+
onError?(error: Error): void;
|
|
58
|
+
onChange?(document: Record<string, unknown>): void;
|
|
59
|
+
onDocumentChange?(document: EmailDocument): void;
|
|
60
|
+
onTestEmail?(request: TestEmailRequest): void | Promise<void>;
|
|
61
|
+
onDraftChange?(value: DraftChange): void;
|
|
62
|
+
onSaveStatusChange?(value: SaveStatusChange): void;
|
|
63
|
+
};
|
|
64
|
+
export type MountOptions = EditorConfig & EditorCallbacks & {
|
|
65
|
+
editorKey: string;
|
|
66
|
+
userId?: string;
|
|
67
|
+
organizationId?: string;
|
|
68
|
+
document?: Record<string, unknown>;
|
|
69
|
+
emailDocument?: EmailDocument;
|
|
70
|
+
subject?: string;
|
|
71
|
+
preheader?: string;
|
|
72
|
+
draftId?: string;
|
|
73
|
+
editorConfig?: EditorConfig;
|
|
74
|
+
editorUrl?: string;
|
|
75
|
+
apiUrl?: string;
|
|
76
|
+
demo?: boolean;
|
|
77
|
+
};
|
|
78
|
+
export type EditorInstance = {
|
|
79
|
+
readonly ready: Promise<void>;
|
|
80
|
+
exportHTML(): Promise<EditorExport>;
|
|
81
|
+
previewTemplate(): Promise<EmailDocument>;
|
|
82
|
+
flushAutosave(): Promise<DraftFlushResult>;
|
|
83
|
+
updateConfig(config: EditorConfig): void;
|
|
84
|
+
destroy(): void;
|
|
85
|
+
};
|
|
86
|
+
export type EditorSDK = {
|
|
87
|
+
mount(target: HTMLElement | string, options: MountOptions): EditorInstance;
|
|
88
|
+
};
|
|
89
|
+
/** Dependency injection for local hosts and private dashboard adapters. Never serialized to the iframe. */
|
|
90
|
+
export type SDKOptions = {
|
|
91
|
+
fetch?: typeof globalThis.fetch;
|
|
92
|
+
scriptUrl?: string;
|
|
93
|
+
readiness?: 'application' | 'configured';
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/weavour/editor-sdk.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/weavour/editor-sdk#readme",
|
|
10
|
+
"bugs": "https://github.com/weavour/editor-sdk/issues",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.12"
|
|
21
|
+
},
|
|
2
22
|
"name": "@weavour/editor",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
23
|
+
"description": "Framework-neutral SDK for embedding the hosted Weavour email editor.",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./protocol": {
|
|
32
|
+
"types": "./dist/protocol.d.ts",
|
|
33
|
+
"import": "./dist/protocol.js"
|
|
34
|
+
},
|
|
35
|
+
"./editor.js": "./dist/editor.js"
|
|
36
|
+
}
|
|
37
|
+
}
|