Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

typed-dom

Package Overview
Dependencies
Maintainers
1
Versions
350
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

typed-dom - npm Package Compare versions

Comparing version 0.0.279 to 0.0.280

22

dom.ts

@@ -137,2 +137,14 @@ import { Symbol, document } from 'spica/global';

el.setAttribute(name, value);
if (name.startsWith('on')) {
const type = name.slice(2).toLowerCase();
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${type}`;
prop in el
? el[prop] ??= (ev: NodeEvent<E>) => ev.returnValue
: el[prop] ??= '';
}
}
continue;

@@ -144,4 +156,4 @@ case 'function':

if (!name.startsWith('on')) throw new Error(`TypedDOM: Attribute names for event listeners must start with "on" but got "${name}".`);
const eventname = name.slice(2).toLowerCase();
el.addEventListener(eventname, value, {
const type = name.slice(2).toLowerCase();
el.addEventListener(type, value, {
passive: [

@@ -154,9 +166,9 @@ 'wheel',

'touchcancel',
].includes(eventname),
].includes(type),
});
switch (eventname) {
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${eventname}`;
const prop = `on${type}`;
prop in el

@@ -163,0 +175,0 @@ ? el[prop] ??= (ev: NodeEvent<E>) => ev.returnValue

@@ -6,4 +6,4 @@ import 'spica/global';

export { NS, Attrs, Children, Factory, shadow, frag, html, svg, text, element, define, append, prepend, defrag } from './src/util/dom';
export { listen, delegate, bind, once, wait, currentTarget } from './src/util/listener';
export { listen, delegate, bind, once, currentTarget } from './src/util/listener';
export { querySelector, querySelectorAll } from './src/util/query';
export { identity } from './src/util/identity';

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

import { bind, delegate, listen, once, wait, currentTarget } from './listener';
import { bind, delegate, listen, once, currentTarget } from './listener';
import { Shadow, HTML } from '../builder';

@@ -82,4 +82,9 @@

assert(cnt === 0 && ++cnt);
once(el, 'click', () => void assert(cnt === 1 && ++cnt) || done());
once(el, 'click', () => void assert(cnt === 2 && ++cnt) || done());
});
once(el, 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
assert(cnt === 1 && ++cnt);
});
document.createDocumentFragment().appendChild(el);

@@ -97,32 +102,12 @@ el.click();

assert(cnt === 0 && ++cnt);
once(dom.element, 'click', () => void assert(cnt === 1 && ++cnt) || done());
once(dom.element, 'click', () => void assert(cnt === 2 && ++cnt) || done());
});
document.createDocumentFragment().appendChild(dom.element);
dom.children[0].element.click();
dom.children[0].element.click();
});
});
describe('wait', () => {
it('bind', done => {
const el = HTML.a().element;
wait(el, 'click').then(ev => {
once(dom.element, 'a', 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
done();
assert(cnt === 1 && ++cnt);
});
document.createDocumentFragment().appendChild(el);
el.click();
});
it('delegate', done => {
const dom = Shadow.section([HTML.a()]);
wait(dom.element, 'a', 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
done();
});
document.createDocumentFragment().appendChild(dom.element);
dom.children[0].element.click();
dom.children[0].element.click();
});

@@ -129,0 +114,0 @@

@@ -13,49 +13,95 @@ import { AtomicPromise } from 'spica/promise';

export function listen<T extends keyof WindowEventMap>(target: Window, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: ((ev: Event) => void) | T, c: boolean | AddEventListenerOptions | ((ev: Event) => void) = false, d: AddEventListenerOptions = {}): () => undefined {
return typeof b === 'string'
? delegate(target as Document, a, b as keyof ElementEventMap, c as () => void, d)
: bind(target as Element, a as keyof ElementEventMap, b, c as boolean);
export function listen<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: T | string,
type: T | ((ev: Event) => void),
listener?: boolean | AddEventListenerOptions | ((ev: Event) => void),
option?: boolean | AddEventListenerOptions,
): () => undefined {
return typeof type === 'string'
? delegate(target as Document, selector, type as keyof ElementEventMap, listener as () => void, option)
: bind(target as Element, selector as keyof ElementEventMap, type, listener as boolean);
}
export function once<T extends keyof WindowEventMap>(target: Window, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function once<T extends keyof WindowEventMap>(target: Window, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function once<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function once<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function once<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<MathMLElementEventMap[T]>;
export function once<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<MathMLElementEventMap[T]>;
export function once<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof ElementEventMap>(target: Element, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function once<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function once<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: ((ev: Event) => void) | T, c: boolean | AddEventListenerOptions | ((ev: Event) => void) = false, d: AddEventListenerOptions = {}): () => undefined {
return typeof b === 'string'
? delegate(target as Document, a, b as keyof ElementEventMap, c as () => void, { ...typeof d === 'boolean' ? { capture: d } : d, once: true })
: bind(target as Element, a as keyof ElementEventMap, b, { ...typeof c === 'boolean' ? { capture: c } : c, once: true });
}
export function wait<T extends keyof WindowEventMap>(target: Window, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function wait<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function wait<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function wait<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function wait<T extends keyof ElementEventMap>(target: Element, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function wait<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function wait<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function wait<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function wait<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: T | boolean | AddEventListenerOptions = false, c: AddEventListenerOptions = {}): AtomicPromise<Event> {
export function once<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: T | string,
type?: T | boolean | AddEventListenerOptions | ((ev: Event) => void),
listener?: boolean | AddEventListenerOptions | ((ev: Event) => void),
option?: boolean | AddEventListenerOptions,
): (() => undefined) | AtomicPromise<Event> {
switch (typeof type) {
case 'string':
switch (typeof listener) {
case 'function':
return delegate(target as Document, selector, type as keyof ElementEventMap, listener as () => void, { ...typeof option === 'boolean' ? { capture: option } : option, once: true });
case 'object':
option = { ...listener, once: true };
break;
default:
option = { once: true };
}
return new AtomicPromise(resolve =>
void delegate(target as Element, selector, type as keyof ElementEventMap, resolve, option));
case 'function':
return bind(target as Element, selector as keyof ElementEventMap, type, { ...typeof listener === 'boolean' ? { capture: listener } : listener, once: true });
case 'object':
option = { ...type, once: true };
break;
default:
option = { once: true };
}
return new AtomicPromise(resolve =>
typeof b === 'string'
? once(target as Document, a, b as keyof ElementEventMap, resolve, c)
: once(target as Element, a as keyof ElementEventMap, resolve, b));
void bind(target as Element, selector as keyof ElementEventMap, resolve, option));
}
export function delegate<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option: AddEventListenerOptions = {}): () => undefined {
return bind(target as Element, type, ev => {
export function delegate<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: string,
type: T,
listener: (ev: Event) => void,
option?: boolean | AddEventListenerOptions,
): () => undefined {
return bind(target as Element, type as keyof ElementEventMap, ev => {
assert(ev.target instanceof Element);

@@ -66,4 +112,4 @@ assert(ev.composedPath()[0] instanceof Element);

: (ev.target as Element)?.closest(selector);
cx && once(cx, type, e => { e === ev && listener(ev); }, option);
}, { ...option, capture: true });
cx && once(cx, type as keyof ElementEventMap, e => { e === ev && listener(ev); }, option);
}, { ...typeof option === 'boolean' ? { capture: true } : option, capture: true });
}

@@ -75,4 +121,19 @@

export function bind<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, type: T, listener: (ev: Event) => void, option: boolean | AddEventListenerOptions = false): () => undefined {
export function bind<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
type: T,
listener: (ev: Event) => void,
option?: boolean | AddEventListenerOptions,
): () => undefined {
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${type}`;
prop in target
? target[prop] ??= (ev: Event) => ev.returnValue
: target[prop] ??= '';
}
target.addEventListener(type, handler, option);

@@ -79,0 +140,0 @@ return singleton(() => void target.removeEventListener(type, handler, option));

{
"name": "typed-dom",
"version": "0.0.279",
"version": "0.0.280",
"description": "A value-level and type-level DOM builder.",

@@ -65,3 +65,3 @@ "private": false,

"power-assert": "^1.6.1",
"spica": "0.0.541",
"spica": "0.0.542",
"tsify": "^5.0.4",

@@ -68,0 +68,0 @@ "typescript": "4.6.4",

@@ -180,2 +180,18 @@ # typed-dom

## Events
These events are enabled only when an event listener is set using the Typed-DOM APIs.
### mutate
It will be dispatched when the children property value is changed.
### connect
It will be dispatched when added to another proxy connected to the context object.
### disconnect
It will be dispatched when removed from the parent proxy connected to the context object.
## Usage

@@ -331,13 +347,18 @@

super(async function* (this: Component) {
for (const child of this.children) {
child.children = child.children.toUpperCase();
let count = 0;
this.children = `${count}`;
while (true) {
if (!this.element.isConnected) {
await new Promise(resolve =>
this.element.addEventListener('connect', resolve));
}
this.children = `${++count}`;
yield;
await new Promise(resolve => setTimeout(resolve, 100));
}
}, { trigger: 'element', capacity: 0 });
}, { trigger: 'element' });
}
private readonly dom = Shadow.section({
private readonly dom = Shadow.section({ onconnect: '' }, {
style: HTML.style(':scope { color: red; }'),
content: HTML.ul([
HTML.li('item'),
]),
content: HTML.p(''),
});

@@ -358,3 +379,2 @@ public readonly tag = this.dom.tag;

Create a helper function of APIs for i18n.
Typed-DOM provides `mutate`, `connect`, and `disconnect` events.

@@ -361,0 +381,0 @@ ```ts

@@ -337,8 +337,7 @@ import { Event } from 'spica/global';

listeners: El[] | undefined = this[privates.listeners].values,
isConnected = listeners.length !== 0 && this.isConnected,
): void {
if (listeners.length === 0) return;
if (listeners !== this[privates.listeners].values && !isConnected) return;
if (listeners !== this[privates.listeners].values && !this.isConnected) return;
for (const listener of listeners) {
listener.element[proxy].dispatchConnectionEvent(void 0, isConnected);
listener.element[proxy].dispatchConnectionEvent();
getListeners(listener)?.connect && listener.element.dispatchEvent(new Event('connect', { bubbles: false, cancelable: true }));

@@ -349,8 +348,7 @@ }

listeners: El[] | undefined = this[privates.listeners].values,
isConnected = listeners.length !== 0 && this.isConnected,
): void {
if (listeners.length === 0) return;
if (listeners !== this[privates.listeners].values && !isConnected) return;
if (listeners !== this[privates.listeners].values && !this.isConnected) return;
for (const listener of listeners) {
listener.element[proxy].dispatchDisconnectionEvent(void 0, isConnected);
listener.element[proxy].dispatchDisconnectionEvent();
getListeners(listener)?.disconnect && listener.element.dispatchEvent(new Event('disconnect', { bubbles: false, cancelable: true }));

@@ -357,0 +355,0 @@ }

@@ -137,2 +137,14 @@ import { Symbol, document } from 'spica/global';

el.setAttribute(name, value);
if (name.startsWith('on')) {
const type = name.slice(2).toLowerCase();
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${type}`;
prop in el
? el[prop] ??= (ev: NodeEvent<E>) => ev.returnValue
: el[prop] ??= '';
}
}
continue;

@@ -144,4 +156,4 @@ case 'function':

if (!name.startsWith('on')) throw new Error(`TypedDOM: Attribute names for event listeners must start with "on" but got "${name}".`);
const eventname = name.slice(2).toLowerCase();
el.addEventListener(eventname, value, {
const type = name.slice(2).toLowerCase();
el.addEventListener(type, value, {
passive: [

@@ -154,9 +166,9 @@ 'wheel',

'touchcancel',
].includes(eventname),
].includes(type),
});
switch (eventname) {
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${eventname}`;
const prop = `on${type}`;
prop in el

@@ -163,0 +175,0 @@ ? el[prop] ??= (ev: NodeEvent<E>) => ev.returnValue

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

import { bind, delegate, listen, once, wait, currentTarget } from './listener';
import { bind, delegate, listen, once, currentTarget } from './listener';
import { Shadow, HTML } from '../builder';

@@ -82,4 +82,9 @@

assert(cnt === 0 && ++cnt);
once(el, 'click', () => void assert(cnt === 1 && ++cnt) || done());
once(el, 'click', () => void assert(cnt === 2 && ++cnt) || done());
});
once(el, 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
assert(cnt === 1 && ++cnt);
});
document.createDocumentFragment().appendChild(el);

@@ -97,32 +102,12 @@ el.click();

assert(cnt === 0 && ++cnt);
once(dom.element, 'click', () => void assert(cnt === 1 && ++cnt) || done());
once(dom.element, 'click', () => void assert(cnt === 2 && ++cnt) || done());
});
document.createDocumentFragment().appendChild(dom.element);
dom.children[0].element.click();
dom.children[0].element.click();
});
});
describe('wait', () => {
it('bind', done => {
const el = HTML.a().element;
wait(el, 'click').then(ev => {
once(dom.element, 'a', 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
done();
assert(cnt === 1 && ++cnt);
});
document.createDocumentFragment().appendChild(el);
el.click();
});
it('delegate', done => {
const dom = Shadow.section([HTML.a()]);
wait(dom.element, 'a', 'click').then(ev => {
assert(ev instanceof Event);
assert(ev[currentTarget] === ev.currentTarget);
done();
});
document.createDocumentFragment().appendChild(dom.element);
dom.children[0].element.click();
dom.children[0].element.click();
});

@@ -129,0 +114,0 @@

@@ -13,49 +13,95 @@ import { AtomicPromise } from 'spica/promise';

export function listen<T extends keyof WindowEventMap>(target: Window, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: ((ev: Event) => void) | T, c: boolean | AddEventListenerOptions | ((ev: Event) => void) = false, d: AddEventListenerOptions = {}): () => undefined {
return typeof b === 'string'
? delegate(target as Document, a, b as keyof ElementEventMap, c as () => void, d)
: bind(target as Element, a as keyof ElementEventMap, b, c as boolean);
export function listen<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function listen<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: T | string,
type: T | ((ev: Event) => void),
listener?: boolean | AddEventListenerOptions | ((ev: Event) => void),
option?: boolean | AddEventListenerOptions,
): () => undefined {
return typeof type === 'string'
? delegate(target as Document, selector, type as keyof ElementEventMap, listener as () => void, option)
: bind(target as Element, selector as keyof ElementEventMap, type, listener as boolean);
}
export function once<T extends keyof WindowEventMap>(target: Window, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function once<T extends keyof WindowEventMap>(target: Window, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function once<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function once<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function once<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function once<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<MathMLElementEventMap[T]>;
export function once<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<MathMLElementEventMap[T]>;
export function once<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof ElementEventMap>(target: Element, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function once<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function once<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function once<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function once<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: ((ev: Event) => void) | T, c: boolean | AddEventListenerOptions | ((ev: Event) => void) = false, d: AddEventListenerOptions = {}): () => undefined {
return typeof b === 'string'
? delegate(target as Document, a, b as keyof ElementEventMap, c as () => void, { ...typeof d === 'boolean' ? { capture: d } : d, once: true })
: bind(target as Element, a as keyof ElementEventMap, b, { ...typeof c === 'boolean' ? { capture: c } : c, once: true });
}
export function wait<T extends keyof WindowEventMap>(target: Window, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<WindowEventMap[T]>;
export function wait<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<DocumentEventMap[T]>;
export function wait<T extends keyof HTMLElementEventMap>(target: HTMLElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function wait<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function wait<T extends keyof ElementEventMap>(target: Element, type: T, option?: boolean | AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function wait<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<HTMLElementEventMap[T]>;
export function wait<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<SVGElementEventMap[T]>;
export function wait<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, option?: AddEventListenerOptions): AtomicPromise<ElementEventMap[T]>;
export function wait<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, a: T | string, b: T | boolean | AddEventListenerOptions = false, c: AddEventListenerOptions = {}): AtomicPromise<Event> {
export function once<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: T | string,
type?: T | boolean | AddEventListenerOptions | ((ev: Event) => void),
listener?: boolean | AddEventListenerOptions | ((ev: Event) => void),
option?: boolean | AddEventListenerOptions,
): (() => undefined) | AtomicPromise<Event> {
switch (typeof type) {
case 'string':
switch (typeof listener) {
case 'function':
return delegate(target as Document, selector, type as keyof ElementEventMap, listener as () => void, { ...typeof option === 'boolean' ? { capture: option } : option, once: true });
case 'object':
option = { ...listener, once: true };
break;
default:
option = { once: true };
}
return new AtomicPromise(resolve =>
void delegate(target as Element, selector, type as keyof ElementEventMap, resolve, option));
case 'function':
return bind(target as Element, selector as keyof ElementEventMap, type, { ...typeof listener === 'boolean' ? { capture: listener } : listener, once: true });
case 'object':
option = { ...type, once: true };
break;
default:
option = { once: true };
}
return new AtomicPromise(resolve =>
typeof b === 'string'
? once(target as Document, a, b as keyof ElementEventMap, resolve, c)
: once(target as Element, a as keyof ElementEventMap, resolve, b));
void bind(target as Element, selector as keyof ElementEventMap, resolve, option));
}
export function delegate<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | HTMLElement, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | SVGElement, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option: AddEventListenerOptions = {}): () => undefined {
return bind(target as Element, type, ev => {
export function delegate<T extends keyof WindowEventMap>(target: Window, selector: string, type: T, listener: (ev: WindowEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof DocumentEventMap>(target: Document | ShadowRoot, selector: string, type: T, listener: (ev: DocumentEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof HTMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: HTMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof SVGElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof MathMLElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof ElementEventMap>(target: Document | ShadowRoot | Element, selector: string, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function delegate<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
selector: string,
type: T,
listener: (ev: Event) => void,
option?: boolean | AddEventListenerOptions,
): () => undefined {
return bind(target as Element, type as keyof ElementEventMap, ev => {
assert(ev.target instanceof Element);

@@ -66,4 +112,4 @@ assert(ev.composedPath()[0] instanceof Element);

: (ev.target as Element)?.closest(selector);
cx && once(cx, type, e => { e === ev && listener(ev); }, option);
}, { ...option, capture: true });
cx && once(cx, type as keyof ElementEventMap, e => { e === ev && listener(ev); }, option);
}, { ...typeof option === 'boolean' ? { capture: true } : option, capture: true });
}

@@ -75,4 +121,19 @@

export function bind<T extends keyof SVGElementEventMap>(target: SVGElement, type: T, listener: (ev: SVGElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof MathMLElementEventMap>(target: MathMLElement, type: T, listener: (ev: MathMLElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof ElementEventMap>(target: Element, type: T, listener: (ev: ElementEventMap[T]) => void, option?: boolean | AddEventListenerOptions): () => undefined;
export function bind<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(target: Window | Document | ShadowRoot | Element, type: T, listener: (ev: Event) => void, option: boolean | AddEventListenerOptions = false): () => undefined {
export function bind<T extends keyof WindowEventMap | keyof DocumentEventMap | keyof ElementEventMap>(
target: Window | Document | ShadowRoot | Element,
type: T,
listener: (ev: Event) => void,
option?: boolean | AddEventListenerOptions,
): () => undefined {
switch (type) {
case 'mutate':
case 'connect':
case 'disconnect':
const prop = `on${type}`;
prop in target
? target[prop] ??= (ev: Event) => ev.returnValue
: target[prop] ??= '';
}
target.addEventListener(type, handler, option);

@@ -79,0 +140,0 @@ return singleton(() => void target.removeEventListener(type, handler, option));

import { Shadow, HTML, SVG, El, Attrs, shadow, html } from '../..';
import { Coroutine } from 'spica/coroutine';
import { Sequence } from 'spica/sequence';
import { wait } from 'spica/timer';

@@ -403,2 +404,5 @@ declare global {

]);
assert.deepStrictEqual(
dom.children.map(v => v.element),
[...dom.element.children]);
dom.children = [

@@ -424,2 +428,5 @@ dom.children[1],

]);
assert.deepStrictEqual(
dom.children.map(v => v.element),
[...dom.element.children]);
});

@@ -460,2 +467,5 @@

]);
assert.deepStrictEqual(
[...Object.values(dom.children)].map(v => v.element),
[...dom.element.children]);
dom.children = {

@@ -481,2 +491,15 @@ a: dom.children.a,

[...dom.element.children]);
doc.children = [];
assert.deepStrictEqual(
Object.entries(dom.children).map(([k, v]) => [k, v.children]),
[
['a', 'aAa'],
['b', 'cCc'],
['c', 'bBb'],
['d', 'fFf'],
['e', 'gGg'],
]);
assert.deepStrictEqual(
[...Object.values(dom.children)].map(v => v.element),
[...dom.element.children]);
});

@@ -565,3 +588,3 @@

it('component coroutine', function () {
it('component coroutine', async function () {
class Component extends Coroutine implements El {

@@ -571,15 +594,18 @@ constructor() {

assert(this.element);
assert(this.children);
for (const child of this.children) {
child.children = child.children.toUpperCase();
let count = 0;
this.children = `${count}`;
while (true) {
if (!this.element.isConnected) {
await new Promise(resolve =>
this.element.addEventListener('connect', resolve, { once: true }));
}
this.children = `${++count}`;
yield;
await new Promise(resolve => setTimeout(resolve, 100));
}
}, { trigger: 'element', capacity: 0 });
assert(this.children[0].children === 'ITEM');
}, { trigger: 'element' });
}
private readonly dom = Shadow.section({
private readonly dom = Shadow.section({ onconnect: '' }, {
style: HTML.style(':scope { color: red; }'),
content: HTML.ul([
HTML.li('item'),
]),
content: HTML.p(''),
});

@@ -597,8 +623,15 @@ public readonly tag = this.dom.tag;

const dom = new Component();
assert(dom.children[0].children === 'ITEM');
dom.children = [
HTML.li('item'),
];
assert(dom.children[0].children === 'item');
assert(HTML.div([dom]));
assert(dom.children === '0');
doc.children = [dom];
await 0;
assert(dom.children === '1');
await wait(110);
assert(dom.children === '2');
doc.children = [];
await wait(110);
assert(dom.children === '2');
doc.children = [dom];
await 0;
assert(dom.children === '3');
doc.children = [];
});

@@ -605,0 +638,0 @@

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

import { API, Shadow, HTML, SVG, NS, shadow, frag, html, svg, text, element, define, append, prepend, defrag, listen, once, wait, bind, delegate, currentTarget, querySelector, querySelectorAll, identity } from '../..';
import { API, Shadow, HTML, SVG, NS, shadow, frag, html, svg, text, element, define, append, prepend, defrag, listen, once, bind, delegate, currentTarget, querySelector, querySelectorAll, identity } from '../..';

@@ -78,6 +78,2 @@ describe('Interface: Package', function () {

it('wait', function () {
assert(typeof wait === 'function');
});
it('bind', function () {

@@ -84,0 +80,0 @@ assert(typeof bind === 'function');

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc