Initial work on novy-syndicate
This commit is contained in:
commit
84569b8c90
|
@ -0,0 +1,5 @@
|
||||||
|
*.d.ts
|
||||||
|
*.js
|
||||||
|
*.js.map
|
||||||
|
package-lock.json
|
||||||
|
node_modules/
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { Value } from 'preserves';
|
||||||
|
|
||||||
|
type Assertion = Value<Peer>;
|
||||||
|
|
||||||
|
type ActorId = number;
|
||||||
|
|
||||||
|
type ExitReason = null | { ok: true } | { ok: false, err: Error };
|
||||||
|
|
||||||
|
let nextActorId: ActorId = 0;
|
||||||
|
|
||||||
|
type AssertionHandle = object;
|
||||||
|
|
||||||
|
const assert = Symbol('assert');
|
||||||
|
const retract = Symbol('retract');
|
||||||
|
const message = Symbol('message');
|
||||||
|
|
||||||
|
type RestParameters<T extends (arg: any, ...args: any) => any> =
|
||||||
|
T extends (arg: any, ...args: infer P) => any ? P : never;
|
||||||
|
|
||||||
|
type Event =
|
||||||
|
| { type: typeof assert, args: RestParameters<Facet[typeof assert]> }
|
||||||
|
| { type: typeof retract, args: RestParameters<Facet[typeof retract]> }
|
||||||
|
| { type: typeof message, args: RestParameters<Facet[typeof message]> }
|
||||||
|
|
||||||
|
type Action = Event;
|
||||||
|
|
||||||
|
class Peer {
|
||||||
|
readonly actor: Actor;
|
||||||
|
readonly target: Facet;
|
||||||
|
|
||||||
|
constructor(actor: Actor, target: Facet) {
|
||||||
|
this.actor = actor;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Facet {
|
||||||
|
[assert](turn: Turn, assertion: Assertion, handle: AssertionHandle): void;
|
||||||
|
[retract](turn: Turn, handle: AssertionHandle): void;
|
||||||
|
[message](turn: Turn, message: Assertion): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Actor {
|
||||||
|
readonly id: ActorId = nextActorId++;
|
||||||
|
exitReason: ExitReason = null;
|
||||||
|
|
||||||
|
scheduleTurn(target: Facet, turn: Turn) {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (this.alive) {
|
||||||
|
try {
|
||||||
|
const event = turn.event;
|
||||||
|
(target as any)[event.type](turn, ...event.args);
|
||||||
|
// ^ This is safe. Try replacing it with the following to see:
|
||||||
|
//
|
||||||
|
// switch (event.type) {
|
||||||
|
// case assert: target[event.type](turn, ...event.args); break;
|
||||||
|
// case retract: target[event.type](turn, ...event.args); break;
|
||||||
|
// case message: target[event.type](turn, ...event.args); break;
|
||||||
|
// }
|
||||||
|
turn.finish();
|
||||||
|
} catch (err) {
|
||||||
|
this.terminateWith({ ok: false, err });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get alive(): boolean {
|
||||||
|
return this.exitReason === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.terminateWith({ ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
terminateWith(reason: Exclude<ExitReason, null>) {
|
||||||
|
if (this.alive) {
|
||||||
|
this.exitReason = reason;
|
||||||
|
// TODO cleanup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Turn {
|
||||||
|
readonly recipient: Actor;
|
||||||
|
readonly event: Event;
|
||||||
|
readonly actions: Map<Actor, Map<Facet, Action[]>> = new Map();
|
||||||
|
|
||||||
|
constructor(recipient: Actor, event: Event) {
|
||||||
|
this.recipient = recipient;
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueueAction(peer: Peer, action: Action) {
|
||||||
|
let targetMap = this.actions.get(peer.actor);
|
||||||
|
if (targetMap === void 0) {
|
||||||
|
targetMap = new Map();
|
||||||
|
this.actions.set(peer.actor, targetMap);
|
||||||
|
}
|
||||||
|
let actions = targetMap.get(peer.target);
|
||||||
|
if (actions === void 0) {
|
||||||
|
actions = [];
|
||||||
|
targetMap.set(peer.target, actions);
|
||||||
|
}
|
||||||
|
actions.push(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
this.actions.forEach((targetMap, actor) => {
|
||||||
|
targetMap.forEach((actions, target) => {
|
||||||
|
actions.forEach(action => actor.scheduleTurn(target, new Turn(actor, action)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
type ValidSelector = string | number | symbol;
|
||||||
|
|
||||||
|
export type EventMessage<Selector extends ValidSelector, Args extends any[]> = {
|
||||||
|
selector: Selector,
|
||||||
|
args: Args,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RequestMessage<Selector extends ValidSelector, Args extends any[], Result> = {
|
||||||
|
selector: Selector,
|
||||||
|
args: Args,
|
||||||
|
callback: (result: Result) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Message<Selector extends ValidSelector, Args extends any[], Result> =
|
||||||
|
void extends Result ? EventMessage<Selector, Args> : RequestMessage<Selector, Args, Result>;
|
||||||
|
|
||||||
|
// export type EventMessage<Selector extends ValidSelector, Args extends any[]> = {
|
||||||
|
// selector: Selector,
|
||||||
|
// args: Args,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// export type RequestMessage<Selector extends ValidSelector, Args extends any[], Result> = {
|
||||||
|
// selector: Selector,
|
||||||
|
// args: Args,
|
||||||
|
// callback: (result: Result) => void,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// export type Message<Selector extends ValidSelector, Args extends any[], Result> =
|
||||||
|
// | EventMessage<Selector, Args>
|
||||||
|
// | RequestMessage<Selector, Args, Result>;
|
||||||
|
//
|
||||||
|
// type Messages1<I, ContextArgs extends any[]> = {
|
||||||
|
// [K in keyof I]: (I[K] extends (...args: [...ContextArgs, ...infer P]) => infer Q
|
||||||
|
// ? (void extends Q ? EventMessage<K, P> : RequestMessage<K, P, Q>)
|
||||||
|
// : never);
|
||||||
|
// };
|
||||||
|
|
||||||
|
type Messages1<I, ContextArgs extends any[]> = {
|
||||||
|
[K in keyof I]: (I[K] extends (...args: [...ContextArgs, ...infer P]) => infer Q
|
||||||
|
? Message<K, P, Q>
|
||||||
|
: never);
|
||||||
|
};
|
||||||
|
|
||||||
|
// type Proj1<I, K extends keyof I> = I[K];
|
||||||
|
// type Proj<I> = Proj1<I, keyof I>;
|
||||||
|
// export type Messages<I, ContextArgs extends any[] = []> = Proj<Messages1<I, ContextArgs>>;
|
||||||
|
|
||||||
|
export type Messages<I, ContextArgs extends any[] = []> = Messages1<I, ContextArgs>[keyof I];
|
||||||
|
|
||||||
|
export type Methods<M extends { selector: ValidSelector }, ContextArgs extends any[] = []> = {
|
||||||
|
[S in M['selector']]: (
|
||||||
|
M extends RequestMessage<S, infer P, infer R> ? (...args: [...ContextArgs, ...P]) => R :
|
||||||
|
M extends EventMessage<S, infer P> ? (... args: [...ContextArgs, ...P]) => void :
|
||||||
|
never);
|
||||||
|
};
|
||||||
|
|
||||||
|
// interface I {
|
||||||
|
// m1(a: string, b: number): boolean;
|
||||||
|
// m2(): void;
|
||||||
|
// m3(n: number): void;
|
||||||
|
// m4(x: [string, string]): { k: string, j: string };
|
||||||
|
// m5(a: string, b: string[]): number;
|
||||||
|
// v: string;
|
||||||
|
// w: number;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// const a = { a(): string { console.log('in a'); return 'hi'; }, b(): void { console.log('in b'); } };
|
||||||
|
// type A = typeof a;
|
||||||
|
// type A1 = Messages<A>;
|
||||||
|
// type A2 = Methods<A1>;
|
||||||
|
// const b: A2 = a;
|
||||||
|
|
||||||
|
// export function performRequest<S extends ValidSelector, A extends any[], R, ContextArgs extends any[] = []>(
|
||||||
|
// i: { [s in S]: (...args: [...ContextArgs, ...A]) => R },
|
||||||
|
// m: { selector: S, args: A, callback: (result: R) => void },
|
||||||
|
// ...ctxt: ContextArgs)
|
||||||
|
// : R
|
||||||
|
// {
|
||||||
|
// const r = i[m.selector](...ctxt, ... m.args);
|
||||||
|
// m.callback(r);
|
||||||
|
// return r;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export function performEvent<S extends ValidSelector, A extends any[], ContextArgs extends any[] = []>(
|
||||||
|
// i: { [s in S]: (...args: [...ContextArgs, ...A]) => void },
|
||||||
|
// m: { selector: S, args: A },
|
||||||
|
// ...ctxt: ContextArgs)
|
||||||
|
// : void
|
||||||
|
// {
|
||||||
|
// i[m.selector](...ctxt, ...m.args);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function send<S extends ValidSelector, A extends any[], R>(
|
||||||
|
// i: { [s in S]: (...args: A) => R },
|
||||||
|
// m: { selector: S, args: A, callback?: (result: R) => void })
|
||||||
|
// : R;
|
||||||
|
// function send<S extends ValidSelector, A extends any[], R>(
|
||||||
|
// i: { [s in S]: (...args: A) => void },
|
||||||
|
// m: { selector: S, args: A })
|
||||||
|
// : void;
|
||||||
|
// function send<S extends ValidSelector, A extends any[], R>(
|
||||||
|
// i: { [s in S]: (...args: A) => R },
|
||||||
|
// m: { selector: S, args: A, callback?: (result: R) => void })
|
||||||
|
// : R
|
||||||
|
// // function send<I extends {}, M extends Messages<I>>(i: I, m: M): [M, I] extends [RequestMessage<any, any, infer R>, Methods<M>] ? R : void
|
||||||
|
// {
|
||||||
|
// const r = i[m.selector](... m.args);
|
||||||
|
// m.callback?.(r);
|
||||||
|
// return r;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>, S extends ValidSelector, M extends RequestMessage<S, any, any>, ContextArgs extends any[] = []>(i: I, m: M, ...ctxt: ContextArgs)
|
||||||
|
: [M, I] extends [RequestMessage<S, any, infer R>, Methods<M>] ? R : void;
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>, S extends ValidSelector, M extends EventMessage<S, any>, ContextArgs extends any[] = []>(i: I, m: M, ...ctxt: ContextArgs): void;
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>, S extends ValidSelector, M extends RequestMessage<S, any, any>, R, ContextArgs extends any[] = []>(i: I, m: M, ...ctxt: ContextArgs): R
|
||||||
|
{
|
||||||
|
const r = i[m.selector](...ctxt, ... m.args);
|
||||||
|
m.callback?.(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function perform<I extends Methods<M>, S extends ValidSelector, M extends RequestMessage<S, any, any>>(i: I, m: M)
|
||||||
|
// : [M, I] extends [RequestMessage<S, any, infer R>, Methods<M>] ? R : void;
|
||||||
|
// function perform<I extends Methods<M>, S extends ValidSelector, M extends EventMessage<S, any>>(i: I, m: M): void;
|
||||||
|
// function perform<I extends Methods<M>, S extends ValidSelector, M extends RequestMessage<S, any, any>>(i: I, m: M): R
|
||||||
|
// {
|
||||||
|
// const r = i[m.selector](... m.args);
|
||||||
|
// m.callback?.(r);
|
||||||
|
// return r;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const aa = perform(a, { selector: 'a', args: [], callback: (_r: string) => {} });
|
||||||
|
// const bb = perform(a, { selector: 'b', args: [], callback: (_r: void) => { console.log('bb'); } });
|
||||||
|
// const bb2 = perform(a, { selector: 'b', args: [] });
|
||||||
|
// // perform({ a(): string { return 'hi' } }, { selector: 'a', args: [123], callback: (_r: string) => {} });
|
||||||
|
|
||||||
|
// type Q = Messages<I>;
|
||||||
|
// type M = Methods<Q>;
|
||||||
|
// type N = M;
|
||||||
|
// type R = Messages<Methods<Q>>;
|
||||||
|
// type S = R;
|
||||||
|
|
||||||
|
// const x: Q = { selector: 'm2', args: [] };
|
|
@ -0,0 +1,87 @@
|
||||||
|
type ValidSelector = string | number | symbol
|
||||||
|
|
||||||
|
export type Message<Selector extends ValidSelector, Args extends any[], Result> =
|
||||||
|
Args extends never[]
|
||||||
|
? { selector: Selector, args: [], callback: (result: Result) => void }
|
||||||
|
: { selector: Selector, args: Args, callback: (result: Result) => void }
|
||||||
|
|
||||||
|
type MessagesProduct<I> = {
|
||||||
|
[K in keyof I]: (I[K] extends (...args: infer P) => infer Q ? Message<K, P, Q> : never);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Messages<I> = MessagesProduct<I>[keyof I]
|
||||||
|
|
||||||
|
export type Methods<M extends { selector: ValidSelector }> = {
|
||||||
|
[S in M['selector']]: (
|
||||||
|
M extends Message<S, infer P, infer R> ? (...args: P) => R :
|
||||||
|
never);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function perform<I extends Methods<M>,
|
||||||
|
S extends ValidSelector,
|
||||||
|
M extends Message<S, any, any>>(
|
||||||
|
i: I,
|
||||||
|
m: M): void
|
||||||
|
{
|
||||||
|
m.callback(i[m.selector](... m.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
m1(a: string, b: number): boolean;
|
||||||
|
m2(): void;
|
||||||
|
m3(n: number): void;
|
||||||
|
m4(x: [string, string]): { k: string, j: string };
|
||||||
|
m5(a: string, b: string[]): number;
|
||||||
|
v: string;
|
||||||
|
w: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type M = Messages<I>
|
||||||
|
// type M =
|
||||||
|
// | Message<"m1", [a: string, b: number], boolean>
|
||||||
|
// | Message<"m2", [], void>
|
||||||
|
// | Message<"m3", [n: number], void>
|
||||||
|
// | Message<"m4", [x: [string, string]], { k: string; j: string }>
|
||||||
|
// | Message<"m5", [a: string, b: string[]], number>
|
||||||
|
// type M =
|
||||||
|
// | { selector: "m1", args: [a: string, b: number], callback: (result: boolean) => void }
|
||||||
|
// | { selector: "m2", args: [], callback: (result: void) => void }
|
||||||
|
// | { selector: "m3", args: [n: number], callback: (result: void) => void }
|
||||||
|
// | { selector: "m4", args: [x: [string, string]], callback: (result: { k: string; j: string }) => void }
|
||||||
|
// | { selector: "m5", args: [a: string, b: string[]], callback: (result: number) => void }
|
||||||
|
// type M =
|
||||||
|
// | { selector: "m1", args: [string, number], callback: (result: boolean) => void }
|
||||||
|
// | { selector: "m2", args: [], callback: (result: void) => void }
|
||||||
|
// | { selector: "m3", args: [number], callback: (result: void) => void }
|
||||||
|
// | { selector: "m4", args: [[string, string]], callback: (result: { k: string; j: string }) => void }
|
||||||
|
// | { selector: "m5", args: [string, string[]], callback: (result: number) => void }
|
||||||
|
|
||||||
|
type I2 = Methods<M>
|
||||||
|
// type I2 = {
|
||||||
|
// m1: (a: string, b: number) => boolean;
|
||||||
|
// m2: () => void;
|
||||||
|
// m3: (n: number) => void;
|
||||||
|
// m4: (x: [string, string]) => { k: string; j: string };
|
||||||
|
// m5: (a: string, b: string[]) => number;
|
||||||
|
// };
|
||||||
|
|
||||||
|
type X = I2
|
||||||
|
|
||||||
|
const a = {
|
||||||
|
a(): string { console.log('in a'); return 'hi'; },
|
||||||
|
b(): void { console.log('in b'); },
|
||||||
|
c(x: number): number { console.log('in c:', x); return x * 2; },
|
||||||
|
v(x: number, y: string, z: boolean): string { return `x ${x} y ${y} z ${z}`; },
|
||||||
|
};
|
||||||
|
type A = typeof a;
|
||||||
|
type A1 = Messages<A>;
|
||||||
|
type A2 = Methods<A1>;
|
||||||
|
const b: A2 = a;
|
||||||
|
|
||||||
|
const aa = perform(a, { selector: 'a', args: [], callback: (_r: string) => {} });
|
||||||
|
const bb = perform(a, { selector: 'b', args: [], callback: (_r: void) => { console.log('bb'); } });
|
||||||
|
const cc = perform(a, { selector: 'c', args: [123], callback: (r: number) => { console.log('cc', r); } });
|
||||||
|
const vv = perform(a, { selector: 'v', args: [123, 'hi', true], callback: (r: string) => { console.log('vv', r); } });
|
||||||
|
perform({ a(): string { return 'hi' } }, { selector: 'a', args: [123], callback: (_r: string) => { console.log('x'); } });
|
|
@ -0,0 +1,67 @@
|
||||||
|
// This Tuple type (and tuple() function) is a hack to induce
|
||||||
|
// TypeScript to infer tuple types rather than array types. (Source:
|
||||||
|
// https://github.com/microsoft/TypeScript/issues/27179#issuecomment-422606990)
|
||||||
|
//
|
||||||
|
// Without it, [123, 'hi', true] will often get the type (string |
|
||||||
|
// number | boolean)[] instead of [number, string, boolean].
|
||||||
|
//
|
||||||
|
export type Tuple = any[] | [any];
|
||||||
|
export const tuple = <A extends Tuple>(... args: A) => args;
|
||||||
|
|
||||||
|
// Type ValidSelector captures TypeScript's notion of a valid object
|
||||||
|
// property name.
|
||||||
|
//
|
||||||
|
export type ValidSelector = string | number | symbol;
|
||||||
|
|
||||||
|
export type EventMessage<Selector extends ValidSelector, Args extends any[]> =
|
||||||
|
{ selector: Selector, args: Args };
|
||||||
|
|
||||||
|
export type RequestMessage<Selector extends ValidSelector, Args extends any[], Result extends Exclude<any, void>> =
|
||||||
|
{ selector: Selector, args: Args, callback: (result: Result) => void };
|
||||||
|
|
||||||
|
export type Message<Selector extends ValidSelector, Args extends any[], Result> =
|
||||||
|
void extends Result ? EventMessage<Selector, Args> : RequestMessage<Selector, Args, Result>;
|
||||||
|
|
||||||
|
// Function message() is needed for similar reasons to tuple() above:
|
||||||
|
// to help TypeScript infer the correct literal type for the selector
|
||||||
|
// (as well as the arguments).
|
||||||
|
//
|
||||||
|
export const message = <S extends ValidSelector, A extends Tuple, R>(m: Message<S, A, R>) => m;
|
||||||
|
|
||||||
|
type MessagesProduct<I, ContextArgs extends any[]> = {
|
||||||
|
[K in keyof I]: (I[K] extends (...args: [...ContextArgs, ...infer P]) => infer Q
|
||||||
|
? Message<K, P, Q>
|
||||||
|
: never);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Messages<I, ContextArgs extends any[] = []> = MessagesProduct<I, ContextArgs>[keyof I];
|
||||||
|
|
||||||
|
export type Methods<M extends { selector: ValidSelector }, ContextArgs extends any[] = []> = {
|
||||||
|
[S in M['selector']]: (
|
||||||
|
M extends RequestMessage<S, infer P, infer R>
|
||||||
|
? (void extends R ? never : (...args: [...ContextArgs, ...P]) => R)
|
||||||
|
: (M extends EventMessage<S, infer P>
|
||||||
|
? (...args: [...ContextArgs, ...P]) => void
|
||||||
|
: never));
|
||||||
|
};
|
||||||
|
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>,
|
||||||
|
S extends ValidSelector,
|
||||||
|
M extends RequestMessage<S, Tuple, any>,
|
||||||
|
ContextArgs extends any[] = []>
|
||||||
|
(i: I, m: M, ...ctxt: ContextArgs): (M extends RequestMessage<S, Tuple, infer R> ? R : never);
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>,
|
||||||
|
S extends ValidSelector,
|
||||||
|
M extends EventMessage<S, Tuple>,
|
||||||
|
ContextArgs extends any[] = []>
|
||||||
|
(i: I, m: M, ...ctxt: ContextArgs): void;
|
||||||
|
export function perform<I extends Methods<M, ContextArgs>,
|
||||||
|
S extends ValidSelector,
|
||||||
|
M extends RequestMessage<S, Tuple, any>,
|
||||||
|
ContextArgs extends any[] = []>
|
||||||
|
(i: I, m: M, ...ctxt: ContextArgs): any
|
||||||
|
{
|
||||||
|
const r = i[m.selector](...ctxt, ... m.args);
|
||||||
|
m.callback?.(r);
|
||||||
|
return r;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { tuple, message, Methods, Messages, perform } from './interfaces.js';
|
||||||
|
|
||||||
|
const m = message({
|
||||||
|
selector: 'b',
|
||||||
|
args: [123, 'hi', true],
|
||||||
|
callback: (result: number) => console.log('result:', result),
|
||||||
|
});
|
||||||
|
const m2 = message({
|
||||||
|
selector: 'c',
|
||||||
|
args: [],
|
||||||
|
// callback: (result: void) => console.log('result:', result),
|
||||||
|
});
|
||||||
|
type X = Methods<typeof m | typeof m2, [string]>;
|
||||||
|
type Y = Messages<X, [string]>;
|
||||||
|
|
||||||
|
perform({
|
||||||
|
a(ctxt: string, x: number): string { return `${ctxt}(${x + 1})`; },
|
||||||
|
b(ctxt: string, y: number): number { return y * 2; },
|
||||||
|
z(ctxt: string, ...v: number[]) { return 3; },
|
||||||
|
v(ctxt: string, x: number, y: string, z: boolean): string {
|
||||||
|
console.log('in v');
|
||||||
|
return `ctxt ${ctxt} x ${x} y ${y} z ${z}`;
|
||||||
|
},
|
||||||
|
w(ctxt: string, m: [string, number]) { console.log('w', ctxt, m); },
|
||||||
|
w2(ctxt: string, x: number, ...m: [string, number][]) { console.log('w2', ctxt, x, m); },
|
||||||
|
},
|
||||||
|
|
||||||
|
// {
|
||||||
|
// selector: 'w2',
|
||||||
|
// args: [99, tuple('hi', 123), tuple('hi', 123)],
|
||||||
|
// callback: (result: void) => console.log('result:', result)
|
||||||
|
// },
|
||||||
|
|
||||||
|
{
|
||||||
|
selector: 'v',
|
||||||
|
args: [123, 'hi', true],
|
||||||
|
callback: (result: string) => console.log('result:', result)
|
||||||
|
},
|
||||||
|
|
||||||
|
'C');
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"preserves": "^0.5.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2017",
|
||||||
|
"lib": ["es2017", "dom"],
|
||||||
|
"declaration": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"rootDir": ".",
|
||||||
|
"outDir": ".",
|
||||||
|
"declarationDir": ".",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "commonjs",
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"],
|
||||||
|
"exclude": []
|
||||||
|
}
|
Loading…
Reference in New Issue