@twip/web 0.0.0-canary-05bfdb9 → 0.0.0-canary-7dfb59b
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 +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/transport/module.d.ts +16 -0
- package/dist/transport/module.js +39 -5
- package/dist/transport/worker.js +22 -3
- package/dist/twip_wasm.wasm +0 -0
- package/dist/worker/entry.js +11 -4
- package/dist/worker.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,4 +32,28 @@ await editor.destroy();
|
|
|
32
32
|
|
|
33
33
|
In Node, workers and tests, `HeadlessEditor.create()` gives the same API with no DOM.
|
|
34
34
|
|
|
35
|
+
## Serving the engine's files yourself
|
|
36
|
+
|
|
37
|
+
The engine runs from files in the package's `dist/`: `worker.js`, `twip-wasm.js`, `twip_wasm.wasm`, and the
|
|
38
|
+
`transport/` and `worker/` folders the worker imports. By default the SDK finds them beside its own code, through
|
|
39
|
+
`new URL(..., import.meta.url)`. If your bundler doesn't follow those (Angular's esbuild builder doesn't), copy
|
|
40
|
+
`dist/` as it is to a folder you serve, and say where, once, before the first `FontLibrary` call or editor:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { configure } from '@twip/web';
|
|
44
|
+
|
|
45
|
+
configure({ assets: new URL('twip/', document.baseURI) }); // or a CDN: 'https://cdn.example.com/twip/'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`assets` may be on another origin. The worker then starts from a same-origin `blob:` module that imports
|
|
49
|
+
`worker.js`, so the other origin must:
|
|
50
|
+
|
|
51
|
+
- send `Access-Control-Allow-Origin` (your page's origin, or `*`) on every file in the folder, as module scripts and
|
|
52
|
+
the WebAssembly are fetched with CORS;
|
|
53
|
+
- serve `twip_wasm.wasm` as `application/wasm`, or the engine compiles it after a full download instead of while
|
|
54
|
+
streaming.
|
|
55
|
+
|
|
56
|
+
With a Content Security Policy, allow `blob:` in `worker-src`, the other origin in `script-src` and `connect-src`, and
|
|
57
|
+
`'wasm-unsafe-eval'` in `script-src` (which the engine needs on any origin).
|
|
58
|
+
|
|
35
59
|
Guides: https://jaxcksn.github.io/twip/docs/guides/web. Licensed under Apache-2.0.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @packageDocumentation
|
|
6
6
|
*/
|
|
7
7
|
export { Editor, HeadlessEditor } from './editor.js';
|
|
8
|
+
export { configure } from './transport/module.js';
|
|
8
9
|
export type { ExportOptions, ExportResult, MountOptions, PdfOptions, SessionOptions } from './editor.js';
|
|
9
10
|
export type { CommandEntry, CommandId, CommandInfo, Commands, FieldTargets, NoArgumentCommandId, Op, OpArgs, PlaceholderAction, PlaceholderLook, } from './commands.js';
|
|
10
11
|
export type { AutoCorrectChange, AutoCorrectEntry, AutoCorrectOptions, CharacterFormatting, CommandState, CommandValues, EditingTarget, EditorState, ElementContext, HighlightColor, HorizontalPosition, HorizontalReference, LineSpacing, MarkupView, Mixed, ObjectHorizontalAlignment, ObjectPlacement, ObjectSelection, ObjectVerticalAlignment, ObjectWrap, PageFormatting, ParagraphFormatting, ReviewState, RowHeight, SelectionSummary, TablePosition, TableSelection, TableStyleOptions, TableWidth, TextDistance, Tri, UnderlineStyle, VerticalPosition, VerticalReference, ViewState, WrapSide, } from './state.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @packageDocumentation
|
|
6
6
|
*/
|
|
7
7
|
export { Editor, HeadlessEditor } from './editor.js';
|
|
8
|
+
export { configure } from './transport/module.js';
|
|
8
9
|
export { EditorError } from './errors.js';
|
|
9
10
|
export { FontLibrary } from './fonts/library.js';
|
|
10
11
|
export { cm, inches, mm, pt } from './types.js';
|
|
@@ -47,6 +47,22 @@ export interface TwipModule {
|
|
|
47
47
|
deleteContext(handle: number): void;
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Sets where the package's runtime files (dist/: worker.js, twip-wasm.js, twip_wasm.wasm, transport/ and worker/) are
|
|
52
|
+
* served from, for an application whose bundler doesn't follow `new URL(..., import.meta.url)`. Call it once, before
|
|
53
|
+
* anything starts the engine: the first {@link FontLibrary} call, {@link Editor.mount} or
|
|
54
|
+
* {@link HeadlessEditor.create}. The engine is shared by every session, so it's not a per-session option.
|
|
55
|
+
*
|
|
56
|
+
* `assets` is the folder, resolved against the document's base URL. It may be on another origin (a CDN), which must
|
|
57
|
+
* then send CORS headers for every file; see the package README.
|
|
58
|
+
*
|
|
59
|
+
* @throws {@link EditorError} `not-allowed` when the engine has already loaded from another folder.
|
|
60
|
+
*/
|
|
61
|
+
export declare function configure(options: {
|
|
62
|
+
assets: string | URL;
|
|
63
|
+
}): void;
|
|
64
|
+
/** The configured assets folder, if any, read to start the engine: it can't change after this. */
|
|
65
|
+
export declare function useAssets(): URL | undefined;
|
|
50
66
|
/** Instantiates the engine module, with `dispatch` registered before any engine call can call back. */
|
|
51
67
|
export declare function loadModule(dispatch: NonNullable<TwipModule['twipDispatch']>): Promise<TwipModule>;
|
|
52
68
|
export {};
|
package/dist/transport/module.js
CHANGED
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
// Loading the engine module: the one place that knows where the WebAssembly build is and what it exports.
|
|
2
2
|
// The build copies the emscripten output (twip-wasm.js and twip_wasm.wasm) next to dist/index.js.
|
|
3
|
+
import { EditorError } from '../errors.js';
|
|
3
4
|
/**
|
|
4
|
-
* Where the engine module and its WebAssembly are
|
|
5
|
-
* `new URL(..., import.meta.url)`s, so a bundler copies both files into the application's
|
|
5
|
+
* Where the engine module and its WebAssembly are by default: relative to this file in dist/transport/. Both are
|
|
6
|
+
* literal `new URL(..., import.meta.url)`s, so a bundler that follows them copies both files into the application's
|
|
7
|
+
* build. {@link configure}'s `assets` replaces them.
|
|
6
8
|
*/
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
+
const defaultModuleUrl = new URL('../twip-wasm.js', import.meta.url);
|
|
10
|
+
const defaultWasmUrl = new URL('../twip_wasm.wasm', import.meta.url);
|
|
11
|
+
/** The folder of the package's dist/ files, when the application set one. */
|
|
12
|
+
let assets;
|
|
13
|
+
/** Whether this thread has read `assets` to start the engine: it can't change after. */
|
|
14
|
+
let used = false;
|
|
15
|
+
/**
|
|
16
|
+
* Sets where the package's runtime files (dist/: worker.js, twip-wasm.js, twip_wasm.wasm, transport/ and worker/) are
|
|
17
|
+
* served from, for an application whose bundler doesn't follow `new URL(..., import.meta.url)`. Call it once, before
|
|
18
|
+
* anything starts the engine: the first {@link FontLibrary} call, {@link Editor.mount} or
|
|
19
|
+
* {@link HeadlessEditor.create}. The engine is shared by every session, so it's not a per-session option.
|
|
20
|
+
*
|
|
21
|
+
* `assets` is the folder, resolved against the document's base URL. It may be on another origin (a CDN), which must
|
|
22
|
+
* then send CORS headers for every file; see the package README.
|
|
23
|
+
*
|
|
24
|
+
* @throws {@link EditorError} `not-allowed` when the engine has already loaded from another folder.
|
|
25
|
+
*/
|
|
26
|
+
export function configure(options) {
|
|
27
|
+
const url = new URL(options.assets, globalThis.document?.baseURI ?? globalThis.location?.href);
|
|
28
|
+
if (!url.pathname.endsWith('/'))
|
|
29
|
+
url.pathname += '/';
|
|
30
|
+
if (used && url.href !== assets?.href) {
|
|
31
|
+
throw new EditorError('not-allowed', 'configure() must be called before the first FontLibrary call or editor: the engine has already loaded');
|
|
32
|
+
}
|
|
33
|
+
assets = url;
|
|
34
|
+
}
|
|
35
|
+
/** The configured assets folder, if any, read to start the engine: it can't change after this. */
|
|
36
|
+
export function useAssets() {
|
|
37
|
+
used = true;
|
|
38
|
+
return assets;
|
|
39
|
+
}
|
|
9
40
|
/** Instantiates the engine module, with `dispatch` registered before any engine call can call back. */
|
|
10
41
|
export async function loadModule(dispatch) {
|
|
42
|
+
const assets = useAssets();
|
|
43
|
+
const moduleUrl = assets ? new URL('twip-wasm.js', assets) : defaultModuleUrl;
|
|
44
|
+
const wasmUrl = assets ? new URL('twip_wasm.wasm', assets) : defaultWasmUrl;
|
|
11
45
|
const { default: create } = (await import(moduleUrl.href));
|
|
12
|
-
// In Node the module finds its file itself; in a browser it's wherever the bundler put it.
|
|
46
|
+
// In Node the module finds its file itself; in a browser it's wherever the bundler, or `assets`, put it.
|
|
13
47
|
const locateFile = moduleUrl.protocol === 'file:' ? undefined : (file) => (file.endsWith('.wasm') ? wasmUrl : new URL(file, moduleUrl)).href;
|
|
14
48
|
const module = await create({ twipDispatch: dispatch, locateFile });
|
|
15
49
|
module.twipDispatch = dispatch;
|
package/dist/transport/worker.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// cache before any event of the same drain.
|
|
6
6
|
import { EditorError, unsupported } from '../errors.js';
|
|
7
7
|
import { live } from '../host/dom.js';
|
|
8
|
+
import { useAssets } from './module.js';
|
|
8
9
|
import { Method, toError } from './protocol.js';
|
|
9
10
|
let modules;
|
|
10
11
|
/** Whether this page can run the engine in a worker: module workers and `transferControlToOffscreen`. */
|
|
@@ -43,6 +44,26 @@ export function chooseExecution(execution = 'auto') {
|
|
|
43
44
|
throw unsupported("execution: 'worker' on this browser");
|
|
44
45
|
return 'main';
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Starts the engine worker. With no `assets`, it's beside this file's directory, in dist/: written out in one
|
|
49
|
+
* expression, as bundlers recognise it and build the worker and its imports into the application. With `assets`, it's
|
|
50
|
+
* there, told the folder in its URL's query (worker.ts); on another origin, through a same-origin module that imports
|
|
51
|
+
* it, as a worker's script must come from the page's origin.
|
|
52
|
+
*/
|
|
53
|
+
function startWorker(assets) {
|
|
54
|
+
if (!assets)
|
|
55
|
+
return new Worker(new URL('../worker.js', import.meta.url), { type: 'module', name: 'twip-engine' });
|
|
56
|
+
const url = new URL(`worker.js?assets=${encodeURIComponent(assets.href)}`, assets);
|
|
57
|
+
if (url.origin === location.origin)
|
|
58
|
+
return new Worker(url, { type: 'module', name: 'twip-engine' });
|
|
59
|
+
const bootstrap = URL.createObjectURL(new Blob([`import ${JSON.stringify(url.href)};`], { type: 'text/javascript' }));
|
|
60
|
+
const worker = new Worker(bootstrap, { type: 'module', name: 'twip-engine' });
|
|
61
|
+
// Fetched by the time the worker answers, or fails.
|
|
62
|
+
const revoke = () => URL.revokeObjectURL(bootstrap);
|
|
63
|
+
worker.addEventListener('message', revoke, { once: true });
|
|
64
|
+
worker.addEventListener('error', revoke, { once: true });
|
|
65
|
+
return worker;
|
|
66
|
+
}
|
|
46
67
|
let shared;
|
|
47
68
|
/** The page's one worker transport. */
|
|
48
69
|
export function workerTransport() {
|
|
@@ -139,9 +160,7 @@ export class WorkerTransport {
|
|
|
139
160
|
#start() {
|
|
140
161
|
if (this.#worker)
|
|
141
162
|
return this.#worker;
|
|
142
|
-
|
|
143
|
-
// it: they build the worker and its imports into the application.
|
|
144
|
-
const worker = new Worker(new URL('../worker.js', import.meta.url), { type: 'module', name: 'twip-engine' });
|
|
163
|
+
const worker = startWorker(useAssets());
|
|
145
164
|
live.workers++;
|
|
146
165
|
worker.onmessage = ({ data }) => this.#receive(data);
|
|
147
166
|
worker.onerror = (e) => {
|
package/dist/twip_wasm.wasm
CHANGED
|
Binary file
|
package/dist/worker/entry.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { EditorError } from '../errors.js';
|
|
7
7
|
import { directTransport } from '../transport/direct.js';
|
|
8
8
|
import { fromError } from '../transport/protocol.js';
|
|
9
|
-
const engine = directTransport();
|
|
10
9
|
const signals = new Map();
|
|
11
10
|
/** Posts to the page, moving the buffer of exported bytes instead of copying it. */
|
|
12
11
|
function post(message) {
|
|
@@ -35,8 +34,16 @@ function run(t, { id, op, args }) {
|
|
|
35
34
|
return t[op].apply(t, args);
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
|
-
|
|
39
|
-
engine
|
|
37
|
+
let started;
|
|
38
|
+
/** The engine, loaded on the first request: by then worker.ts has applied the page's `assets`. */
|
|
39
|
+
function engine() {
|
|
40
|
+
if (!started) {
|
|
41
|
+
started = directTransport();
|
|
42
|
+
// Session 0 is the engine's own: font requests.
|
|
43
|
+
started.then((t) => t.listen(0, post), () => { });
|
|
44
|
+
}
|
|
45
|
+
return started;
|
|
46
|
+
}
|
|
40
47
|
onmessage = ({ data }) => {
|
|
41
48
|
if (data.op === 'abort') {
|
|
42
49
|
signals.get(data.id)?.abort();
|
|
@@ -44,7 +51,7 @@ onmessage = ({ data }) => {
|
|
|
44
51
|
}
|
|
45
52
|
// Every request waits on the same promise, so they reach the engine's queue in arrival order. Id 0 is a replayed
|
|
46
53
|
// library change: nothing waits for its reply.
|
|
47
|
-
engine.then((t) => run(t, data)).then((ok) => data.id && post({ id: data.id, ok }), (e) => {
|
|
54
|
+
engine().then((t) => run(t, data)).then((ok) => data.id && post({ id: data.id, ok }), (e) => {
|
|
48
55
|
if (!data.id)
|
|
49
56
|
return console.error(e);
|
|
50
57
|
const error = e instanceof EditorError ? e : new EditorError('internal', String(e), { cause: e });
|
package/dist/worker.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
// The page's engine worker entry (`@twip/web/worker.js`). The SDK starts it itself; applications never load it.
|
|
2
2
|
import './worker/entry.js';
|
|
3
|
+
import { configure } from './transport/module.js';
|
|
4
|
+
// The page's `assets` (configure()), in the query it started this worker with: the engine loads from there, not from
|
|
5
|
+
// this file's own URL. Applied before the first request, which loads the engine (worker/entry.ts).
|
|
6
|
+
const assets = new URL(import.meta.url).searchParams.get('assets');
|
|
7
|
+
if (assets)
|
|
8
|
+
configure({ assets });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twip/web",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-7dfb59b",
|
|
4
4
|
"description": "The Twip DOCX editor for the browser and Node: a managed, framework-independent editor over the WebAssembly engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|