Comparing version 0.0.1-7 to 0.0.1-8
export declare const PROJECT_DIR: string; | ||
export declare const RES_DIR: string; | ||
export declare const BLD_DIR: string; | ||
export declare const NODE_MODULES_DIR: string; | ||
export declare const FRONTPAGE_RES_DIR: string; | ||
export declare const FRONTPAGE_BLD_DIR: string; | ||
export declare const FRONTPAGE_INDEX_PATH: string; | ||
export declare const FRONTPAGE_MAIN_PATH: string; | ||
export declare const FRONTPAGE_BUNDLED_PATH: string; |
@@ -7,6 +7,8 @@ import { dirname, join } from 'path'; | ||
export const BLD_DIR = join(PROJECT_DIR, 'bld'); | ||
export const NODE_MODULES_DIR = join(PROJECT_DIR, 'node_modules'); | ||
// frontpage | ||
export const FRONTPAGE_RES_DIR = join(RES_DIR, 'frontpage'); | ||
export const FRONTPAGE_BLD_DIR = join(BLD_DIR, 'frontpage'); | ||
export const FRONTPAGE_INDEX_PATH = join(FRONTPAGE_RES_DIR, 'index.html'); | ||
export const FRONTPAGE_MAIN_PATH = join(FRONTPAGE_BLD_DIR, 'main.js'); | ||
export const FRONTPAGE_BUNDLED_PATH = join(FRONTPAGE_BLD_DIR, 'bundled.js'); | ||
//# sourceMappingURL=@paths.js.map |
import type { ReactNode } from 'react'; | ||
import type { SelfHostedTunnelOptions } from './tunnels/index.js'; | ||
export type BackPageOptions = SelfHostedTunnelOptions & { | ||
import type { FrontPageTunnelOptions } from './tunnels/index.js'; | ||
export type BackPageOptions = FrontPageTunnelOptions & { | ||
title?: string; | ||
@@ -8,3 +8,3 @@ }; | ||
private tunnel; | ||
private container; | ||
private content; | ||
private root; | ||
@@ -11,0 +11,0 @@ private mutationObserver; |
@@ -5,3 +5,3 @@ import React from 'react'; | ||
import { BackPageContext } from './components/index.js'; | ||
import { SelfHostedTunnel } from './tunnels/index.js'; | ||
import { FrontPageTunnel } from './tunnels/index.js'; | ||
export class BackPage { | ||
@@ -15,3 +15,3 @@ constructor({ title, ...options } = {}) { | ||
}); | ||
Object.defineProperty(this, "container", { | ||
Object.defineProperty(this, "content", { | ||
enumerable: true, | ||
@@ -26,3 +26,3 @@ configurable: true, | ||
writable: true, | ||
value: createRoot(this.container) | ||
value: createRoot(this.content) | ||
}); | ||
@@ -35,3 +35,3 @@ Object.defineProperty(this, "mutationObserver", { | ||
}); | ||
this.tunnel = new SelfHostedTunnel(options); | ||
this.tunnel = new FrontPageTunnel(options); | ||
if (title !== undefined) { | ||
@@ -41,3 +41,3 @@ this.tunnel.update({ title }); | ||
this.mutationObserver = new window.MutationObserver(() => this.updateHTML()); | ||
this.mutationObserver.observe(this.container, { | ||
this.mutationObserver.observe(this.content, { | ||
attributes: true, | ||
@@ -72,6 +72,7 @@ childList: true, | ||
updateHTML() { | ||
const html = this.container.innerHTML; | ||
this.tunnel.update({ html }); | ||
this.tunnel.update({ | ||
content: this.content.cloneNode(true), | ||
}); | ||
} | ||
} | ||
//# sourceMappingURL=backpage.js.map |
@@ -0,1 +1,2 @@ | ||
import { randomBytes } from 'crypto'; | ||
import AnsiToHTML from 'ansi-to-html'; | ||
@@ -5,5 +6,11 @@ import patchConsole from 'patch-console'; | ||
const LIMIT_DEFAULT = 100; | ||
let consolePatched = false; | ||
export function Console({ limit = LIMIT_DEFAULT, colors, ...attrs }) { | ||
const [idPrefix] = useState(() => `console-${randomBytes(2).toString('hex')}:`); | ||
const [recentLines, setRecentLines] = useState([]); | ||
useEffect(() => { | ||
if (consolePatched) { | ||
throw new Error('Console already patched, are you using multiple <Console /> component?'); | ||
} | ||
consolePatched = true; | ||
const ansiToHTML = new AnsiToHTML( | ||
@@ -14,3 +21,3 @@ // AnsiToHTML seems to have problem with an undefined `colors` option. | ||
const recentLines = []; | ||
return patchConsole((type, data) => { | ||
const restore = patchConsole((type, data) => { | ||
process[type].write(data); | ||
@@ -40,5 +47,9 @@ const lastMatchingLine = recentLines.findLast(line => line.type === type); | ||
}); | ||
return () => { | ||
consolePatched = false; | ||
restore(); | ||
}; | ||
}, [colors, limit]); | ||
return (React.createElement("pre", { ...attrs }, recentLines.map(({ key, type, html }) => (React.createElement("div", { key: key, className: type, dangerouslySetInnerHTML: { __html: html } }))))); | ||
return (React.createElement("pre", { ...attrs }, recentLines.map(({ key, type, html }) => (React.createElement("div", { key: key, id: `${idPrefix}${key}`, className: type, dangerouslySetInnerHTML: { __html: html } }))))); | ||
} | ||
//# sourceMappingURL=console.js.map |
@@ -17,3 +17,3 @@ import type { BackFrontMessage } from '../shared/index.js'; | ||
title?: string; | ||
html?: string; | ||
content?: HTMLDivElement; | ||
}; | ||
@@ -20,0 +20,0 @@ export declare abstract class TunnelClient { |
@@ -0,1 +1,7 @@ | ||
import DiffMatchPatch from 'diff-match-patch'; | ||
import { MultikeyMap } from 'multikey-map'; | ||
import { window } from './@jsdom.js'; | ||
const INITIAL_CONTENT = window.document.createElement('div'); | ||
INITIAL_CONTENT.innerHTML = '<div>BackPage</div>'; | ||
const dmp = new DiffMatchPatch(); | ||
export class Tunnel { | ||
@@ -15,3 +21,3 @@ constructor() { | ||
title: 'BackPage', | ||
html: '<div>BackPage</div>', | ||
content: INITIAL_CONTENT, | ||
} | ||
@@ -43,8 +49,14 @@ }); | ||
this.pendingUpdate = undefined; | ||
const { title, html } = pendingUpdate; | ||
const { title, content } = pendingUpdate; | ||
if (title !== undefined && title !== snapshot.title) { | ||
snapshot = { ...snapshot, title }; | ||
} | ||
if (html !== undefined && html !== snapshot.html) { | ||
snapshot = { ...snapshot, html }; | ||
if (content !== undefined) { | ||
const patches = cachedDOMPatch(snapshot.content, content); | ||
if (patches.length > 0) { | ||
snapshot = { | ||
...snapshot, | ||
content, | ||
}; | ||
} | ||
} | ||
@@ -60,3 +72,6 @@ if (snapshot === this.snapshot) { | ||
addClient(client) { | ||
const clientState = { idle: true }; | ||
const clientState = { | ||
idle: true, | ||
content: undefined, | ||
}; | ||
this.clientStateMap.set(client, clientState); | ||
@@ -72,5 +87,13 @@ this.sendUpdateToClient(client, clientState); | ||
} | ||
const { snapshot } = this; | ||
const message = { | ||
type: 'update', | ||
title: snapshot.title, | ||
content: clientState.content === undefined | ||
? snapshot.content.innerHTML | ||
: cachedDOMPatch(clientState.content, snapshot.content), | ||
}; | ||
clientState.idle = false; | ||
const { snapshot } = this; | ||
void client.send({ type: 'update', ...snapshot }).then(() => { | ||
clientState.content = snapshot.content; | ||
void client.send(message).then(() => { | ||
clientState.idle = true; | ||
@@ -83,4 +106,14 @@ if (snapshot !== this.snapshot) { | ||
} | ||
const cachedDOMPatchesMap = new MultikeyMap(); | ||
function cachedDOMPatch(elementA, elementB) { | ||
const cachedDiffs = cachedDOMPatchesMap.get([elementA, elementB]); | ||
if (cachedDiffs) { | ||
return cachedDiffs; | ||
} | ||
const patches = dmp.patch_make(elementA.innerHTML, elementB.innerHTML); | ||
cachedDOMPatchesMap.set([elementA, elementB], patches); | ||
return patches; | ||
} | ||
export class TunnelClient { | ||
} | ||
//# sourceMappingURL=tunnel.js.map |
@@ -1,1 +0,1 @@ | ||
export * from './self-hosted-tunnel.js'; | ||
export * from './frontpage-tunnel.js'; |
@@ -1,2 +0,2 @@ | ||
export * from './self-hosted-tunnel.js'; | ||
export * from './frontpage-tunnel.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -0,4 +1,8 @@ | ||
import DiffMatchPatch from 'diff-match-patch'; | ||
import morphdom from 'morphdom'; | ||
// Using string replace also handles the case of HTTPS. | ||
const WS_URL = new URL('/', location.href).href.replace(/^http/, 'ws'); | ||
const RECONNECT_INTERVAL = 1000; | ||
const dmp = new DiffMatchPatch(); | ||
let html; | ||
connect(); | ||
@@ -13,3 +17,15 @@ function connect() { | ||
document.title = message.title; | ||
document.body.innerHTML = message.html; | ||
if (typeof message.content === 'string') { | ||
html = message.content; | ||
document.body.innerHTML = html; | ||
} | ||
else if (html !== undefined) { | ||
[html] = dmp.patch_apply(message.content, html); | ||
const body = document.createElement('body'); | ||
body.innerHTML = html; | ||
morphdom(document.body, body); | ||
} | ||
else { | ||
document.body.innerHTML = 'An error occurred.'; | ||
} | ||
break; | ||
@@ -26,3 +42,2 @@ } | ||
} | ||
export {}; | ||
//# sourceMappingURL=main.js.map |
@@ -0,5 +1,6 @@ | ||
import type { DiffMatchPatches } from './diff-match-patch.js'; | ||
export type BackFrontMessage = { | ||
type: 'update'; | ||
title: string; | ||
html: string; | ||
content: string | DiffMatchPatches; | ||
}; |
export * from './back-front-message.js'; | ||
export * from './diff-match-patch.js'; |
export * from './back-front-message.js'; | ||
export * from './diff-match-patch.js'; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "backpage", | ||
"version": "0.0.1-7", | ||
"version": "0.0.1-8", | ||
"repository": "https://github.com/vilicvane/backpage.git", | ||
@@ -20,3 +20,4 @@ "license": "MIT", | ||
"3": "pnpm install && pnpm dedupe && pnpm install", | ||
"build": "rimraf ./bld && tsc --build", | ||
"build": "rimraf ./bld && tsc --build && pnpm build:rollup", | ||
"build:rollup": "rollup --config ./rollup.config.js", | ||
"lint": "eslint --no-error-on-unmatched-pattern --report-unused-disable-directives . && run-in-every eslint-project --parallel --echo -- eslint --no-error-on-unmatched-pattern --report-unused-disable-directives .", | ||
@@ -27,2 +28,5 @@ "lint-prettier": "prettier --check .", | ||
"dependencies": { | ||
"@rollup/plugin-commonjs": "^25.0.7", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"@types/diff-match-patch": "^1.0.36", | ||
"@types/express": "^4.17.21", | ||
@@ -35,7 +39,11 @@ "@types/express-ws": "^3.0.4", | ||
"ansi-to-html": "^0.7.2", | ||
"diff-match-patch": "^1.0.5", | ||
"express": "^4.18.2", | ||
"express-ws": "^5.0.2", | ||
"jsdom": "^23.2.0", | ||
"morphdom": "^2.7.1", | ||
"multikey-map": "^0.2.1", | ||
"patch-console": "^2.0.0", | ||
"react-dom": "^18.2.0", | ||
"rollup": "^4.9.5", | ||
"tslib": "^2.6.2", | ||
@@ -42,0 +50,0 @@ "ws": "^8.16.0" |
@@ -99,6 +99,4 @@ [![NPM version](https://img.shields.io/npm/v/backpage?color=%23cb3837&style=flat-square)](https://www.npmjs.com/package/backpage) | ||
> Console outputs are streamed as part of the HTML in a non-incremental way, so it is not recommended to use `<Console />` with a large `limit` number. | ||
## License | ||
MIT License. |
@@ -11,2 +11,6 @@ import {dirname, join} from 'path'; | ||
export const NODE_MODULES_DIR = join(PROJECT_DIR, 'node_modules'); | ||
// frontpage | ||
export const FRONTPAGE_RES_DIR = join(RES_DIR, 'frontpage'); | ||
@@ -18,2 +22,2 @@ | ||
export const FRONTPAGE_MAIN_PATH = join(FRONTPAGE_BLD_DIR, 'main.js'); | ||
export const FRONTPAGE_BUNDLED_PATH = join(FRONTPAGE_BLD_DIR, 'bundled.js'); |
@@ -1,3 +0,14 @@ | ||
import type {BackFrontMessage} from '../shared/index.js'; | ||
import DiffMatchPatch from 'diff-match-patch'; | ||
import {MultikeyMap} from 'multikey-map'; | ||
import type {BackFrontMessage, DiffMatchPatches} from '../shared/index.js'; | ||
import {window} from './@jsdom.js'; | ||
const INITIAL_CONTENT = window.document.createElement('div'); | ||
INITIAL_CONTENT.innerHTML = '<div>BackPage</div>'; | ||
const dmp = new DiffMatchPatch(); | ||
export abstract class Tunnel { | ||
@@ -8,3 +19,3 @@ private clientStateMap = new Map<TunnelClient, ClientState>(); | ||
title: 'BackPage', | ||
html: '<div>BackPage</div>', | ||
content: INITIAL_CONTENT, | ||
}; | ||
@@ -33,3 +44,3 @@ | ||
const {title, html} = pendingUpdate; | ||
const {title, content} = pendingUpdate; | ||
@@ -40,4 +51,11 @@ if (title !== undefined && title !== snapshot.title) { | ||
if (html !== undefined && html !== snapshot.html) { | ||
snapshot = {...snapshot, html}; | ||
if (content !== undefined) { | ||
const patches = cachedDOMPatch(snapshot.content, content); | ||
if (patches.length > 0) { | ||
snapshot = { | ||
...snapshot, | ||
content, | ||
}; | ||
} | ||
} | ||
@@ -61,3 +79,6 @@ | ||
protected addClient(client: TunnelClient): void { | ||
const clientState: ClientState = {idle: true}; | ||
const clientState: ClientState = { | ||
idle: true, | ||
content: undefined, | ||
}; | ||
@@ -80,8 +101,17 @@ this.clientStateMap.set(client, clientState); | ||
} | ||
const {snapshot} = this; | ||
const message: BackFrontMessage = { | ||
type: 'update', | ||
title: snapshot.title, | ||
content: | ||
clientState.content === undefined | ||
? snapshot.content.innerHTML | ||
: cachedDOMPatch(clientState.content, snapshot.content), | ||
}; | ||
clientState.idle = false; | ||
clientState.content = snapshot.content; | ||
const {snapshot} = this; | ||
void client.send({type: 'update', ...snapshot}).then(() => { | ||
void client.send(message).then(() => { | ||
clientState.idle = true; | ||
@@ -98,3 +128,3 @@ | ||
title?: string; | ||
html?: string; | ||
content?: HTMLDivElement; | ||
}; | ||
@@ -104,3 +134,3 @@ | ||
title: string; | ||
html: string; | ||
content: HTMLDivElement; | ||
}; | ||
@@ -110,6 +140,29 @@ | ||
idle: boolean; | ||
content: HTMLDivElement | undefined; | ||
}; | ||
const cachedDOMPatchesMap = new MultikeyMap< | ||
[HTMLElement, HTMLElement], | ||
DiffMatchPatches | ||
>(); | ||
function cachedDOMPatch( | ||
elementA: HTMLElement, | ||
elementB: HTMLElement, | ||
): DiffMatchPatches { | ||
const cachedDiffs = cachedDOMPatchesMap.get([elementA, elementB]); | ||
if (cachedDiffs) { | ||
return cachedDiffs; | ||
} | ||
const patches = dmp.patch_make(elementA.innerHTML, elementB.innerHTML); | ||
cachedDOMPatchesMap.set([elementA, elementB], patches); | ||
return patches; | ||
} | ||
export abstract class TunnelClient { | ||
abstract send(message: BackFrontMessage): Promise<void>; | ||
} |
@@ -1,1 +0,1 @@ | ||
export * from './self-hosted-tunnel.js'; | ||
export * from './frontpage-tunnel.js'; |
@@ -0,1 +1,4 @@ | ||
import DiffMatchPatch from 'diff-match-patch'; | ||
import morphdom from 'morphdom'; | ||
import type {BackFrontMessage} from '../shared/index.js'; | ||
@@ -8,2 +11,6 @@ | ||
const dmp = new DiffMatchPatch(); | ||
let html: string | undefined; | ||
connect(); | ||
@@ -21,3 +28,19 @@ | ||
document.title = message.title; | ||
document.body.innerHTML = message.html; | ||
if (typeof message.content === 'string') { | ||
html = message.content; | ||
document.body.innerHTML = html; | ||
} else if (html !== undefined) { | ||
[html] = dmp.patch_apply(message.content, html); | ||
const body = document.createElement('body'); | ||
body.innerHTML = html; | ||
morphdom(document.body, body); | ||
} else { | ||
document.body.innerHTML = 'An error occurred.'; | ||
} | ||
break; | ||
@@ -24,0 +47,0 @@ } |
@@ -0,5 +1,7 @@ | ||
import type {DiffMatchPatches} from './diff-match-patch.js'; | ||
export type BackFrontMessage = { | ||
type: 'update'; | ||
title: string; | ||
html: string; | ||
content: string | DiffMatchPatches; | ||
}; |
export * from './back-front-message.js'; | ||
export * from './diff-match-patch.js'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
165885
74
3818
22
1
102
2
+ Addeddiff-match-patch@^1.0.5
+ Addedmorphdom@^2.7.1
+ Addedmultikey-map@^0.2.1
+ Addedrollup@^4.9.5
+ Added@jridgewell/sourcemap-codec@1.5.0(transitive)
+ Added@rollup/plugin-commonjs@25.0.8(transitive)
+ Added@rollup/plugin-node-resolve@15.3.0(transitive)
+ Added@rollup/pluginutils@5.1.2(transitive)
+ Added@rollup/rollup-android-arm-eabi@4.22.4(transitive)
+ Added@rollup/rollup-android-arm64@4.22.4(transitive)
+ Added@rollup/rollup-darwin-arm64@4.22.4(transitive)
+ Added@rollup/rollup-darwin-x64@4.22.4(transitive)
+ Added@rollup/rollup-linux-arm-gnueabihf@4.22.4(transitive)
+ Added@rollup/rollup-linux-arm-musleabihf@4.22.4(transitive)
+ Added@rollup/rollup-linux-arm64-gnu@4.22.4(transitive)
+ Added@rollup/rollup-linux-arm64-musl@4.22.4(transitive)
+ Added@rollup/rollup-linux-powerpc64le-gnu@4.22.4(transitive)
+ Added@rollup/rollup-linux-riscv64-gnu@4.22.4(transitive)
+ Added@rollup/rollup-linux-s390x-gnu@4.22.4(transitive)
+ Added@rollup/rollup-linux-x64-gnu@4.22.4(transitive)
+ Added@rollup/rollup-linux-x64-musl@4.22.4(transitive)
+ Added@rollup/rollup-win32-arm64-msvc@4.22.4(transitive)
+ Added@rollup/rollup-win32-ia32-msvc@4.22.4(transitive)
+ Added@rollup/rollup-win32-x64-msvc@4.22.4(transitive)
+ Added@types/diff-match-patch@1.0.36(transitive)
+ Added@types/estree@1.0.51.0.6(transitive)
+ Added@types/node@20.16.9(transitive)
+ Added@types/react@18.3.9(transitive)
+ Added@types/resolve@1.20.2(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedcommondir@1.0.1(transitive)
+ Addeddeepmerge@4.3.1(transitive)
+ Addeddiff-match-patch@1.0.5(transitive)
+ Addedestree-walker@2.0.2(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedfsevents@2.3.3(transitive)
+ Addedglob@8.1.0(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedis-core-module@2.15.1(transitive)
+ Addedis-module@1.0.0(transitive)
+ Addedis-reference@1.2.1(transitive)
+ Addedmagic-string@0.30.11(transitive)
+ Addedminimatch@5.1.6(transitive)
+ Addedmixed-map@0.1.2(transitive)
+ Addedmorphdom@2.7.4(transitive)
+ Addedmultikey-map@0.2.1(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpicomatch@2.3.1(transitive)
+ Addedresolve@1.22.8(transitive)
+ Addedrollup@4.22.4(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removed@types/node@20.16.10(transitive)
- Removed@types/react@18.3.10(transitive)