@getdraft/plugin 1.13.0 → 1.15.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/.eslintrc +2 -2
- package/README.md +5 -2
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +231 -205
- package/dist/index.es.js.map +1 -1
- package/dist/index.iife.js +2 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/plugin.d.ts +5 -2
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/types.d.ts +21 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/index.ts +3 -0
- package/src/plugin.ts +89 -34
- package/src/types.ts +29 -0
- package/vite.config.ts +2 -2
- package/.turbo/turbo-build.log +0 -22
- package/.turbo/turbo-eslint.log +0 -4
package/src/plugin.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LOCAL_ENDPOINT, PROD_ENDPOINT } from './routes';
|
|
2
2
|
import {
|
|
3
|
+
DraftError,
|
|
3
4
|
EditorMenu,
|
|
4
5
|
ExitParams,
|
|
5
6
|
OnInitParams,
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
UploadImageParams,
|
|
12
13
|
ViewMode,
|
|
13
14
|
HandlerParams,
|
|
15
|
+
MessageData,
|
|
16
|
+
ExportAsFileParams,
|
|
14
17
|
} from './types';
|
|
15
18
|
import {
|
|
16
19
|
getImageParamsFromFileUrl,
|
|
@@ -28,12 +31,8 @@ const getDeployLink = () => {
|
|
|
28
31
|
return LOCAL_ENDPOINT;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
if (
|
|
32
|
-
value
|
|
33
|
-
value?.includes('https://') ||
|
|
34
|
-
window.location.pathname.includes('netlify')
|
|
35
|
-
) {
|
|
36
|
-
return value?.includes('https://') ? value : `${PROD_ENDPOINT}/stage`;
|
|
34
|
+
if (value?.startsWith('https://')) {
|
|
35
|
+
return value;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
const [major, mid] = pkg.version.split('.');
|
|
@@ -55,17 +54,19 @@ const verifyConfig = ({ token, pluginId, apiUrl }: PluginConfig) => {
|
|
|
55
54
|
}
|
|
56
55
|
};
|
|
57
56
|
|
|
58
|
-
const createIframe = () => {
|
|
57
|
+
const createIframe = (parentOrigin: string) => {
|
|
59
58
|
const iframe = document.createElement('iframe');
|
|
60
|
-
|
|
59
|
+
const url = new URL(getDeployLink());
|
|
60
|
+
url.searchParams.set('parentOrigin', parentOrigin);
|
|
61
|
+
iframe.src = url.toString();
|
|
61
62
|
iframe.setAttribute(
|
|
62
63
|
'style',
|
|
63
|
-
'height:100%;width:100%;min-width:960px;border:0px',
|
|
64
|
+
'height:100%;width:100%;min-width:960px;border:0px;',
|
|
64
65
|
);
|
|
65
66
|
iframe.setAttribute('allow', 'clipboard-read; clipboard-write');
|
|
66
67
|
iframe.setAttribute(
|
|
67
68
|
'sandbox',
|
|
68
|
-
'allow-scripts allow-same-origin allow-forms allow-popups',
|
|
69
|
+
'allow-scripts allow-same-origin allow-forms allow-popups allow-downloads',
|
|
69
70
|
);
|
|
70
71
|
|
|
71
72
|
return iframe;
|
|
@@ -74,6 +75,7 @@ const createIframe = () => {
|
|
|
74
75
|
export class PluginInstance {
|
|
75
76
|
public iframe: HTMLIFrameElement | null = null;
|
|
76
77
|
private hotkeysAttached = false;
|
|
78
|
+
private iframeOrigin: string | null = null;
|
|
77
79
|
constructor(public config: PluginConfig) {}
|
|
78
80
|
|
|
79
81
|
private captureHotkeysEnabled() {
|
|
@@ -116,35 +118,69 @@ export class PluginInstance {
|
|
|
116
118
|
});
|
|
117
119
|
};
|
|
118
120
|
|
|
121
|
+
private static HEARTBEAT_TIMEOUT = 4000;
|
|
122
|
+
|
|
119
123
|
private promisedIframeMethod<T>(eventName: string) {
|
|
120
124
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
125
|
+
let listener: ((e: MessageEvent<MessageData>) => void) | null = null;
|
|
126
|
+
|
|
127
|
+
const cleanup = () => {
|
|
128
|
+
if (timer) {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
timer = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (listener) {
|
|
134
|
+
window.removeEventListener('message', listener, true);
|
|
135
|
+
listener = null;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
121
138
|
|
|
122
139
|
const res = new Promise<T>((resolve, reject) => {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
const resetTimer = () => {
|
|
141
|
+
if (timer) clearTimeout(timer);
|
|
142
|
+
timer = setTimeout(() => {
|
|
143
|
+
cleanup();
|
|
144
|
+
reject(
|
|
145
|
+
new DraftError('timeout_error', `Action "${eventName}" timed out`),
|
|
146
|
+
);
|
|
147
|
+
}, PluginInstance.HEARTBEAT_TIMEOUT);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
listener = (e: MessageEvent<MessageData>) => {
|
|
151
|
+
if (e.source !== this.iframe?.contentWindow) return;
|
|
152
|
+
|
|
153
|
+
const { data } = e;
|
|
154
|
+
if (data.source !== 'DraftPlugin') return;
|
|
155
|
+
|
|
156
|
+
if (data.event === 'actionPing' && data.action === eventName) {
|
|
130
157
|
e.stopPropagation();
|
|
158
|
+
resetTimer();
|
|
131
159
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (data.event === eventName) {
|
|
164
|
+
e.stopPropagation();
|
|
165
|
+
cleanup();
|
|
166
|
+
|
|
167
|
+
const { source: _1, event: _2, ok, code, message, ...rest } = data;
|
|
168
|
+
|
|
169
|
+
if (ok === false) {
|
|
170
|
+
reject(
|
|
171
|
+
new DraftError(
|
|
172
|
+
code || 'unknown_error',
|
|
173
|
+
message || 'Unknown error',
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
resolve(rest as T);
|
|
135
178
|
}
|
|
136
|
-
resolve(data as T);
|
|
137
|
-
window.removeEventListener('message', onEvent, true);
|
|
138
179
|
}
|
|
139
180
|
};
|
|
140
181
|
|
|
141
|
-
window.addEventListener('message',
|
|
142
|
-
|
|
143
|
-
timer = setTimeout(() => {
|
|
144
|
-
window.removeEventListener('message', onEvent, true);
|
|
145
|
-
reject('Request timed out');
|
|
146
|
-
timer = null;
|
|
147
|
-
}, 2000);
|
|
182
|
+
window.addEventListener('message', listener, true);
|
|
183
|
+
resetTimer();
|
|
148
184
|
});
|
|
149
185
|
|
|
150
186
|
this.postEvent(eventName);
|
|
@@ -153,15 +189,17 @@ export class PluginInstance {
|
|
|
153
189
|
}
|
|
154
190
|
|
|
155
191
|
private postEvent(event: string, data: object = {}) {
|
|
156
|
-
if (this.iframe && this.iframe.contentWindow) {
|
|
192
|
+
if (this.iframe && this.iframe.contentWindow && this.iframeOrigin) {
|
|
157
193
|
this.iframe.contentWindow.postMessage(
|
|
158
194
|
{
|
|
159
195
|
...data,
|
|
160
196
|
event,
|
|
161
197
|
source: 'DraftPlugin',
|
|
162
198
|
},
|
|
163
|
-
|
|
199
|
+
this.iframeOrigin,
|
|
164
200
|
);
|
|
201
|
+
} else if (!this.iframeOrigin) {
|
|
202
|
+
throw new Error('Iframe origin is not initialized');
|
|
165
203
|
}
|
|
166
204
|
}
|
|
167
205
|
|
|
@@ -232,6 +270,10 @@ export class PluginInstance {
|
|
|
232
270
|
private onMessage = (
|
|
233
271
|
ev: MessageEvent<{ source: string; event: keyof Handlers }>,
|
|
234
272
|
) => {
|
|
273
|
+
if (ev.source !== this.iframe?.contentWindow) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
235
277
|
const {
|
|
236
278
|
data: { source, event, ...data },
|
|
237
279
|
} = ev;
|
|
@@ -258,7 +300,7 @@ export class PluginInstance {
|
|
|
258
300
|
uid,
|
|
259
301
|
}: {
|
|
260
302
|
template: string | TemplateJSON;
|
|
261
|
-
uid: string;
|
|
303
|
+
uid: string | number;
|
|
262
304
|
templateName?: string;
|
|
263
305
|
}) {
|
|
264
306
|
const containerEl = document.querySelector(this.config.container);
|
|
@@ -266,8 +308,15 @@ export class PluginInstance {
|
|
|
266
308
|
if (!containerEl) {
|
|
267
309
|
throw new Error('Specified container not found');
|
|
268
310
|
} else {
|
|
269
|
-
const onInit = (
|
|
311
|
+
const onInit = (msg: OnInitParams) => {
|
|
312
|
+
const {
|
|
313
|
+
data: { source, event },
|
|
314
|
+
} = msg;
|
|
270
315
|
if (source === 'DraftPlugin' && event === 'loaded') {
|
|
316
|
+
if (msg.source !== this.iframe?.contentWindow) return;
|
|
317
|
+
|
|
318
|
+
this.iframeOrigin = msg.origin;
|
|
319
|
+
|
|
271
320
|
const {
|
|
272
321
|
on,
|
|
273
322
|
container,
|
|
@@ -276,6 +325,7 @@ export class PluginInstance {
|
|
|
276
325
|
token,
|
|
277
326
|
apiUrl,
|
|
278
327
|
pluginId,
|
|
328
|
+
authData,
|
|
279
329
|
__config,
|
|
280
330
|
} = this.config;
|
|
281
331
|
|
|
@@ -289,6 +339,7 @@ export class PluginInstance {
|
|
|
289
339
|
autosave,
|
|
290
340
|
token,
|
|
291
341
|
pluginId,
|
|
342
|
+
authData,
|
|
292
343
|
apiUrl,
|
|
293
344
|
__config,
|
|
294
345
|
enabledListeners: Object.keys(on),
|
|
@@ -302,7 +353,7 @@ export class PluginInstance {
|
|
|
302
353
|
}
|
|
303
354
|
};
|
|
304
355
|
|
|
305
|
-
this.iframe = createIframe();
|
|
356
|
+
this.iframe = createIframe(window.location.origin);
|
|
306
357
|
window.addEventListener('message', onInit);
|
|
307
358
|
containerEl.appendChild(this.iframe);
|
|
308
359
|
|
|
@@ -369,4 +420,8 @@ export class PluginInstance {
|
|
|
369
420
|
exportContent() {
|
|
370
421
|
return this.promisedIframeMethod<SaveParams>('exportContent');
|
|
371
422
|
}
|
|
423
|
+
|
|
424
|
+
exportAsFile() {
|
|
425
|
+
return this.promisedIframeMethod<ExportAsFileParams>('exportAsFile');
|
|
426
|
+
}
|
|
372
427
|
}
|
package/src/types.ts
CHANGED
|
@@ -2,6 +2,30 @@ export type EventHandler<T> = (data: T) => void;
|
|
|
2
2
|
|
|
3
3
|
export type AsyncEventHandler<P, R> = (params: P) => Promise<R>;
|
|
4
4
|
|
|
5
|
+
export class DraftError extends Error {
|
|
6
|
+
public readonly code: string;
|
|
7
|
+
|
|
8
|
+
constructor(code: string, message: string) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'DraftError';
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type MessageData = {
|
|
16
|
+
source: string;
|
|
17
|
+
event: string;
|
|
18
|
+
ok?: boolean;
|
|
19
|
+
code?: string;
|
|
20
|
+
message?: string;
|
|
21
|
+
action?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface ExportAsFileParams {
|
|
25
|
+
blob: Blob;
|
|
26
|
+
fileName: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
5
29
|
export interface SaveParams {
|
|
6
30
|
json: TemplateJSON;
|
|
7
31
|
html: string;
|
|
@@ -34,6 +58,7 @@ export interface ReadyParams extends SaveParams {
|
|
|
34
58
|
export interface ErrorParams {
|
|
35
59
|
message: string;
|
|
36
60
|
code?: string;
|
|
61
|
+
details?: unknown;
|
|
37
62
|
}
|
|
38
63
|
|
|
39
64
|
export interface ExitParams extends SaveParams {}
|
|
@@ -239,6 +264,10 @@ export interface PluginConfig {
|
|
|
239
264
|
interval: number;
|
|
240
265
|
};
|
|
241
266
|
token: string;
|
|
267
|
+
authData?: {
|
|
268
|
+
orgId: number;
|
|
269
|
+
projectId?: number;
|
|
270
|
+
};
|
|
242
271
|
pluginId: string;
|
|
243
272
|
apiUrl: string;
|
|
244
273
|
disableHotkeysPassing?: boolean;
|
package/vite.config.ts
CHANGED
|
@@ -9,8 +9,8 @@ export default defineConfig({
|
|
|
9
9
|
sourcemap: true,
|
|
10
10
|
lib: {
|
|
11
11
|
entry: path.resolve(__dirname, 'src/index.ts'),
|
|
12
|
-
name: '
|
|
13
|
-
formats: ['es', 'cjs'],
|
|
12
|
+
name: 'DraftPlugin',
|
|
13
|
+
formats: ['es', 'cjs', 'iife'],
|
|
14
14
|
fileName: (format) => `index.${format}.js`,
|
|
15
15
|
},
|
|
16
16
|
},
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @letty-email/plugin@1.12.1 build /Users/nipanasovich/Documents/Work/draft/packages/plugin
|
|
3
|
-
> vite build
|
|
4
|
-
|
|
5
|
-
[33mThe CJS build of Vite's Node API is deprecated. See https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated for more details.[39m
|
|
6
|
-
vite v5.4.20 building for production...
|
|
7
|
-
transforming...
|
|
8
|
-
✓ 6 modules transformed.
|
|
9
|
-
rendering chunks...
|
|
10
|
-
|
|
11
|
-
[vite:dts] Start generate declaration files...
|
|
12
|
-
computing gzip size...
|
|
13
|
-
dist/index.es.js 7.69 kB │ gzip: 2.73 kB │ map: 21.92 kB
|
|
14
|
-
|
|
15
|
-
[vite:dts] The resolved path of type entry is not ending with '.d.ts'.
|
|
16
|
-
|
|
17
|
-
[vite:dts] Outside emitted: /Users/nipanasovich/Documents/Work/draft/packages/plugin/src.d.ts
|
|
18
|
-
[vite:dts] Declaration files built in 8574ms.
|
|
19
|
-
|
|
20
|
-
Entry module "src/index.ts" is using named and default exports together. Consumers of your bundle will have to use `seplugin.default` to access the default export, which may not be what you want. Use `output.exports: "named"` to disable this warning.
|
|
21
|
-
dist/index.cjs.js 5.69 kB │ gzip: 2.41 kB │ map: 21.10 kB
|
|
22
|
-
✓ built in 9.44s
|
package/.turbo/turbo-eslint.log
DELETED