@rsj-de/pdfmergesortsplit 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/README.md CHANGED
@@ -1,3 +1,126 @@
1
- # Temporary Holding Version
1
+ # @rsj-de/pdfmergesortsplit — RSJ PDF Merge, Sort & Split SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > The editor runs in test mode without a licence: everything works, a banner
4
+ > shows that the embedding is unlicensed. See
5
+ > [Licensing](https://pdfmerge.rsj.de/docs/licensing).
6
+
7
+ Embed the editor of <https://pdfmerge.rsj.de> in your own product. One iframe, a
8
+ small SDK, and documents that never leave the browser.
9
+
10
+ Two levels, same editor:
11
+
12
+ - **Low-level** `PDFMergeSortSplitClass` (`@rsj-de/pdfmergesortsplit`) — any framework.
13
+ - **High-level** `<PDFMergeSortSplit>` + `usePdfMergeSortSplit`
14
+ (`@rsj-de/pdfmergesortsplit/react`) — React, built on the class.
15
+
16
+ ```bash
17
+ pnpm add @rsj-de/pdfmergesortsplit
18
+ ```
19
+
20
+ ```js
21
+ import { PDFMergeSortSplitClass } from '@rsj-de/pdfmergesortsplit';
22
+
23
+ const editor = new PDFMergeSortSplitClass('#editor', {
24
+ embedUrl: 'https://pdfmerge.rsj.de', // or your self-hosted deployment
25
+ apiKey: '<licence JWT>', // removes the test-mode banner
26
+ title: 'Acme Document Suite',
27
+ ui: { toolbar: { print: false } },
28
+ actions: {
29
+ // Any number of buttons per injection point; the event carries the key.
30
+ saveAll: {
31
+ savePdf: { title: 'Export PDF' },
32
+ saveZip: { title: 'Export ZIP' },
33
+ },
34
+ },
35
+ });
36
+
37
+ await editor.whenReady();
38
+ await editor.loadDocument({ name: 'contract.pdf', content: file });
39
+ const pdf = await editor.exportDocument(0, 'pdf'); // part 0 → Blob
40
+
41
+ editor.on('action', async ({ action, part }) => {
42
+ if (action === 'savePdf') download(await editor.exportDocument(part));
43
+ if (action === 'saveZip') download(await editor.exportDocument(undefined, 'zip'));
44
+ });
45
+ ```
46
+
47
+ React:
48
+
49
+ ```jsx
50
+ import { PDFMergeSortSplit, usePdfMergeSortSplit } from '@rsj-de/pdfmergesortsplit/react';
51
+
52
+ function App() {
53
+ // The hook returns the editor's methods, bound to the instance it creates.
54
+ const { ref, exportDocument } = usePdfMergeSortSplit();
55
+
56
+ const handleAction = async ({ action, part }) => {
57
+ if (action === 'savePdf') download(await exportDocument(part, 'pdf'));
58
+ };
59
+
60
+ const handleExport = ({ fileName, blob }) => download(blob, fileName);
61
+
62
+ return (
63
+ <PDFMergeSortSplit
64
+ ref={ref}
65
+ config={{ apiKey }}
66
+ style={{ height: 640 }}
67
+ onAction={handleAction}
68
+ onExport={handleExport}
69
+ />
70
+ );
71
+ }
72
+ ```
73
+
74
+ ## API
75
+
76
+ | member | description |
77
+ |---|---|
78
+ | `new PDFMergeSortSplitClass(container, config)` | creates the iframe in `container` (element or selector) |
79
+ | `whenReady()` | resolves when the editor took the configuration |
80
+ | `setConfig(config)` | applies a new configuration to the running editor (no reload) |
81
+ | `loadDocument({name, content, provenance})` | adds a PDF (File, Blob, ArrayBuffer, base64, data URL) |
82
+ | `getPages()` | the pages with their per-page provenance |
83
+ | `exportDocument(reference?, mode?)` | one part (`"pdf"`) or everything (`"zip"`) → `Blob`; `reference` is a part index or name |
84
+ | `on("action" \| "export", handler)` | injected buttons and finished exports; returns an unsubscribe function |
85
+ | `destroy()` | removes the iframe |
86
+
87
+ ## Configuration
88
+
89
+ `config` is passed to the editor as it is:
90
+
91
+ - `embedUrl` — where the editor is served (default `https://pdfmerge.rsj.de`).
92
+ - `apiKey` — the licence JWT. Without one the embedded editor shows a
93
+ "test mode" banner and stays fully functional.
94
+ - `title`, `icon` — replace the title bar of the editor.
95
+ - `ui` — hide any combination of the title-bar and toolbar controls and the
96
+ part-header buttons (`ui.toolbar`, `ui.partHeader`); everything is shown by
97
+ default.
98
+ - `actions.loaders` / `actions.saveAll` / `actions.savePart` — buttons injected
99
+ into the editor. Their clicks arrive as `on("action", …)` with
100
+ `{action, where, reference, part}`.
101
+ - `provenance.attach` — write the page provenance into exported PDFs.
102
+
103
+ A running editor takes a new configuration with `setConfig(config)`; the React
104
+ component calls it whenever the `config` prop changes, so the open document
105
+ stays. Only a different `embedUrl` needs a new iframe.
106
+
107
+ The full schema is in `types/index.d.ts`.
108
+
109
+ ## Build and publish
110
+
111
+ ```bash
112
+ pnpm install --ignore-workspace # this package is not part of the app's workspace
113
+ pnpm run build # dist/index.js, dist/index.cjs, dist/react.*
114
+ npm publish # prepublishOnly builds again
115
+ ```
116
+
117
+ `react` is an optional peer dependency: the plain class does not import it.
118
+
119
+ ## Samples
120
+
121
+ `../sdksimple` (plain JavaScript) and `../sdksample` (React) both use this
122
+ package through `link:../sdk`, so build the SDK before starting a sample.
123
+
124
+ The published sources live in
125
+ <https://github.com/pdfmergesortsplit/pdfmergesortsplit> (mirrored from the
126
+ product repository on every release); issues go there.
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=`https://pdfmerge.rsj.de`,t=2e4,n=e=>typeof Blob<`u`&&e instanceof Blob||e instanceof ArrayBuffer||ArrayBuffer.isView(e),r=class{constructor(n,r={}){let i=typeof n==`string`?document.querySelector(n):n;if(!i)throw Error(`pdfmerge: container not found: ${String(n)}`);this.config=r??{},this.embedUrl=String(this.config.embedUrl||e).replace(/\/+$/,``),this.origin=new URL(this.embedUrl).origin,this.handlers=new Map,this.pending=new Map,this.requestId=0,this.readyDone=!1,this.destroyed=!1,this.iframe=document.createElement(`iframe`),this.iframe.src=`${this.embedUrl}/`,this.iframe.title=`PDF Merge, Sort & Split`,this.iframe.setAttribute(`allow`,`clipboard-read; clipboard-write; fullscreen`),this.iframe.setAttribute(`allowfullscreen`,`true`),this.iframe.style.cssText=`display:block;width:100%;height:100%;border:0`,i.appendChild(this.iframe),this.handleMessage=e=>this.receive(e),window.addEventListener(`message`,this.handleMessage);let a=this.config.readyTimeout??t;this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t,this.readyTimer=setTimeout(()=>{t(Error(`pdfmerge: the editor did not answer within ${a} ms`))},a)}),this.ready.catch(()=>{})}whenReady(){return this.ready}setConfig(e={}){this.config=e??{},this.send({op:`config`,config:this.config})}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}async loadDocument(e,t,r){let i=e&&typeof e==`object`&&!n(e)?e:{name:e,content:t,provenance:r},a=await this.request(`addPages`,`addPagesRsp`,{name:i.name||`document.pdf`,content:i.content,provenance:i.provenance});if(a?.ok===!1)throw Error(`pdfmerge: loading failed: ${a.error}`);return a}async getPages(){return(await this.request(`pages`,`pagesRsp`))?.pages??[]}async exportDocument(e,t=`pdf`){let n=Number.isInteger(e),r=await this.request(`export`,`exportRsp`,{mode:t,part:n?e:void 0,reference:n?void 0:e});if(r?.error)throw Error(`pdfmerge: export failed: ${r.error}`);let i=new Blob([r.bytes],{type:r.mimeType||`application/pdf`});return this.emit(`export`,{reference:e,mode:t,fileName:r.fileName,pages:r.pages??[],blob:i}),i}destroy(){if(!this.destroyed){this.destroyed=!0,window.removeEventListener(`message`,this.handleMessage),clearTimeout(this.readyTimer);for(let[,e]of this.pending)clearTimeout(e.timer),e.reject(Error(`pdfmerge: the editor was destroyed`));this.pending.clear(),this.iframe.remove()}}send(e){this.iframe.contentWindow?.postMessage({partner:`pdfmerge`,...e},this.origin)}request(e,n,r={},i=t){let a=++this.requestId;return new Promise((t,o)=>{if(this.destroyed){o(Error(`pdfmerge: the editor was destroyed`));return}let s=setTimeout(()=>{this.pending.delete(a),o(Error(`pdfmerge: "${e}" timed out after ${i} ms`))},i);this.pending.set(a,{op:n,resolve:t,reject:o,timer:s}),this.send({op:e,id:a,...r})})}receive(e){if(e.origin!==this.origin||e.source!==this.iframe.contentWindow)return;let t=e.data;if(t&&typeof t==`object`)switch(t.op){case`init`:this.clearReadyTimer(),this.send({op:`initRsp`,config:this.config}),this.readyDone||(this.readyDone=!0,this.resolveReady(this));return;case`licenseRequest`:this.config.apiKey&&this.send({op:`licenseRsp`,token:this.config.apiKey});return;case`licenseStatus`:this.emit(`license`,{licensed:!!t.licensed,reason:t.reason});return;case`action`:this.emit(`action`,{action:t.action,where:t.where,reference:t.reference,part:t.part});return;case`pagesRsp`:case`exportRsp`:case`addPagesRsp`:case`addFilesRsp`:{let e=t.id===void 0?void 0:this.pending.get(t.id);e&&e.op===t.op&&(clearTimeout(e.timer),this.pending.delete(t.id),e.resolve(t));return}default:return}}emit(e,t){for(let n of this.handlers.get(e)??[])try{n(t)}catch(e){console.error(`pdfmerge: event handler failed`,e)}}clearReadyTimer(){this.readyTimer&&=(clearTimeout(this.readyTimer),null)}};exports.PDFMergeSortSplitClass=r,exports.default=r;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["DEFAULT_EMBED_URL","DEFAULT_TIMEOUT","isBinary","value","PDFMergeSortSplitClass","element","container","config","event","timeout","resolve","reject","handler","options","nameOrOptions","answer","byIndex","reference","blob","entry","message","id","timer","op","responseOp","payload","data","error"],"sources":["../src/index.js"],"sourcesContent":["/**\n * RSJ PDF Merge, Sort & Split - SDK\n *\n * The editor runs in an iframe (hosted by RSJ, or by you when self-hosting).\n * This class owns that iframe and the postMessage protocol between the two:\n *\n * app -> host {op: \"init\", version} the editor is up\n * host -> app {op: \"initRsp\", config} title, icon, hidden UI, actions, apiKey\n * app -> host {op: \"licenseRequest\"} framed and not licensed yet: asks for a licence\n * host -> app {op: \"licenseRsp\", token} the licence (config.apiKey), or no token\n * app -> host {op: \"licenseStatus\", licensed, reason} the verdict\n * host -> app {op: \"addFiles\", data} File/Blob/base64/data URL -> addFilesRsp\n * host -> app {op: \"addPages\", name, content, provenance} -> addPagesRsp\n * host -> app {op: \"pages\"} -> pagesRsp\n * host -> app {op: \"export\", mode, part|reference} -> exportRsp\n * app -> host {op: \"action\", action, where, reference, part} a button the host configured\n *\n * `action` is the key of the button in `config.actions`, `where` the injection\n * point it was configured in; an injection point holds any number of buttons.\n *\n * Every request carries an `id` that the answer echoes, so concurrent calls do\n * not get mixed up.\n */\n\nconst DEFAULT_EMBED_URL = 'https://pdfmerge.rsj.de';\nconst DEFAULT_TIMEOUT = 20000;\n\n/** Requests whose payload is the document itself, not options. */\nconst isBinary = (value) =>\n (typeof Blob !== 'undefined' && value instanceof Blob) ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value);\n\nexport class PDFMergeSortSplitClass {\n /**\n * @param {string|HTMLElement} container element (or selector) that receives the iframe\n * @param {object} config `{ embedUrl, apiKey, title, icon, ui, actions, provenance, … }`\n */\n constructor(container, config = {}) {\n const element = typeof container === 'string' ? document.querySelector(container) : container;\n\n if (!element) {\n throw new Error(`pdfmerge: container not found: ${String(container)}`);\n }\n\n this.config = config ?? {};\n this.embedUrl = String(this.config.embedUrl || DEFAULT_EMBED_URL).replace(/\\/+$/, '');\n this.origin = new URL(this.embedUrl).origin;\n this.handlers = new Map();\n this.pending = new Map();\n this.requestId = 0;\n this.readyDone = false;\n this.destroyed = false;\n\n this.iframe = document.createElement('iframe');\n this.iframe.src = `${this.embedUrl}/`;\n this.iframe.title = 'PDF Merge, Sort & Split';\n this.iframe.setAttribute('allow', 'clipboard-read; clipboard-write; fullscreen');\n this.iframe.setAttribute('allowfullscreen', 'true');\n this.iframe.style.cssText = 'display:block;width:100%;height:100%;border:0';\n element.appendChild(this.iframe);\n\n this.handleMessage = (event) => this.receive(event);\n window.addEventListener('message', this.handleMessage);\n\n const timeout = this.config.readyTimeout ?? DEFAULT_TIMEOUT;\n this.ready = new Promise((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n this.readyTimer = setTimeout(() => {\n reject(new Error(`pdfmerge: the editor did not answer within ${timeout} ms`));\n }, timeout);\n });\n // Nobody has to await it; an unhandled rejection would only be noise.\n this.ready.catch(() => {});\n }\n\n /** Resolves once the editor has taken our configuration. */\n whenReady() {\n return this.ready;\n }\n\n /**\n * Replace the configuration on the running editor.\n *\n * The editor applies it immediately - title, icon, hidden controls, injected\n * buttons, provenance and the licence token - without reloading the iframe.\n * `embedUrl` is the exception: it is the iframe's `src`, so changing it needs\n * a new iframe (the React component does that for you).\n */\n setConfig(config = {}) {\n this.config = config ?? {};\n this.send({ op: 'config', config: this.config });\n }\n\n /** Subscribe to \"action\" / \"export\"; returns an unsubscribe function. */\n on(event, handler) {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event).add(handler);\n\n return () => this.off(event, handler);\n }\n\n /** Remove a handler added with `on`. */\n off(event, handler) {\n this.handlers.get(event)?.delete(handler);\n }\n\n /**\n * Add a PDF to the editor.\n *\n * `loadDocument({name, content, provenance})` or `loadDocument(name, content)`.\n * `content` may be a File, a Blob, an ArrayBuffer, a Uint8Array, base64 or a\n * data URL; `provenance` is one entry per page (any JSON).\n */\n async loadDocument(nameOrOptions, content, provenance) {\n const options =\n nameOrOptions && typeof nameOrOptions === 'object' && !isBinary(nameOrOptions)\n ? nameOrOptions\n : { name: nameOrOptions, content, provenance };\n\n const answer = await this.request('addPages', 'addPagesRsp', {\n name: options.name || 'document.pdf',\n content: options.content,\n provenance: options.provenance,\n });\n\n if (answer?.ok === false) {\n throw new Error(`pdfmerge: loading failed: ${answer.error}`);\n }\n\n return answer;\n }\n\n /** The current pages, each with its provenance. */\n async getPages() {\n const answer = await this.request('pages', 'pagesRsp');\n return answer?.pages ?? [];\n }\n\n /**\n * Export a part as a PDF, or every part as a ZIP (`mode: \"zip\"`).\n *\n * `reference` is a part index or a part name; without it every page goes into\n * one document. Resolves with a Blob and emits \"export\".\n */\n async exportDocument(reference, mode = 'pdf') {\n const byIndex = Number.isInteger(reference);\n const answer = await this.request('export', 'exportRsp', {\n mode,\n part: byIndex ? reference : undefined,\n reference: byIndex ? undefined : reference,\n });\n\n if (answer?.error) {\n throw new Error(`pdfmerge: export failed: ${answer.error}`);\n }\n\n const blob = new Blob([answer.bytes], { type: answer.mimeType || 'application/pdf' });\n this.emit('export', {\n reference,\n mode,\n fileName: answer.fileName,\n pages: answer.pages ?? [],\n blob,\n });\n\n return blob;\n }\n\n /** Remove the iframe and stop listening. */\n destroy() {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n window.removeEventListener('message', this.handleMessage);\n clearTimeout(this.readyTimer);\n\n for (const [, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(new Error('pdfmerge: the editor was destroyed'));\n }\n this.pending.clear();\n\n this.iframe.remove();\n }\n\n /** postMessage into the iframe. */\n send(message) {\n this.iframe.contentWindow?.postMessage({ partner: 'pdfmerge', ...message }, this.origin);\n }\n\n /** Send a request and wait for the answer that echoes its id. */\n request(op, responseOp, payload = {}, timeout = DEFAULT_TIMEOUT) {\n const id = ++this.requestId;\n\n return new Promise((resolve, reject) => {\n if (this.destroyed) {\n reject(new Error('pdfmerge: the editor was destroyed'));\n return;\n }\n\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`pdfmerge: \"${op}\" timed out after ${timeout} ms`));\n }, timeout);\n\n this.pending.set(id, { op: responseOp, resolve, reject, timer });\n this.send({ op, id, ...payload });\n });\n }\n\n receive(event) {\n if (event.origin !== this.origin) {\n return;\n }\n if (event.source !== this.iframe.contentWindow) {\n return;\n }\n\n const data = event.data;\n if (!data || typeof data !== 'object') {\n return;\n }\n\n switch (data.op) {\n case 'init':\n this.clearReadyTimer();\n this.send({ op: 'initRsp', config: this.config });\n if (!this.readyDone) {\n this.readyDone = true;\n this.resolveReady(this);\n }\n return;\n\n case 'licenseRequest':\n // The editor is framed and tainted: answer with the licence it may use.\n // Without `apiKey` there is nothing to send and it stays in test mode.\n if (this.config.apiKey) {\n this.send({ op: 'licenseRsp', token: this.config.apiKey });\n }\n return;\n\n case 'licenseStatus':\n // The editor tells us whether the licence was accepted, so a host can\n // show the state instead of guessing.\n this.emit('license', { licensed: Boolean(data.licensed), reason: data.reason });\n return;\n\n case 'action':\n this.emit('action', {\n action: data.action,\n where: data.where,\n reference: data.reference,\n part: data.part,\n });\n return;\n\n case 'pagesRsp':\n case 'exportRsp':\n case 'addPagesRsp':\n case 'addFilesRsp': {\n const entry = data.id === undefined ? undefined : this.pending.get(data.id);\n\n if (entry && entry.op === data.op) {\n clearTimeout(entry.timer);\n this.pending.delete(data.id);\n entry.resolve(data);\n }\n return;\n }\n\n default:\n return;\n }\n }\n\n emit(event, payload) {\n for (const handler of this.handlers.get(event) ?? []) {\n try {\n handler(payload);\n } catch (error) {\n // A failing consumer handler must not break the message loop.\n console.error('pdfmerge: event handler failed', error);\n }\n }\n }\n\n clearReadyTimer() {\n if (this.readyTimer) {\n clearTimeout(this.readyTimer);\n this.readyTimer = null;\n }\n }\n}\n\nexport default PDFMergeSortSplitClass;\n"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,150 @@
1
+ // src/index.js
2
+ var e = 2e4, t = (e) => typeof Blob < "u" && e instanceof Blob || e instanceof ArrayBuffer || ArrayBuffer.isView(e), n = class {
3
+ constructor(t, n = {}) {
4
+ let r = typeof t == "string" ? document.querySelector(t) : t;
5
+ if (!r) throw Error(`pdfmerge: container not found: ${String(t)}`);
6
+ this.config = n ?? {}, this.embedUrl = String(this.config.embedUrl || "https://pdfmerge.rsj.de").replace(/\/+$/, ""), this.origin = new URL(this.embedUrl).origin, this.handlers = /* @__PURE__ */ new Map(), this.pending = /* @__PURE__ */ new Map(), this.requestId = 0, this.readyDone = !1, this.destroyed = !1, this.iframe = document.createElement("iframe"), this.iframe.src = `${this.embedUrl}/`, this.iframe.title = "PDF Merge, Sort & Split", this.iframe.setAttribute("allow", "clipboard-read; clipboard-write; fullscreen"), this.iframe.setAttribute("allowfullscreen", "true"), this.iframe.style.cssText = "display:block;width:100%;height:100%;border:0", r.appendChild(this.iframe), this.handleMessage = (e) => this.receive(e), window.addEventListener("message", this.handleMessage);
7
+ let i = this.config.readyTimeout ?? e;
8
+ this.ready = new Promise((e, t) => {
9
+ this.resolveReady = e, this.rejectReady = t, this.readyTimer = setTimeout(() => {
10
+ t(/* @__PURE__ */ Error(`pdfmerge: the editor did not answer within ${i} ms`));
11
+ }, i);
12
+ }), this.ready.catch(() => {});
13
+ }
14
+ whenReady() {
15
+ return this.ready;
16
+ }
17
+ setConfig(e = {}) {
18
+ this.config = e ?? {}, this.send({
19
+ op: "config",
20
+ config: this.config
21
+ });
22
+ }
23
+ on(e, t) {
24
+ return this.handlers.has(e) || this.handlers.set(e, /* @__PURE__ */ new Set()), this.handlers.get(e).add(t), () => this.off(e, t);
25
+ }
26
+ off(e, t) {
27
+ this.handlers.get(e)?.delete(t);
28
+ }
29
+ async loadDocument(e, n, r) {
30
+ let i = e && typeof e == "object" && !t(e) ? e : {
31
+ name: e,
32
+ content: n,
33
+ provenance: r
34
+ }, a = await this.request("addPages", "addPagesRsp", {
35
+ name: i.name || "document.pdf",
36
+ content: i.content,
37
+ provenance: i.provenance
38
+ });
39
+ if (a?.ok === !1) throw Error(`pdfmerge: loading failed: ${a.error}`);
40
+ return a;
41
+ }
42
+ async getPages() {
43
+ return (await this.request("pages", "pagesRsp"))?.pages ?? [];
44
+ }
45
+ async exportDocument(e, t = "pdf") {
46
+ let n = Number.isInteger(e), r = await this.request("export", "exportRsp", {
47
+ mode: t,
48
+ part: n ? e : void 0,
49
+ reference: n ? void 0 : e
50
+ });
51
+ if (r?.error) throw Error(`pdfmerge: export failed: ${r.error}`);
52
+ let i = new Blob([r.bytes], { type: r.mimeType || "application/pdf" });
53
+ return this.emit("export", {
54
+ reference: e,
55
+ mode: t,
56
+ fileName: r.fileName,
57
+ pages: r.pages ?? [],
58
+ blob: i
59
+ }), i;
60
+ }
61
+ destroy() {
62
+ if (!this.destroyed) {
63
+ this.destroyed = !0, window.removeEventListener("message", this.handleMessage), clearTimeout(this.readyTimer);
64
+ for (let [, e] of this.pending) clearTimeout(e.timer), e.reject(/* @__PURE__ */ Error("pdfmerge: the editor was destroyed"));
65
+ this.pending.clear(), this.iframe.remove();
66
+ }
67
+ }
68
+ send(e) {
69
+ this.iframe.contentWindow?.postMessage({
70
+ partner: "pdfmerge",
71
+ ...e
72
+ }, this.origin);
73
+ }
74
+ request(t, n, r = {}, i = e) {
75
+ let a = ++this.requestId;
76
+ return new Promise((e, o) => {
77
+ if (this.destroyed) {
78
+ o(/* @__PURE__ */ Error("pdfmerge: the editor was destroyed"));
79
+ return;
80
+ }
81
+ let s = setTimeout(() => {
82
+ this.pending.delete(a), o(/* @__PURE__ */ Error(`pdfmerge: "${t}" timed out after ${i} ms`));
83
+ }, i);
84
+ this.pending.set(a, {
85
+ op: n,
86
+ resolve: e,
87
+ reject: o,
88
+ timer: s
89
+ }), this.send({
90
+ op: t,
91
+ id: a,
92
+ ...r
93
+ });
94
+ });
95
+ }
96
+ receive(e) {
97
+ if (e.origin !== this.origin || e.source !== this.iframe.contentWindow) return;
98
+ let t = e.data;
99
+ if (t && typeof t == "object") switch (t.op) {
100
+ case "init":
101
+ this.clearReadyTimer(), this.send({
102
+ op: "initRsp",
103
+ config: this.config
104
+ }), this.readyDone || (this.readyDone = !0, this.resolveReady(this));
105
+ return;
106
+ case "licenseRequest":
107
+ this.config.apiKey && this.send({
108
+ op: "licenseRsp",
109
+ token: this.config.apiKey
110
+ });
111
+ return;
112
+ case "licenseStatus":
113
+ this.emit("license", {
114
+ licensed: !!t.licensed,
115
+ reason: t.reason
116
+ });
117
+ return;
118
+ case "action":
119
+ this.emit("action", {
120
+ action: t.action,
121
+ where: t.where,
122
+ reference: t.reference,
123
+ part: t.part
124
+ });
125
+ return;
126
+ case "pagesRsp":
127
+ case "exportRsp":
128
+ case "addPagesRsp":
129
+ case "addFilesRsp": {
130
+ let e = t.id === void 0 ? void 0 : this.pending.get(t.id);
131
+ e && e.op === t.op && (clearTimeout(e.timer), this.pending.delete(t.id), e.resolve(t));
132
+ return;
133
+ }
134
+ default: return;
135
+ }
136
+ }
137
+ emit(e, t) {
138
+ for (let n of this.handlers.get(e) ?? []) try {
139
+ n(t);
140
+ } catch (e) {
141
+ console.error("pdfmerge: event handler failed", e);
142
+ }
143
+ }
144
+ clearReadyTimer() {
145
+ this.readyTimer &&= (clearTimeout(this.readyTimer), null);
146
+ }
147
+ };
148
+ export { n as PDFMergeSortSplitClass, n as default };
149
+
150
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["DEFAULT_TIMEOUT","isBinary","value","PDFMergeSortSplitClass","element","container","config","event","timeout","resolve","reject","handler","options","nameOrOptions","answer","byIndex","reference","blob","entry","message","id","timer","op","responseOp","payload","data","error"],"sources":["../src/index.js"],"sourcesContent":["/**\n * RSJ PDF Merge, Sort & Split - SDK\n *\n * The editor runs in an iframe (hosted by RSJ, or by you when self-hosting).\n * This class owns that iframe and the postMessage protocol between the two:\n *\n * app -> host {op: \"init\", version} the editor is up\n * host -> app {op: \"initRsp\", config} title, icon, hidden UI, actions, apiKey\n * app -> host {op: \"licenseRequest\"} framed and not licensed yet: asks for a licence\n * host -> app {op: \"licenseRsp\", token} the licence (config.apiKey), or no token\n * app -> host {op: \"licenseStatus\", licensed, reason} the verdict\n * host -> app {op: \"addFiles\", data} File/Blob/base64/data URL -> addFilesRsp\n * host -> app {op: \"addPages\", name, content, provenance} -> addPagesRsp\n * host -> app {op: \"pages\"} -> pagesRsp\n * host -> app {op: \"export\", mode, part|reference} -> exportRsp\n * app -> host {op: \"action\", action, where, reference, part} a button the host configured\n *\n * `action` is the key of the button in `config.actions`, `where` the injection\n * point it was configured in; an injection point holds any number of buttons.\n *\n * Every request carries an `id` that the answer echoes, so concurrent calls do\n * not get mixed up.\n */\n\nconst DEFAULT_EMBED_URL = 'https://pdfmerge.rsj.de';\nconst DEFAULT_TIMEOUT = 20000;\n\n/** Requests whose payload is the document itself, not options. */\nconst isBinary = (value) =>\n (typeof Blob !== 'undefined' && value instanceof Blob) ||\n value instanceof ArrayBuffer ||\n ArrayBuffer.isView(value);\n\nexport class PDFMergeSortSplitClass {\n /**\n * @param {string|HTMLElement} container element (or selector) that receives the iframe\n * @param {object} config `{ embedUrl, apiKey, title, icon, ui, actions, provenance, … }`\n */\n constructor(container, config = {}) {\n const element = typeof container === 'string' ? document.querySelector(container) : container;\n\n if (!element) {\n throw new Error(`pdfmerge: container not found: ${String(container)}`);\n }\n\n this.config = config ?? {};\n this.embedUrl = String(this.config.embedUrl || DEFAULT_EMBED_URL).replace(/\\/+$/, '');\n this.origin = new URL(this.embedUrl).origin;\n this.handlers = new Map();\n this.pending = new Map();\n this.requestId = 0;\n this.readyDone = false;\n this.destroyed = false;\n\n this.iframe = document.createElement('iframe');\n this.iframe.src = `${this.embedUrl}/`;\n this.iframe.title = 'PDF Merge, Sort & Split';\n this.iframe.setAttribute('allow', 'clipboard-read; clipboard-write; fullscreen');\n this.iframe.setAttribute('allowfullscreen', 'true');\n this.iframe.style.cssText = 'display:block;width:100%;height:100%;border:0';\n element.appendChild(this.iframe);\n\n this.handleMessage = (event) => this.receive(event);\n window.addEventListener('message', this.handleMessage);\n\n const timeout = this.config.readyTimeout ?? DEFAULT_TIMEOUT;\n this.ready = new Promise((resolve, reject) => {\n this.resolveReady = resolve;\n this.rejectReady = reject;\n this.readyTimer = setTimeout(() => {\n reject(new Error(`pdfmerge: the editor did not answer within ${timeout} ms`));\n }, timeout);\n });\n // Nobody has to await it; an unhandled rejection would only be noise.\n this.ready.catch(() => {});\n }\n\n /** Resolves once the editor has taken our configuration. */\n whenReady() {\n return this.ready;\n }\n\n /**\n * Replace the configuration on the running editor.\n *\n * The editor applies it immediately - title, icon, hidden controls, injected\n * buttons, provenance and the licence token - without reloading the iframe.\n * `embedUrl` is the exception: it is the iframe's `src`, so changing it needs\n * a new iframe (the React component does that for you).\n */\n setConfig(config = {}) {\n this.config = config ?? {};\n this.send({ op: 'config', config: this.config });\n }\n\n /** Subscribe to \"action\" / \"export\"; returns an unsubscribe function. */\n on(event, handler) {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event).add(handler);\n\n return () => this.off(event, handler);\n }\n\n /** Remove a handler added with `on`. */\n off(event, handler) {\n this.handlers.get(event)?.delete(handler);\n }\n\n /**\n * Add a PDF to the editor.\n *\n * `loadDocument({name, content, provenance})` or `loadDocument(name, content)`.\n * `content` may be a File, a Blob, an ArrayBuffer, a Uint8Array, base64 or a\n * data URL; `provenance` is one entry per page (any JSON).\n */\n async loadDocument(nameOrOptions, content, provenance) {\n const options =\n nameOrOptions && typeof nameOrOptions === 'object' && !isBinary(nameOrOptions)\n ? nameOrOptions\n : { name: nameOrOptions, content, provenance };\n\n const answer = await this.request('addPages', 'addPagesRsp', {\n name: options.name || 'document.pdf',\n content: options.content,\n provenance: options.provenance,\n });\n\n if (answer?.ok === false) {\n throw new Error(`pdfmerge: loading failed: ${answer.error}`);\n }\n\n return answer;\n }\n\n /** The current pages, each with its provenance. */\n async getPages() {\n const answer = await this.request('pages', 'pagesRsp');\n return answer?.pages ?? [];\n }\n\n /**\n * Export a part as a PDF, or every part as a ZIP (`mode: \"zip\"`).\n *\n * `reference` is a part index or a part name; without it every page goes into\n * one document. Resolves with a Blob and emits \"export\".\n */\n async exportDocument(reference, mode = 'pdf') {\n const byIndex = Number.isInteger(reference);\n const answer = await this.request('export', 'exportRsp', {\n mode,\n part: byIndex ? reference : undefined,\n reference: byIndex ? undefined : reference,\n });\n\n if (answer?.error) {\n throw new Error(`pdfmerge: export failed: ${answer.error}`);\n }\n\n const blob = new Blob([answer.bytes], { type: answer.mimeType || 'application/pdf' });\n this.emit('export', {\n reference,\n mode,\n fileName: answer.fileName,\n pages: answer.pages ?? [],\n blob,\n });\n\n return blob;\n }\n\n /** Remove the iframe and stop listening. */\n destroy() {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n window.removeEventListener('message', this.handleMessage);\n clearTimeout(this.readyTimer);\n\n for (const [, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(new Error('pdfmerge: the editor was destroyed'));\n }\n this.pending.clear();\n\n this.iframe.remove();\n }\n\n /** postMessage into the iframe. */\n send(message) {\n this.iframe.contentWindow?.postMessage({ partner: 'pdfmerge', ...message }, this.origin);\n }\n\n /** Send a request and wait for the answer that echoes its id. */\n request(op, responseOp, payload = {}, timeout = DEFAULT_TIMEOUT) {\n const id = ++this.requestId;\n\n return new Promise((resolve, reject) => {\n if (this.destroyed) {\n reject(new Error('pdfmerge: the editor was destroyed'));\n return;\n }\n\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`pdfmerge: \"${op}\" timed out after ${timeout} ms`));\n }, timeout);\n\n this.pending.set(id, { op: responseOp, resolve, reject, timer });\n this.send({ op, id, ...payload });\n });\n }\n\n receive(event) {\n if (event.origin !== this.origin) {\n return;\n }\n if (event.source !== this.iframe.contentWindow) {\n return;\n }\n\n const data = event.data;\n if (!data || typeof data !== 'object') {\n return;\n }\n\n switch (data.op) {\n case 'init':\n this.clearReadyTimer();\n this.send({ op: 'initRsp', config: this.config });\n if (!this.readyDone) {\n this.readyDone = true;\n this.resolveReady(this);\n }\n return;\n\n case 'licenseRequest':\n // The editor is framed and tainted: answer with the licence it may use.\n // Without `apiKey` there is nothing to send and it stays in test mode.\n if (this.config.apiKey) {\n this.send({ op: 'licenseRsp', token: this.config.apiKey });\n }\n return;\n\n case 'licenseStatus':\n // The editor tells us whether the licence was accepted, so a host can\n // show the state instead of guessing.\n this.emit('license', { licensed: Boolean(data.licensed), reason: data.reason });\n return;\n\n case 'action':\n this.emit('action', {\n action: data.action,\n where: data.where,\n reference: data.reference,\n part: data.part,\n });\n return;\n\n case 'pagesRsp':\n case 'exportRsp':\n case 'addPagesRsp':\n case 'addFilesRsp': {\n const entry = data.id === undefined ? undefined : this.pending.get(data.id);\n\n if (entry && entry.op === data.op) {\n clearTimeout(entry.timer);\n this.pending.delete(data.id);\n entry.resolve(data);\n }\n return;\n }\n\n default:\n return;\n }\n }\n\n emit(event, payload) {\n for (const handler of this.handlers.get(event) ?? []) {\n try {\n handler(payload);\n } catch (error) {\n // A failing consumer handler must not break the message loop.\n console.error('pdfmerge: event handler failed', error);\n }\n }\n }\n\n clearReadyTimer() {\n if (this.readyTimer) {\n clearTimeout(this.readyTimer);\n this.readyTimer = null;\n }\n }\n}\n\nexport default PDFMergeSortSplitClass;\n"],"mappings":"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"}
package/dist/react.cjs ADDED
@@ -0,0 +1,2 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));const c=require("./index.cjs");let l=require("react");l=s(l,1);let u=require("react/jsx-runtime");var d=()=>{let e=(0,l.useRef)(null);return{ref:e,loadDocument:(0,l.useCallback)(t=>e.current.loadDocument(t),[]),exportDocument:(0,l.useCallback)((t,n)=>e.current.exportDocument(t,n),[]),getPages:(0,l.useCallback)(()=>e.current.getPages(),[])}},f=(0,l.forwardRef)(function({config:e,style:t,className:n,onAction:r,onExport:i,onLicense:a,onReady:o},s){let d=(0,l.useRef)(null),f=(0,l.useRef)(null),p=(0,l.useRef)({onAction:r,onExport:i,onLicense:a,onReady:o});p.current={onAction:r,onExport:i,onLicense:a,onReady:o};let m=(0,l.useRef)(e);m.current=e;let h=(0,l.useRef)(null),g=e?.embedUrl;return(0,l.useEffect)(()=>{let e=m.current,t=new c.PDFMergeSortSplitClass(d.current,e);f.current=t,h.current=JSON.stringify(e??{}),s&&(s.current=t),t.whenReady().then(e=>p.current.onReady?.(e)).catch(()=>{});let n=t.on(`action`,e=>p.current.onAction?.(e)),r=t.on(`export`,e=>p.current.onExport?.(e)),i=t.on(`license`,e=>p.current.onLicense?.(e));return()=>{n(),r(),i(),t.destroy(),f.current=null,s&&(s.current=null)}},[g]),(0,l.useEffect)(()=>{let t=f.current;if(!t)return;let n=JSON.stringify(e??{});n!==h.current&&(h.current=n,t.setConfig(e))},[e]),(0,u.jsx)(`div`,{ref:d,className:n,style:{width:`100%`,height:`100%`,...t}})});exports.PDFMergeSortSplit=f,exports.default=f,exports.PDFMergeSortSplitClass=c.PDFMergeSortSplitClass,exports.usePdfMergeSortSplit=d;
2
+ //# sourceMappingURL=react.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.cjs","names":["useRef","useCallback","forwardRef","PDFMergeSortSplitClass","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","require_index","react","react_jsx_runtime","usePdfMergeSortSplit","ref","document","reference","mode","PDFMergeSortSplit","container","instance","handlers","configRef","config","applied","embedUrl","current","created","ready","offAction","payload","offExport","offLicense","json","style"],"sources":["../src/react.jsx"],"sourcesContent":["import React, { forwardRef, useCallback, useEffect, useRef } from 'react';\n\nimport { PDFMergeSortSplitClass } from './index.js';\n\n/**\n * The editor's methods, bound to the instance the component creates.\n *\n * ```jsx\n * const { ref, loadDocument, exportDocument, getPages } = usePdfMergeSortSplit();\n *\n * const handleAction = async ({ action }) => {\n * if (action === 'saveZip') download(await exportDocument(undefined, 'zip'));\n * };\n *\n * <PDFMergeSortSplit ref={ref} config={{ apiKey }} onAction={handleAction} />\n * ```\n *\n * The returned functions are stable, so callers need no `useCallback` or\n * `useMemo` wrapper; `ref` is only needed for the escape hatches\n * (`whenReady`, `on`, `destroy`) and to pass to the component.\n */\nexport const usePdfMergeSortSplit = () => {\n const ref = useRef(null);\n\n const loadDocument = useCallback(\n (document) => ref.current.loadDocument(document),\n [],\n );\n const exportDocument = useCallback(\n (reference, mode) => ref.current.exportDocument(reference, mode),\n [],\n );\n const getPages = useCallback(() => ref.current.getPages(), []);\n\n return { ref, loadDocument, exportDocument, getPages };\n};\n\n/**\n * The editor as a React component.\n *\n * The iframe is created once and lives for the lifetime of the component:\n * changing `onAction` / `onExport` / `onReady` does not reload it, and a new\n * `config` is applied to the running editor (`setConfig`) instead of rebuilding\n * it, so the open document stays. Only a different `config.embedUrl` needs a\n * new iframe, which the component creates itself.\n */\nexport const PDFMergeSortSplit = forwardRef(function PDFMergeSortSplit(\n { config, style, className, onAction, onExport, onLicense, onReady },\n ref,\n) {\n const container = useRef(null);\n const instance = useRef(null);\n const handlers = useRef({ onAction, onExport, onLicense, onReady });\n handlers.current = { onAction, onExport, onLicense, onReady };\n\n // The effect that creates the iframe needs the config of the render it runs\n // in, but must not re-run for every new config object.\n const configRef = useRef(config);\n configRef.current = config;\n\n // What the running editor already knows. `config={{ … }}` is a new object on\n // every render, so the comparison is by content, not by reference.\n const applied = useRef(null);\n\n // `embedUrl` is the iframe's src: only that needs a new iframe.\n const embedUrl = config?.embedUrl;\n\n useEffect(() => {\n const current = configRef.current;\n const created = new PDFMergeSortSplitClass(container.current, current);\n instance.current = created;\n applied.current = JSON.stringify(current ?? {});\n\n if (ref) {\n ref.current = created;\n }\n\n created\n .whenReady()\n .then((ready) => handlers.current.onReady?.(ready))\n .catch(() => {\n // `ready` rejects when the editor never answers; onReady is not called.\n });\n\n const offAction = created.on('action', (payload) =>\n handlers.current.onAction?.(payload),\n );\n const offExport = created.on('export', (payload) =>\n handlers.current.onExport?.(payload),\n );\n const offLicense = created.on('license', (payload) =>\n handlers.current.onLicense?.(payload),\n );\n\n return () => {\n offAction();\n offExport();\n offLicense();\n created.destroy();\n instance.current = null;\n\n if (ref) {\n ref.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [embedUrl]);\n\n // Everything except `embedUrl` is applied to the running editor, so a config\n // change does not reload the iframe and the open document stays.\n useEffect(() => {\n const current = instance.current;\n if (!current) {\n return;\n }\n\n const json = JSON.stringify(config ?? {});\n if (json === applied.current) {\n return;\n }\n applied.current = json;\n\n current.setConfig(config);\n }, [config]);\n\n return (\n <div\n ref={container}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n});\n\nexport { PDFMergeSortSplitClass } from './index.js';\nexport default PDFMergeSortSplit;\n"],"mappings":"A,qrBAqBA,IAAa2B,MAA6B,CACxC,IAAMC,GAAA,EAAM5B,EAAAA,OAAAA,CAAO,IAAI,EAYvB,MAAO,CAAE,MAAK,cAAA,EAVOC,EAAAA,YAAAA,CAClB,GAAa2B,EAAI,QAAQ,aAAaC,CAAQ,EAC/C,CAAC,CAQW,EAAc,gBAAA,EANL5B,EAAAA,YAAAA,EACpB,EAAW,IAAS2B,EAAI,QAAQ,eAAeE,EAAWC,CAAI,EAC/D,CAAC,CAIyB,EAAgB,UAAA,EAF3B9B,EAAAA,YAAAA,KAAkB2B,EAAI,QAAQ,SAAS,EAAG,CAAC,CAEhB,CAAS,CACvD,EAWaI,GAAA,EAAoB9B,EAAAA,WAAAA,CAAW,SAC1C,CAAE,SAAQ,QAAO,YAAW,WAAU,WAAU,YAAW,WAC3D,EACA,CACA,IAAM+B,GAAA,EAAYjC,EAAAA,OAAAA,CAAO,IAAI,EACvBkC,GAAA,EAAWlC,EAAAA,OAAAA,CAAO,IAAI,EACtBmC,GAAA,EAAWnC,EAAAA,OAAAA,CAAO,CAAE,WAAU,WAAU,YAAW,SAAQ,CAAC,EAClE,EAAS,QAAU,CAAE,WAAU,WAAU,YAAW,SAAQ,EAI5D,IAAMoC,GAAA,EAAYpC,EAAAA,OAAAA,CAAOqC,CAAM,EAC/B,EAAU,QAAUA,EAIpB,IAAMC,GAAA,EAAUtC,EAAAA,OAAAA,CAAO,IAAI,EAGrBuC,EAAWF,GAAQ,SA4DzB,OA1DA,EAAAZ,EAAA,UAAA,KAAgB,CACd,IAAMe,EAAUJ,EAAU,QACpBK,EAAU,IAAItC,EAAAA,uBAAuB8B,EAAU,QAASO,CAAO,EACrE,EAAS,QAAUC,EACnB,EAAQ,QAAU,KAAK,UAAUD,GAAW,CAAC,CAAC,EAE1CZ,IACF,EAAI,QAAUa,GAGhBA,EACG,UAAU,CAAC,CACX,KAAM,GAAUN,EAAS,QAAQ,UAAUO,CAAK,CAAC,CAAC,CAClD,UAAY,CAEb,CAAC,EAEH,IAAMC,EAAYF,EAAQ,GAAG,SAAW,GACtCN,EAAS,QAAQ,WAAWS,CAAO,CACrC,EACMC,EAAYJ,EAAQ,GAAG,SAAW,GACtCN,EAAS,QAAQ,WAAWS,CAAO,CACrC,EACME,EAAaL,EAAQ,GAAG,UAAY,GACxCN,EAAS,QAAQ,YAAYS,CAAO,CACtC,EAEA,UAAa,CACXD,EAAU,EACVE,EAAU,EACVC,EAAW,EACXL,EAAQ,QAAQ,EAChB,EAAS,QAAU,KAEfb,IACF,EAAI,QAAU,KAElB,CAEF,EAAG,CAACW,CAAQ,CAAC,GAIb,EAAAd,EAAA,UAAA,KAAgB,CACd,IAAMe,EAAUN,EAAS,QACzB,GAAI,CAACM,EACH,OAGF,IAAMO,EAAO,KAAK,UAAUV,GAAU,CAAC,CAAC,EACpCU,IAAST,EAAQ,UAGrB,EAAQ,QAAUS,EAElBP,EAAQ,UAAUH,CAAM,EAC1B,EAAG,CAACA,CAAM,CAAC,GAGT,EAAAX,EAAA,IAAA,CAAC,MAAD,CACE,IAAKO,EACM,YACX,MAAO,CAAE,MAAO,OAAQ,OAAQ,OAAQ,GAAGe,CAAM,CAClD,CAAA,CAEL,CAAC,E"}
package/dist/react.js ADDED
@@ -0,0 +1,53 @@
1
+ import { PDFMergeSortSplitClass as e } from "./index.js";
2
+ import { forwardRef as t, useCallback as n, useEffect as r, useRef as i } from "react";
3
+ import { jsx as a } from "react/jsx-runtime";
4
+ // src/react.jsx
5
+ var o = () => {
6
+ let e = i(null);
7
+ return {
8
+ ref: e,
9
+ loadDocument: n((t) => e.current.loadDocument(t), []),
10
+ exportDocument: n((t, n) => e.current.exportDocument(t, n), []),
11
+ getPages: n(() => e.current.getPages(), [])
12
+ };
13
+ }, s = t(function({ config: t, style: n, className: o, onAction: s, onExport: c, onLicense: l, onReady: u }, d) {
14
+ let f = i(null), p = i(null), m = i({
15
+ onAction: s,
16
+ onExport: c,
17
+ onLicense: l,
18
+ onReady: u
19
+ });
20
+ m.current = {
21
+ onAction: s,
22
+ onExport: c,
23
+ onLicense: l,
24
+ onReady: u
25
+ };
26
+ let h = i(t);
27
+ h.current = t;
28
+ let g = i(null), _ = t?.embedUrl;
29
+ return r(() => {
30
+ let t = h.current, n = new e(f.current, t);
31
+ p.current = n, g.current = JSON.stringify(t ?? {}), d && (d.current = n), n.whenReady().then((e) => m.current.onReady?.(e)).catch(() => {});
32
+ let r = n.on("action", (e) => m.current.onAction?.(e)), i = n.on("export", (e) => m.current.onExport?.(e)), a = n.on("license", (e) => m.current.onLicense?.(e));
33
+ return () => {
34
+ r(), i(), a(), n.destroy(), p.current = null, d && (d.current = null);
35
+ };
36
+ }, [_]), r(() => {
37
+ let e = p.current;
38
+ if (!e) return;
39
+ let n = JSON.stringify(t ?? {});
40
+ n !== g.current && (g.current = n, e.setConfig(t));
41
+ }, [t]), /* @__PURE__ */ a("div", {
42
+ ref: f,
43
+ className: o,
44
+ style: {
45
+ width: "100%",
46
+ height: "100%",
47
+ ...n
48
+ }
49
+ });
50
+ });
51
+ export { s as PDFMergeSortSplit, s as default, e as PDFMergeSortSplitClass, o as usePdfMergeSortSplit };
52
+
53
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","names":["usePdfMergeSortSplit","ref","useRef","useCallback","document","reference","mode","PDFMergeSortSplit","forwardRef","container","instance","handlers","configRef","config","applied","embedUrl","useEffect","current","created","PDFMergeSortSplitClass","ready","offAction","payload","offExport","offLicense","json","style"],"sources":["../src/react.jsx"],"sourcesContent":["import React, { forwardRef, useCallback, useEffect, useRef } from 'react';\n\nimport { PDFMergeSortSplitClass } from './index.js';\n\n/**\n * The editor's methods, bound to the instance the component creates.\n *\n * ```jsx\n * const { ref, loadDocument, exportDocument, getPages } = usePdfMergeSortSplit();\n *\n * const handleAction = async ({ action }) => {\n * if (action === 'saveZip') download(await exportDocument(undefined, 'zip'));\n * };\n *\n * <PDFMergeSortSplit ref={ref} config={{ apiKey }} onAction={handleAction} />\n * ```\n *\n * The returned functions are stable, so callers need no `useCallback` or\n * `useMemo` wrapper; `ref` is only needed for the escape hatches\n * (`whenReady`, `on`, `destroy`) and to pass to the component.\n */\nexport const usePdfMergeSortSplit = () => {\n const ref = useRef(null);\n\n const loadDocument = useCallback(\n (document) => ref.current.loadDocument(document),\n [],\n );\n const exportDocument = useCallback(\n (reference, mode) => ref.current.exportDocument(reference, mode),\n [],\n );\n const getPages = useCallback(() => ref.current.getPages(), []);\n\n return { ref, loadDocument, exportDocument, getPages };\n};\n\n/**\n * The editor as a React component.\n *\n * The iframe is created once and lives for the lifetime of the component:\n * changing `onAction` / `onExport` / `onReady` does not reload it, and a new\n * `config` is applied to the running editor (`setConfig`) instead of rebuilding\n * it, so the open document stays. Only a different `config.embedUrl` needs a\n * new iframe, which the component creates itself.\n */\nexport const PDFMergeSortSplit = forwardRef(function PDFMergeSortSplit(\n { config, style, className, onAction, onExport, onLicense, onReady },\n ref,\n) {\n const container = useRef(null);\n const instance = useRef(null);\n const handlers = useRef({ onAction, onExport, onLicense, onReady });\n handlers.current = { onAction, onExport, onLicense, onReady };\n\n // The effect that creates the iframe needs the config of the render it runs\n // in, but must not re-run for every new config object.\n const configRef = useRef(config);\n configRef.current = config;\n\n // What the running editor already knows. `config={{ … }}` is a new object on\n // every render, so the comparison is by content, not by reference.\n const applied = useRef(null);\n\n // `embedUrl` is the iframe's src: only that needs a new iframe.\n const embedUrl = config?.embedUrl;\n\n useEffect(() => {\n const current = configRef.current;\n const created = new PDFMergeSortSplitClass(container.current, current);\n instance.current = created;\n applied.current = JSON.stringify(current ?? {});\n\n if (ref) {\n ref.current = created;\n }\n\n created\n .whenReady()\n .then((ready) => handlers.current.onReady?.(ready))\n .catch(() => {\n // `ready` rejects when the editor never answers; onReady is not called.\n });\n\n const offAction = created.on('action', (payload) =>\n handlers.current.onAction?.(payload),\n );\n const offExport = created.on('export', (payload) =>\n handlers.current.onExport?.(payload),\n );\n const offLicense = created.on('license', (payload) =>\n handlers.current.onLicense?.(payload),\n );\n\n return () => {\n offAction();\n offExport();\n offLicense();\n created.destroy();\n instance.current = null;\n\n if (ref) {\n ref.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [embedUrl]);\n\n // Everything except `embedUrl` is applied to the running editor, so a config\n // change does not reload the iframe and the open document stays.\n useEffect(() => {\n const current = instance.current;\n if (!current) {\n return;\n }\n\n const json = JSON.stringify(config ?? {});\n if (json === applied.current) {\n return;\n }\n applied.current = json;\n\n current.setConfig(config);\n }, [config]);\n\n return (\n <div\n ref={container}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n});\n\nexport { PDFMergeSortSplitClass } from './index.js';\nexport default PDFMergeSortSplit;\n"],"mappings":";;;;AAqBA,IAAaA,UAA6B;CACxC,IAAMC,IAAMC,EAAO,IAAI;CAYvB,OAAO;EAAE;EAAK,cAVOC,GAClB,MAAaF,EAAI,QAAQ,aAAaG,CAAQ,GAC/C,CAAC,CAQW;EAAc,gBANLD,GACpB,GAAW,MAASF,EAAI,QAAQ,eAAeI,GAAWC,CAAI,GAC/D,CAAC,CAIyB;EAAgB,UAF3BH,QAAkBF,EAAI,QAAQ,SAAS,GAAG,CAAC,CAEhB;CAAS;AACvD,GAWaM,IAAoBC,EAAW,SAC1C,EAAE,WAAQ,UAAO,cAAW,aAAU,aAAU,cAAW,cAC3D,GACA;CACA,IAAMC,IAAYP,EAAO,IAAI,GACvBQ,IAAWR,EAAO,IAAI,GACtBS,IAAWT,EAAO;EAAE;EAAU;EAAU;EAAW;CAAQ,CAAC;CAClE,EAAS,UAAU;EAAE;EAAU;EAAU;EAAW;CAAQ;CAI5D,IAAMU,IAAYV,EAAOW,CAAM;CAC/B,EAAU,UAAUA;CAIpB,IAAMC,IAAUZ,EAAO,IAAI,GAGrBa,IAAWF,GAAQ;CA4DzB,OA1DAG,QAAgB;EACd,IAAMC,IAAUL,EAAU,SACpBM,IAAU,IAAIC,EAAuBV,EAAU,SAASQ,CAAO;EAQrE,AAPA,EAAS,UAAUC,GACnB,EAAQ,UAAU,KAAK,UAAUD,KAAW,CAAC,CAAC,GAE1ChB,MACF,EAAI,UAAUiB,IAGhBA,EACG,UAAU,CAAC,CACX,MAAM,MAAUP,EAAS,QAAQ,UAAUS,CAAK,CAAC,CAAC,CAClD,YAAY,CAEb,CAAC;EAEH,IAAMC,IAAYH,EAAQ,GAAG,WAAW,MACtCP,EAAS,QAAQ,WAAWW,CAAO,CACrC,GACMC,IAAYL,EAAQ,GAAG,WAAW,MACtCP,EAAS,QAAQ,WAAWW,CAAO,CACrC,GACME,IAAaN,EAAQ,GAAG,YAAY,MACxCP,EAAS,QAAQ,YAAYW,CAAO,CACtC;EAEA,aAAa;GAOX,AANAD,EAAU,GACVE,EAAU,GACVC,EAAW,GACXN,EAAQ,QAAQ,GAChB,EAAS,UAAU,MAEfjB,MACF,EAAI,UAAU;EAElB;CAEF,GAAG,CAACc,CAAQ,CAAC,GAIbC,QAAgB;EACd,IAAMC,IAAUP,EAAS;EACzB,IAAI,CAACO,GACH;EAGF,IAAMQ,IAAO,KAAK,UAAUZ,KAAU,CAAC,CAAC;EACxC,AAAIY,MAASX,EAAQ,YAGrB,EAAQ,UAAUW,GAElBR,EAAQ,UAAUJ,CAAM;CAC1B,GAAG,CAACA,CAAM,CAAC,GAGT,kBAAC,OAAD;EACE,KAAKJ;EACM;EACX,OAAO;GAAE,OAAO;GAAQ,QAAQ;GAAQ,GAAGiB;EAAM;CAClD,CAAA;AAEL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@rsj-de/pdfmergesortsplit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Embed the RSJ PDF Merge, Sort & Split editor in your own product: one iframe, a small SDK, documents never leave the browser.",
5
+ "keywords": [
6
+ "pdf",
7
+ "merge",
8
+ "split",
9
+ "pdf-editor",
10
+ "iframe",
11
+ "rsj"
12
+ ],
13
+ "homepage": "https://pdfmerge.rsj.de/docs/getting-started",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/pdfmergesortsplit/pdfmergesortsplit.git",
17
+ "directory": "sdk"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/pdfmergesortsplit/pdfmergesortsplit/issues"
21
+ },
22
+ "author": "RSJ Software GmbH <ruediger.jungbeck@rsj.de> (https://rsj.de)",
23
+ "type": "module",
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.js",
26
+ "types": "./types/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./types/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs"
32
+ },
33
+ "./react": {
34
+ "types": "./types/react.d.ts",
35
+ "import": "./dist/react.js",
36
+ "require": "./dist/react.cjs"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "types",
43
+ "README.md"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "sideEffects": false,
49
+ "license": "UNLICENSED",
50
+ "scripts": {
51
+ "build": "vite build",
52
+ "prepublishOnly": "pnpm run build"
53
+ },
54
+ "peerDependencies": {
55
+ "react": ">=18"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "react": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@vitejs/plugin-react": "^4.7.0",
64
+ "react": "^19.2.6",
65
+ "vite": "^8.0.13"
66
+ }
67
+ }
@@ -0,0 +1,123 @@
1
+ /** Configuration the embedding page hands to the editor. */
2
+ export interface PDFMergeConfig {
3
+ /** Where the editor is served (default `https://pdfmerge.rsj.de`). */
4
+ embedUrl?: string;
5
+ /** Licence JWT - removes the test-mode banner in the embedded editor. */
6
+ apiKey?: string;
7
+ /** Title shown in the editor's title bar. */
8
+ title?: string;
9
+ /** Icon (URL) that replaces ours in the title bar. */
10
+ icon?: string;
11
+ /** Hide parts of the editor's UI. Everything is shown by default. */
12
+ ui?: {
13
+ logo?: boolean;
14
+ icon?: boolean;
15
+ landing?: boolean;
16
+ title?: boolean;
17
+ about?: boolean;
18
+ blog?: boolean;
19
+ language?: boolean;
20
+ extension?: boolean;
21
+ fullscreen?: boolean;
22
+ theme?: boolean;
23
+ toolbar?: {
24
+ new?: boolean;
25
+ savePdf?: boolean;
26
+ saveZip?: boolean;
27
+ saveDrive?: boolean;
28
+ print?: boolean;
29
+ undo?: boolean;
30
+ redo?: boolean;
31
+ upload?: boolean;
32
+ drive?: boolean;
33
+ addText?: boolean;
34
+ scaleSort?: boolean;
35
+ pages?: boolean;
36
+ rotate?: boolean;
37
+ previous?: boolean;
38
+ next?: boolean;
39
+ delete?: boolean;
40
+ scalePage?: boolean;
41
+ };
42
+ partHeader?: {
43
+ label?: boolean;
44
+ name?: boolean;
45
+ pages?: boolean;
46
+ save?: boolean;
47
+ drive?: boolean;
48
+ print?: boolean;
49
+ };
50
+ };
51
+ /** Buttons injected into the editor; clicks arrive as "action" events. */
52
+ actions?: {
53
+ loaders?: Record<string, PDFMergeAction>;
54
+ saveAll?: Record<string, PDFMergeAction>;
55
+ savePart?: Record<string, PDFMergeAction>;
56
+ };
57
+ /** Write the page provenance into exported PDFs (default off). */
58
+ provenance?: { attach?: boolean };
59
+ /** Timeout for the initial handshake in ms (default 20000). */
60
+ readyTimeout?: number;
61
+ }
62
+
63
+ export interface PDFMergeAction {
64
+ title?: string;
65
+ icon?: string;
66
+ }
67
+
68
+ export interface PDFMergePage {
69
+ id: number;
70
+ page: number;
71
+ rotate: number;
72
+ partName: string;
73
+ provenance: unknown;
74
+ }
75
+
76
+ export interface PDFMergeActionEvent {
77
+ /** Key of the clicked button in `config.actions` - not the injection point. */
78
+ action: string;
79
+ /** Injection point the button was configured in. */
80
+ where: 'loaders' | 'saveAll' | 'savePart';
81
+ reference?: string;
82
+ part?: number;
83
+ }
84
+
85
+ export interface PDFMergeExportEvent {
86
+ reference?: string | number;
87
+ mode: 'pdf' | 'zip';
88
+ fileName: string;
89
+ pages: Array<{ page: number; provenance: unknown }>;
90
+ blob: Blob;
91
+ }
92
+
93
+ export interface PDFMergeLicenseEvent {
94
+ licensed: boolean;
95
+ reason?: string;
96
+ }
97
+
98
+ export interface LoadDocumentOptions {
99
+ name?: string;
100
+ content: File | Blob | ArrayBuffer | Uint8Array | string;
101
+ provenance?: unknown[] | object;
102
+ }
103
+
104
+ export declare class PDFMergeSortSplitClass {
105
+ constructor(container: string | HTMLElement, config?: PDFMergeConfig);
106
+
107
+ readonly iframe: HTMLIFrameElement;
108
+
109
+ whenReady(): Promise<PDFMergeSortSplitClass>;
110
+ /** Apply a new configuration to the running editor (no reload). */
111
+ setConfig(config?: PDFMergeConfig): void;
112
+ on(event: 'action', handler: (event: PDFMergeActionEvent) => void): () => void;
113
+ on(event: 'export', handler: (event: PDFMergeExportEvent) => void): () => void;
114
+ on(event: 'license', handler: (event: PDFMergeLicenseEvent) => void): () => void;
115
+ off(event: string, handler: (event: unknown) => void): void;
116
+ loadDocument(options: LoadDocumentOptions): Promise<unknown>;
117
+ loadDocument(name: string, content: LoadDocumentOptions['content']): Promise<unknown>;
118
+ getPages(): Promise<PDFMergePage[]>;
119
+ exportDocument(reference?: string | number, mode?: 'pdf' | 'zip'): Promise<Blob>;
120
+ destroy(): void;
121
+ }
122
+
123
+ export default PDFMergeSortSplitClass;
@@ -0,0 +1,39 @@
1
+ import type { CSSProperties, RefObject } from 'react';
2
+
3
+ import type {
4
+ PDFMergeActionEvent,
5
+ PDFMergeConfig,
6
+ PDFMergeExportEvent,
7
+ PDFMergeLicenseEvent,
8
+ PDFMergePage,
9
+ PDFMergeSortSplitClass,
10
+ } from './index.js';
11
+
12
+ export interface PDFMergeSortSplitProps {
13
+ config?: PDFMergeConfig;
14
+ style?: CSSProperties;
15
+ className?: string;
16
+ onAction?: (event: PDFMergeActionEvent) => void;
17
+ onExport?: (event: PDFMergeExportEvent) => void;
18
+ onLicense?: (event: PDFMergeLicenseEvent) => void;
19
+ onReady?: (editor: PDFMergeSortSplitClass) => void;
20
+ }
21
+
22
+ export declare const PDFMergeSortSplit: React.ForwardRefExoticComponent<
23
+ PDFMergeSortSplitProps & React.RefAttributes<PDFMergeSortSplitClass | null>
24
+ >;
25
+
26
+ export declare function usePdfMergeSortSplit(): {
27
+ ref: RefObject<PDFMergeSortSplitClass | null>;
28
+ loadDocument: (
29
+ document:
30
+ | { name?: string; content: File | Blob | ArrayBuffer | Uint8Array | string; provenance?: unknown[] | object }
31
+ | string,
32
+ content?: File | Blob | ArrayBuffer | Uint8Array | string,
33
+ ) => Promise<unknown>;
34
+ exportDocument: (reference?: string | number, mode?: 'pdf' | 'zip') => Promise<Blob>;
35
+ getPages: () => Promise<PDFMergePage[]>;
36
+ };
37
+
38
+ export { PDFMergeSortSplitClass } from './index.js';
39
+ export default PDFMergeSortSplit;