Socket
Socket
Sign inDemoInstall

backpage

Package Overview
Dependencies
237
Maintainers
1
Versions
37
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.1-8 to 0.0.1-9

bld/shared/front-back-message.d.ts

15

bld/backpage/backpage.d.ts
import type { ReactNode } from 'react';
import type { TunnelNotification, TunnelNotifyOptions } from './tunnel.js';
import type { FrontPageTunnelOptions } from './tunnels/index.js';
export type BackPageOptions = FrontPageTunnelOptions & {
title?: string;
notify?: Partial<TunnelNotifyOptions> & {
fallback?: (notification: TunnelNotification) => BackPageFallbackRequest | string | void;
};
};

@@ -11,3 +15,4 @@ export declare class BackPage {

private mutationObserver;
constructor({ title, ...options }?: BackPageOptions);
private notifyOptions;
constructor({ title, notify: notifyOptions, ...options }?: BackPageOptions);
getURL(): Promise<string>;

@@ -22,6 +27,14 @@ /**

update(update: BackPageUpdate): void;
notify(notification: TunnelNotification | string, options?: BackPageNotifyOptions): void;
private updateHTML;
}
export type BackPageFallbackRequest = {
url: string;
options?: RequestInit;
};
export type BackPageUpdate = {
title?: string;
};
export type BackPageNotifyOptions = {
timeout?: false;
};

@@ -6,4 +6,6 @@ import React from 'react';

import { FrontPageTunnel } from './tunnels/index.js';
const NOTIFY_TIMEOUT_DEFAULT = 30000;
export class BackPage {
constructor({ title, ...options } = {}) {
constructor({ title, notify: notifyOptions, ...options } = {}) {
var _a;
Object.defineProperty(this, "tunnel", {

@@ -33,3 +35,29 @@ enumerable: true,

});
Object.defineProperty(this, "notifyOptions", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.notifyOptions = notifyOptions
? {
timeout: (_a = notifyOptions.timeout) !== null && _a !== void 0 ? _a : NOTIFY_TIMEOUT_DEFAULT,
}
: {
timeout: false,
};
this.tunnel = new FrontPageTunnel(options);
const notifyFallback = notifyOptions === null || notifyOptions === void 0 ? void 0 : notifyOptions.fallback;
if (notifyFallback) {
this.tunnel.onNotifyTimeout(notification => {
let request = notifyFallback(notification);
if (request === undefined) {
return;
}
if (typeof request === 'string') {
request = { url: request, options: undefined };
}
void fetch(request.url, request.options);
});
}
if (title !== undefined) {

@@ -68,2 +96,10 @@ this.tunnel.update({ title });

}
notify(notification, options = {}) {
notification =
typeof notification === 'string' ? { title: notification } : notification;
this.tunnel.notify(notification, {
...this.notifyOptions,
...options,
});
}
updateHTML() {

@@ -70,0 +106,0 @@ this.tunnel.update({

@@ -1,2 +0,2 @@

import type { BackFrontMessage } from '../shared/index.js';
import type { BackFrontMessage, FrontBackMessage } from '../shared/index.js';
export declare abstract class Tunnel {

@@ -7,4 +7,10 @@ private clientStateMap;

private updateDebounceImmediate;
update(update: Update): void;
update(update: TunnelUpdate): void;
private _update;
private notifyTimeoutCallbackSet;
private notifyTimeoutMap;
onNotifyTimeout(callback: TunnelNotifyTimeoutCallback): void;
notify(notification: TunnelNotification, { timeout }: TunnelNotifyOptions): void;
private emitNotifyTimeout;
private handleNotified;
abstract getURL(): Promise<string>;

@@ -16,8 +22,20 @@ abstract close(): void;

}
export type Update = {
export type TunnelNotifyTimeoutCallback = (notification: TunnelNotification) => void;
export type TunnelUpdate = {
title?: string;
content?: HTMLDivElement;
};
export type TunnelNotification = {
title: string;
body?: string;
};
export type TunnelNotifyOptions = {
timeout: number | false;
};
export declare abstract class TunnelClient {
private messageCallbackSet;
abstract send(message: BackFrontMessage): Promise<void>;
onMessage(callback: TunnelClientMessageCallback): void;
protected emitMessage(message: FrontBackMessage): void;
}
export type TunnelClientMessageCallback = (message: FrontBackMessage) => void;

@@ -0,1 +1,2 @@

import { randomUUID } from 'crypto';
import DiffMatchPatch from 'diff-match-patch';

@@ -36,2 +37,14 @@ import { MultikeyMap } from 'multikey-map';

});
Object.defineProperty(this, "notifyTimeoutCallbackSet", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
Object.defineProperty(this, "notifyTimeoutMap", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}

@@ -70,2 +83,41 @@ update(update) {

}
onNotifyTimeout(callback) {
this.notifyTimeoutCallbackSet.add(callback);
}
notify(notification, { timeout }) {
const { clientStateMap, notifyTimeoutMap } = this;
if (timeout !== false && clientStateMap.size === 0) {
this.emitNotifyTimeout(notification);
return;
}
const id = randomUUID();
for (const client of clientStateMap.keys()) {
void client.send({
type: 'notify',
id,
...notification,
});
}
if (timeout !== false) {
const timer = setTimeout(() => {
notifyTimeoutMap.delete(id);
this.emitNotifyTimeout(notification);
}, timeout);
notifyTimeoutMap.set(id, timer);
}
}
emitNotifyTimeout(notification) {
for (const callback of this.notifyTimeoutCallbackSet) {
callback(notification);
}
}
handleNotified({ id }) {
const { notifyTimeoutMap } = this;
const timer = notifyTimeoutMap.get(id);
if (timer === undefined) {
return;
}
clearTimeout(timer);
notifyTimeoutMap.delete(id);
}
addClient(client) {

@@ -77,2 +129,9 @@ const clientState = {

this.clientStateMap.set(client, clientState);
client.onMessage(message => {
switch (message.type) {
case 'notified':
this.handleNotified(message);
break;
}
});
this.sendUpdateToClient(client, clientState);

@@ -116,3 +175,19 @@ }

export class TunnelClient {
constructor() {
Object.defineProperty(this, "messageCallbackSet", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
}
onMessage(callback) {
this.messageCallbackSet.add(callback);
}
emitMessage(message) {
for (const callback of this.messageCallbackSet) {
callback(message);
}
}
}
//# sourceMappingURL=tunnel.js.map

@@ -60,2 +60,8 @@ import { createServer } from 'http';

});
ws.on('message', data => {
if (typeof data === 'string') {
const message = JSON.parse(data);
this.emitMessage(message);
}
});
}

@@ -62,0 +68,0 @@ async send(message) {

64

bld/frontpage/main.js

@@ -16,17 +16,7 @@ import DiffMatchPatch from 'diff-match-patch';

case 'update':
document.title = message.title;
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.';
}
update(message);
break;
case 'notify':
void notify(message);
break;
}

@@ -41,3 +31,49 @@ }

});
function update({ title, content }) {
document.title = title;
if (typeof content === 'string') {
html = content;
document.body.innerHTML = html;
}
else if (html !== undefined) {
[html] = dmp.patch_apply(content, html);
const body = document.createElement('body');
body.innerHTML = html;
morphdom(document.body, body);
}
else {
document.body.innerHTML = 'An error occurred.';
}
}
async function notify({ id, title, body, }) {
switch (Notification.permission) {
case 'granted':
break;
case 'denied':
return;
case 'default':
if ((await Notification.requestPermission()) !== 'granted') {
return;
}
else {
break;
}
}
const notification = new Notification(title, { body });
notification.addEventListener('click', () => {
window.focus();
notified();
});
notification.addEventListener('close', notified);
function notified() {
send({ type: 'notified', id });
}
}
function send(message) {
if (ws.readyState !== ws.OPEN) {
return;
}
ws.send(JSON.stringify(message));
}
}
//# sourceMappingURL=main.js.map
import type { DiffMatchPatches } from './diff-match-patch.js';
export type BackFrontMessage = {
export type BackFrontMessage = BackFrontUpdateMessage | BackFrontNotifyMessage;
export type BackFrontUpdateMessage = {
type: 'update';

@@ -7,1 +8,7 @@ title: string;

};
export type BackFrontNotifyMessage = {
type: 'notify';
id: string;
title: string;
body?: string;
};
export * from './back-front-message.js';
export * from './diff-match-patch.js';
export * from './front-back-message.js';
export * from './back-front-message.js';
export * from './diff-match-patch.js';
export * from './front-back-message.js';
//# sourceMappingURL=index.js.map
{
"name": "backpage",
"version": "0.0.1-8",
"version": "0.0.1-9",
"repository": "https://github.com/vilicvane/backpage.git",

@@ -5,0 +5,0 @@ "license": "MIT",

@@ -8,3 +8,3 @@ [![NPM version](https://img.shields.io/npm/v/backpage?color=%23cb3837&style=flat-square)](https://www.npmjs.com/package/backpage)

Naive web UI streaming based on React for Node.js CLI applications.
Naive static HTML streaming based on React for Node.js CLI applications.

@@ -17,2 +17,7 @@ ## How does it work?

## Features
- Stream static HTML from React rendering.
- Send notification to browser.
## Installation

@@ -24,3 +29,3 @@

## Usage
## Basic Usage

@@ -39,3 +44,7 @@ **main.tsx**

await page.guide(); // Print connect information.
// Print page information including URL.
await page.guide();
// Send notification to browser (if connected).
page.notify('Hello BackPage!');
```

@@ -62,2 +71,23 @@

## Notify Fallback
You can get notified if no browser is connected or the notification is not **clicked** within the timeout.
```ts
const page = new BackPage({
notify: {
timeout: 30_000,
fallback: notification => {
// Handle the notification manually.
// You can also return a webhook URL or request options to initiate an
// HTTP request.
return 'https://some.webhook/';
},
},
});
page.notify('Hello BackPage!');
```
## Built-in Components

@@ -64,0 +94,0 @@

@@ -0,5 +1,12 @@

import {randomUUID} from 'crypto';
import DiffMatchPatch from 'diff-match-patch';
import {MultikeyMap} from 'multikey-map';
import type {BackFrontMessage, DiffMatchPatches} from '../shared/index.js';
import type {
BackFrontMessage,
DiffMatchPatches,
FrontBackMessage,
FrontBackNotifiedMessage,
} from '../shared/index.js';

@@ -22,7 +29,7 @@ import {window} from './@jsdom.js';

private pendingUpdate: Update | undefined;
private pendingUpdate: TunnelUpdate | undefined;
private updateDebounceImmediate: NodeJS.Immediate | undefined;
update(update: Update): void {
update(update: TunnelUpdate): void {
clearImmediate(this.updateDebounceImmediate);

@@ -72,2 +79,61 @@

private notifyTimeoutCallbackSet = new Set<TunnelNotifyTimeoutCallback>();
private notifyTimeoutMap = new Map<string, NodeJS.Timeout>();
onNotifyTimeout(callback: TunnelNotifyTimeoutCallback): void {
this.notifyTimeoutCallbackSet.add(callback);
}
notify(
notification: TunnelNotification,
{timeout}: TunnelNotifyOptions,
): void {
const {clientStateMap, notifyTimeoutMap} = this;
if (timeout !== false && clientStateMap.size === 0) {
this.emitNotifyTimeout(notification);
return;
}
const id = randomUUID();
for (const client of clientStateMap.keys()) {
void client.send({
type: 'notify',
id,
...notification,
});
}
if (timeout !== false) {
const timer = setTimeout(() => {
notifyTimeoutMap.delete(id);
this.emitNotifyTimeout(notification);
}, timeout);
notifyTimeoutMap.set(id, timer);
}
}
private emitNotifyTimeout(notification: TunnelNotification): void {
for (const callback of this.notifyTimeoutCallbackSet) {
callback(notification);
}
}
private handleNotified({id}: FrontBackNotifiedMessage): void {
const {notifyTimeoutMap} = this;
const timer = notifyTimeoutMap.get(id);
if (timer === undefined) {
return;
}
clearTimeout(timer);
notifyTimeoutMap.delete(id);
}
abstract getURL(): Promise<string>;

@@ -85,2 +151,10 @@

client.onMessage(message => {
switch (message.type) {
case 'notified':
this.handleNotified(message);
break;
}
});
this.sendUpdateToClient(client, clientState);

@@ -100,2 +174,3 @@ }

}
const {snapshot} = this;

@@ -125,3 +200,7 @@

export type Update = {
export type TunnelNotifyTimeoutCallback = (
notification: TunnelNotification,
) => void;
export type TunnelUpdate = {
title?: string;

@@ -131,2 +210,11 @@ content?: HTMLDivElement;

export type TunnelNotification = {
title: string;
body?: string;
};
export type TunnelNotifyOptions = {
timeout: number | false;
};
type Snapshot = {

@@ -165,3 +253,17 @@ title: string;

export abstract class TunnelClient {
private messageCallbackSet = new Set<TunnelClientMessageCallback>();
abstract send(message: BackFrontMessage): Promise<void>;
onMessage(callback: TunnelClientMessageCallback): void {
this.messageCallbackSet.add(callback);
}
protected emitMessage(message: FrontBackMessage): void {
for (const callback of this.messageCallbackSet) {
callback(message);
}
}
}
export type TunnelClientMessageCallback = (message: FrontBackMessage) => void;

@@ -9,3 +9,3 @@ import type {Server} from 'http';

import type {BackFrontMessage} from '../../shared/index.js';
import type {BackFrontMessage, FrontBackMessage} from '../../shared/index.js';
import {

@@ -80,2 +80,10 @@ FRONTPAGE_BUNDLED_PATH,

super();
ws.on('message', data => {
if (typeof data === 'string') {
const message = JSON.parse(data) as FrontBackMessage;
this.emitMessage(message);
}
});
}

@@ -82,0 +90,0 @@

import DiffMatchPatch from 'diff-match-patch';
import morphdom from 'morphdom';
import type {BackFrontMessage} from '../shared/index.js';
import type {
BackFrontMessage,
BackFrontNotifyMessage,
BackFrontUpdateMessage,
FrontBackMessage,
} from '../shared/index.js';

@@ -26,21 +31,7 @@ // Using string replace also handles the case of HTTPS.

case 'update':
document.title = message.title;
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.';
}
update(message);
break;
case 'notify':
void notify(message);
break;
}

@@ -57,2 +48,61 @@ }

});
function update({title, content}: BackFrontUpdateMessage): void {
document.title = title;
if (typeof content === 'string') {
html = content;
document.body.innerHTML = html;
} else if (html !== undefined) {
[html] = dmp.patch_apply(content, html);
const body = document.createElement('body');
body.innerHTML = html;
morphdom(document.body, body);
} else {
document.body.innerHTML = 'An error occurred.';
}
}
async function notify({
id,
title,
body,
}: BackFrontNotifyMessage): Promise<void> {
switch (Notification.permission) {
case 'granted':
break;
case 'denied':
return;
case 'default':
if ((await Notification.requestPermission()) !== 'granted') {
return;
} else {
break;
}
}
const notification = new Notification(title, {body});
notification.addEventListener('click', () => {
window.focus();
notified();
});
notification.addEventListener('close', notified);
function notified(): void {
send({type: 'notified', id});
}
}
function send(message: FrontBackMessage): void {
if (ws.readyState !== ws.OPEN) {
return;
}
ws.send(JSON.stringify(message));
}
}
import type {DiffMatchPatches} from './diff-match-patch.js';
export type BackFrontMessage = {
export type BackFrontMessage = BackFrontUpdateMessage | BackFrontNotifyMessage;
export type BackFrontUpdateMessage = {
type: 'update';

@@ -8,1 +10,8 @@ title: string;

};
export type BackFrontNotifyMessage = {
type: 'notify';
id: string;
title: string;
body?: string;
};
export * from './back-front-message.js';
export * from './diff-match-patch.js';
export * from './front-back-message.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 too big to display

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc