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

@milkdown/ctx

Package Overview
Dependencies
Maintainers
1
Versions
72
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@milkdown/ctx - npm Package Compare versions

Comparing version 6.5.4 to 7.0.0-next.0

./lib/index.es.js

13

lib/context/container.d.ts

@@ -1,8 +0,9 @@

import type { Slice, SliceValue } from './slice';
export interface Container {
readonly sliceMap: Map<symbol, SliceValue>;
readonly getSlice: <T, N extends string = string>(slice: Slice<T, N> | N) => SliceValue<T, N>;
readonly removeSlice: <T, N extends string = string>(slice: Slice<T, N> | N) => void;
import type { Slice, SliceType } from './slice';
export type SliceMap = Map<symbol, Slice>;
export declare class Container {
sliceMap: SliceMap;
get: <T, N extends string = string>(slice: N | SliceType<T, N>) => Slice<T, N>;
remove: <T, N extends string = string>(slice: N | SliceType<T, N>) => void;
has: <T, N extends string = string>(slice: N | SliceType<T, N>) => boolean;
}
export declare const createContainer: () => Container;
//# sourceMappingURL=container.d.ts.map

@@ -1,16 +0,19 @@

export interface SliceValue<T = unknown, N extends string = string> {
import type { SliceMap } from './container';
export declare class Slice<T = any, N extends string = string> {
#private;
readonly type: SliceType<T, N>;
constructor(container: SliceMap, value: T, type: SliceType<T, N>);
set: (value: T) => void;
get: () => T;
update: (updater: (prev: T) => T) => void;
}
export declare class SliceType<T = any, N extends string = string> {
readonly id: symbol;
readonly name: N;
readonly set: (value: T) => void;
readonly get: () => T;
readonly update: (updater: (prev: T) => T) => void;
}
export type SliceMap = Map<symbol, SliceValue>;
export interface Slice<T, N extends string = string> {
readonly id: symbol;
readonly sliceName: N;
readonly _typeInfo: () => T;
(container: SliceMap, resetValue?: T): SliceValue<T>;
readonly _defaultValue: T;
constructor(value: T, name: N);
create(container: SliceMap, value?: T): Slice<T, N>;
}
export declare const createSlice: <T, N extends string = string>(value: T, name: N) => Slice<T, N>;
export declare const createSlice: <T = any, N extends string = string>(value: T, name: N) => SliceType<T, N>;
//# sourceMappingURL=slice.d.ts.map
export * from './context';
export * from './plugin';
export * from './timing';
export * from './timer';
//# sourceMappingURL=index.d.ts.map

@@ -1,151 +0,156 @@

var P = Object.defineProperty;
var k = (e, t, i) => t in e ? P(e, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : e[t] = i;
var r = (e, t, i) => (k(e, typeof t != "symbol" ? t + "" : t, i), i), T = (e, t, i) => {
if (!t.has(e))
throw TypeError("Cannot " + i);
var g = Object.defineProperty;
var v = (i, t, e) => t in i ? g(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
var s = (i, t, e) => (v(i, typeof t != "symbol" ? t + "" : t, e), e), y = (i, t, e) => {
if (!t.has(i))
throw TypeError("Cannot " + e);
};
var n = (e, t, i) => (T(e, t, "read from private field"), i ? i.call(e) : t.get(e)), u = (e, t, i) => {
if (t.has(e))
var r = (i, t, e) => (y(i, t, "read from private field"), e ? e.call(i) : t.get(i)), o = (i, t, e) => {
if (t.has(i))
throw TypeError("Cannot add the same private member more than once");
t instanceof WeakSet ? t.add(e) : t.set(e, i);
}, h = (e, t, i, s) => (T(e, t, "write to private field"), s ? s.call(e, i) : t.set(e, i), i);
import { contextNotFound as M, ctxCallOutOfScope as b, timerNotFound as x } from "@milkdown/exception";
const F = () => {
const e = /* @__PURE__ */ new Map();
return { sliceMap: e, getSlice: (s) => {
const o = typeof s == "string" ? [...e.values()].find((c) => c.name === s) : e.get(s.id);
if (!o) {
const c = typeof s == "string" ? s : s.sliceName;
throw M(c);
}
return o;
}, removeSlice: (s) => {
const o = typeof s == "string" ? [...e.values()].find((c) => c.name === s) : e.get(s.id);
!o || e.delete(o.id);
} };
}, j = (e) => Array.isArray(e) ? [...e] : typeof e == "object" ? { ...e } : e, I = (e, t) => {
const i = Symbol(`Context-${t}`), s = (o, c = j(e)) => {
let a = c;
const g = {
name: t,
id: i,
set: (m) => {
a = m;
},
get: () => a,
update: (m) => {
a = m(a);
t instanceof WeakSet ? t.add(i) : t.set(i, e);
}, a = (i, t, e, n) => (y(i, t, "write to private field"), n ? n.call(i, e) : t.set(i, e), e);
import { contextNotFound as w, ctxCallOutOfScope as M, timerNotFound as T } from "@milkdown/exception";
class b {
constructor() {
s(this, "sliceMap", /* @__PURE__ */ new Map());
s(this, "get", (t) => {
const e = typeof t == "string" ? [...this.sliceMap.values()].find((n) => n.type.name === t) : this.sliceMap.get(t.id);
if (!e) {
const n = typeof t == "string" ? t : t.name;
throw w(n);
}
return e;
});
s(this, "remove", (t) => {
const e = typeof t == "string" ? [...this.sliceMap.values()].find((n) => n.type.name === t) : this.sliceMap.get(t.id);
!e || this.sliceMap.delete(e.type.id);
});
s(this, "has", (t) => typeof t == "string" ? [...this.sliceMap.values()].some((e) => e.type.name === t) : this.sliceMap.has(t.id));
}
}
var h;
class x {
constructor(t, e, n) {
s(this, "type");
o(this, h, void 0);
s(this, "set", (t) => {
a(this, h, t);
});
s(this, "get", () => r(this, h));
s(this, "update", (t) => {
a(this, h, t(r(this, h)));
});
this.type = n, a(this, h, e), t.set(n.id, this);
}
}
h = new WeakMap();
class C {
constructor(t, e) {
s(this, "id");
s(this, "name");
s(this, "_typeInfo");
s(this, "_defaultValue");
this.id = Symbol(`Context-${e}`), this.name = e, this._defaultValue = t, this._typeInfo = () => {
throw M();
};
return o.set(i, g), g;
};
return s.sliceName = t, s.id = i, s._typeInfo = () => {
throw b();
}, s;
};
var p, v;
class O {
constructor(t, i) {
u(this, p, void 0);
u(this, v, void 0);
r(this, "use", (t) => n(this, p).getSlice(t));
r(this, "get", (t) => this.use(t).get());
r(this, "set", (t, i) => this.use(t).set(i));
r(this, "update", (t, i) => this.use(t).update(i));
r(this, "timing", (t) => n(this, v).get(t));
r(this, "done", (t) => this.timing(t).done());
r(this, "wait", (t) => this.timing(t)());
r(this, "waitTimers", async (t) => {
await Promise.all(this.get(t).map((i) => this.wait(i)));
}
create(t, e = this._defaultValue) {
return new x(t, e, this);
}
}
const L = (i, t) => new C(i, t);
var c, m;
class P {
constructor(t, e) {
o(this, c, void 0);
o(this, m, void 0);
s(this, "inject", (t, e) => {
const n = t.create(r(this, c).sliceMap);
return e != null && n.set(e), this;
});
h(this, p, t), h(this, v, i);
s(this, "remove", (t) => (r(this, c).remove(t), this));
s(this, "record", (t) => (t.create(r(this, m).store), this));
s(this, "clearTimer", (t) => (r(this, m).remove(t), this));
s(this, "isInjected", (t) => r(this, c).has(t));
s(this, "isRecorded", (t) => r(this, m).has(t));
s(this, "use", (t) => r(this, c).get(t));
s(this, "get", (t) => this.use(t).get());
s(this, "set", (t, e) => this.use(t).set(e));
s(this, "update", (t, e) => this.use(t).update(e));
s(this, "timer", (t) => r(this, m).get(t));
s(this, "done", (t) => this.timer(t).done());
s(this, "wait", (t) => this.timer(t).start());
s(this, "waitTimers", async (t) => {
await Promise.all(this.get(t).map((e) => this.wait(e)));
});
a(this, c, t), a(this, m, e);
}
}
p = new WeakMap(), v = new WeakMap();
var l, d;
class $ {
constructor(t, i) {
u(this, l, void 0);
u(this, d, void 0);
r(this, "inject", (t, i) => (t(n(this, l).sliceMap, i), this));
r(this, "remove", (t) => (n(this, l).removeSlice(t), this));
r(this, "record", (t) => (t(n(this, d).store), this));
r(this, "clearTimer", (t) => (n(this, d).remove(t), this));
r(this, "use", (t) => n(this, l).getSlice(t));
r(this, "get", (t) => this.use(t).get());
r(this, "set", (t, i) => this.use(t).set(i));
r(this, "update", (t, i) => this.use(t).update(i));
r(this, "timing", (t) => n(this, d).get(t));
r(this, "wait", (t) => this.timing(t)());
r(this, "done", (t) => this.timing(t).done());
r(this, "waitTimers", async (t) => {
await Promise.all(this.get(t).map((i) => this.wait(i)));
c = new WeakMap(), m = new WeakMap();
class V {
constructor() {
s(this, "store", /* @__PURE__ */ new Map());
s(this, "get", (t) => {
const e = this.store.get(t.id);
if (!e)
throw T(t.name);
return e;
});
h(this, l, t), h(this, d, i);
s(this, "remove", (t) => {
this.store.delete(t.id);
});
s(this, "has", (t) => this.store.has(t.id));
}
}
l = new WeakMap(), d = new WeakMap();
var f, w;
class _ {
constructor(t, i) {
u(this, f, void 0);
u(this, w, void 0);
r(this, "clearTimer", (t) => (n(this, w).remove(t), this));
r(this, "remove", (t) => (n(this, f).removeSlice(t), this));
h(this, f, t), h(this, w, i);
var l, u, d, p, f;
class S {
constructor(t, e) {
s(this, "type");
o(this, l, null);
o(this, u, null);
o(this, d, void 0);
s(this, "start", () => (r(this, l) ?? a(this, l, new Promise((t, e) => {
a(this, u, (n) => {
n instanceof CustomEvent && n.detail.id === r(this, d) && (r(this, p).call(this), n.stopImmediatePropagation(), t());
}), r(this, f).call(this, () => {
r(this, p).call(this), e(new Error(`Timing ${this.type.name} timeout.`));
}), addEventListener(this.type.name, r(this, u));
})), r(this, l)));
s(this, "done", () => {
const t = new CustomEvent(this.type.name, { detail: { id: r(this, d) } });
dispatchEvent(t);
});
o(this, p, () => {
r(this, u) && removeEventListener(this.type.name, r(this, u));
});
o(this, f, (t) => {
setTimeout(() => {
t();
}, this.type.timeout);
});
a(this, d, Symbol(e.name)), this.type = e, t.set(e.id, this);
}
}
f = new WeakMap(), w = new WeakMap();
var y, S;
class q {
constructor(t, i) {
u(this, y, void 0);
u(this, S, void 0);
r(this, "inject", (t, i) => (t(n(this, y).sliceMap, i), this));
r(this, "record", (t) => (t(n(this, S).store), this));
h(this, y, t), h(this, S, i);
l = new WeakMap(), u = new WeakMap(), d = new WeakMap(), p = new WeakMap(), f = new WeakMap();
class E {
constructor(t, e = 3e3) {
s(this, "id");
s(this, "name");
s(this, "timeout");
s(this, "create", (t) => new S(t, this));
this.id = Symbol(`Timer-${t}`), this.name = t, this.timeout = e;
}
}
y = new WeakMap(), S = new WeakMap();
const z = () => {
const e = /* @__PURE__ */ new Map();
return {
store: e,
get: (s) => {
const o = e.get(s.id);
if (!o)
throw x(s.timerName);
return o;
},
remove: (s) => {
e.delete(s.id);
}
};
}, B = (e, t = 3e3) => {
const i = Symbol("Timer"), s = (o) => {
let c = null, a;
const g = Symbol(e), m = () => c != null ? c : c = new Promise((C, N) => {
a = (E) => {
E instanceof CustomEvent && E.detail.id === g && (removeEventListener(e, a), E.stopImmediatePropagation(), C());
}, setTimeout(() => {
N(new Error(`Timing ${e} timeout.`)), removeEventListener(e, a);
}, t), addEventListener(e, a);
});
return m.done = () => {
const C = new CustomEvent(e, { detail: { id: g } });
dispatchEvent(C);
}, o.set(i, m), m;
};
return s.id = i, s.timerName = e, s;
};
const $ = (i, t = 3e3) => new E(i, t);
export {
O as Ctx,
$ as Env,
_ as Post,
q as Pre,
z as createClock,
F as createContainer,
I as createSlice,
B as createTimer
V as Clock,
b as Container,
P as Ctx,
x as Slice,
C as SliceType,
S as Timer,
E as TimerType,
L as createSlice,
$ as createTimer
};
//# sourceMappingURL=index.es.js.map

@@ -1,73 +0,21 @@

import type { Container, Slice, SliceValue } from '../context';
import type { Clock, Timer } from '../timing';
/**
* The ctx object that can be accessed in plugin and action.
*/
import type { Container, Slice, SliceType } from '../context';
import type { Clock, TimerType } from '../timer';
export declare class Ctx {
#private;
constructor(container: Container, clock: Clock);
/**
* Get the slice instance.
*
* @param slice - The slice or slice name that needs to be used.
* @returns The slice instance.
*/
readonly use: <T, N extends string = string>(slice: N | Slice<T, N>) => SliceValue<T, N>;
/**
* Get the slice value.
*
* @param slice - The slice needs to be used.
* @returns The slice value.
*/
readonly get: <T, N extends string>(slice: Slice<T, N>) => T;
/**
* Set the slice value.
*
* @param slice - The slice needs to be used.
* @param value - The default value.
* @returns
*/
readonly set: <T, N extends string>(slice: Slice<T, N>, value: T) => void;
/**
* Update the slice by its current value.
*
* @example
* ```
* update(NumberSlice, x => x + 1);
* ```
*
* @param slice - The slice needs to be used.
* @param updater - The update function, gets current value as parameter and returns new value.
* @returns
*/
readonly update: <T, N extends string>(slice: Slice<T, N>, updater: (prev: T) => T) => void;
/**
* Get the timer instance.
*
* @param timer - The timer needs to be used.
* @returns The timer instance.
*/
readonly timing: (timer: Timer) => import("../timing").Timing;
/**
* Finish a timer
*
* @param timer - The timer needs to be finished.
* @returns
*/
readonly done: (timer: Timer) => void;
/**
* Wait for a timer to finish.
*
* @param timer - The timer needs to be used.
* @returns A promise that will be resolved when timer finish.
*/
readonly wait: (timer: Timer) => Promise<void>;
/**
* Wait for a list of timers in target slice to be all finished.
*
* @param slice - The slice that holds a list of timer.
* @returns A promise that will be resolved when all timers finish.
*/
readonly waitTimers: (slice: Slice<Timer[]>) => Promise<void>;
readonly inject: <T>(sliceType: SliceType<T, string>, value?: T | undefined) => this;
readonly remove: <T, N extends string = string>(sliceType: N | SliceType<T, N>) => this;
readonly record: (timerType: TimerType) => this;
readonly clearTimer: (timerType: TimerType) => this;
readonly isInjected: <T, N extends string = string>(sliceType: N | SliceType<T, N>) => boolean;
readonly isRecorded: (timerType: TimerType) => boolean;
readonly use: <T, N extends string = string>(sliceType: N | SliceType<T, N>) => Slice<T, N>;
readonly get: <T, N extends string>(sliceType: SliceType<T, N>) => T;
readonly set: <T, N extends string>(sliceType: SliceType<T, N>, value: T) => void;
readonly update: <T, N extends string>(sliceType: SliceType<T, N>, updater: (prev: T) => T) => void;
readonly timer: (timer: TimerType) => import("../timer").Timer;
readonly done: (timer: TimerType) => void;
readonly wait: (timer: TimerType) => Promise<void>;
readonly waitTimers: (slice: SliceType<TimerType[]>) => Promise<void>;
}
//# sourceMappingURL=ctx.d.ts.map
export * from './ctx';
export * from './env';
export * from './post';
export * from './pre';
export * from './types';
//# sourceMappingURL=index.d.ts.map
import type { Ctx } from './ctx';
import type { Post } from './post';
import type { Pre } from './pre';
export type Cleanup = (post: Post) => void | Promise<void>;
export type HandlerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>;
export type CtxHandler = (ctx: Ctx) => HandlerReturnType;
export type MilkdownPlugin = (pre: Pre) => CtxHandler;
export type Cleanup = () => void | Promise<void>;
export type RunnerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>;
export type CtxRunner = () => RunnerReturnType;
export type MilkdownPlugin = (ctx: Ctx) => CtxRunner;
//# sourceMappingURL=types.d.ts.map
{
"name": "@milkdown/ctx",
"type": "module",
"version": "6.5.4",
"version": "7.0.0-next.0",
"license": "MIT",

@@ -29,3 +29,3 @@ "repository": {

"tslib": "^2.4.0",
"@milkdown/exception": "6.5.4"
"@milkdown/exception": "7.0.0-next.0"
},

@@ -32,0 +32,0 @@ "nx": {

/* Copyright 2021, Milkdown by Mirone. */
import { describe, expect, it } from 'vitest'
import { createContainer, createSlice } from '.'
import { Container, SliceType } from '.'
describe('context/container', () => {
it('sliceMap', () => {
const container = createContainer()
const container = new Container()

@@ -14,30 +14,43 @@ expect(container.sliceMap).toEqual(new Map())

it('getSlice', () => {
const container = createContainer()
const ctx = createSlice(0, 'num')
const container = new Container()
const ctx = new SliceType(0, 'num')
ctx(container.sliceMap)
ctx.create(container.sliceMap)
expect(container.getSlice(ctx).id).toBe(ctx.id)
expect(container.getSlice(ctx).get()).toBe(0)
expect(container.get(ctx).type.id).toBe(ctx.id)
expect(container.get(ctx).get()).toBe(0)
container.getSlice(ctx).set(10)
container.get(ctx).set(10)
expect(container.getSlice(ctx).get()).toBe(10)
expect(container.get(ctx).get()).toBe(10)
expect(container.getSlice<number>('num').get()).toBe(10)
expect(container.get<number>('num').get()).toBe(10)
})
it('removeSlice', () => {
const container = createContainer()
const ctx = createSlice(0, 'num')
const container = new Container()
const ctx = new SliceType(0, 'num')
ctx(container.sliceMap)
ctx.create(container.sliceMap)
expect(container.getSlice(ctx).id).toBe(ctx.id)
expect(container.getSlice(ctx).get()).toBe(0)
expect(container.get(ctx).type.id).toBe(ctx.id)
expect(container.get(ctx).get()).toBe(0)
container.removeSlice(ctx)
container.remove(ctx)
expect(() => container.getSlice(ctx)).toThrow()
expect(() => container.get(ctx)).toThrow()
})
it('hasSlice', () => {
const container = new Container()
const ctx = new SliceType(0, 'num')
ctx.create(container.sliceMap)
expect(container.has(ctx)).toBeTruthy()
container.remove(ctx)
expect(container.has(ctx)).toBeFalsy()
})
})
/* Copyright 2021, Milkdown by Mirone. */
import { contextNotFound } from '@milkdown/exception'
import type { Slice, SliceValue } from './slice'
import type { Slice, SliceType } from './slice'
export interface Container {
readonly sliceMap: Map<symbol, SliceValue>
readonly getSlice: <T, N extends string = string>(slice: Slice<T, N> | N) => SliceValue<T, N>
readonly removeSlice: <T, N extends string = string>(slice: Slice<T, N> | N) => void
}
/// @internal
export type SliceMap = Map<symbol, Slice>
export const createContainer = (): Container => {
const sliceMap: Map<symbol, SliceValue> = new Map()
/// Container is a map of slices.
export class Container {
/// @internal
sliceMap: SliceMap = new Map()
const getSlice = <T, N extends string = string>(slice: Slice<T, N> | N): SliceValue<T, N> => {
const context
= typeof slice === 'string' ? [...sliceMap.values()].find(x => x.name === slice) : sliceMap.get(slice.id)
/// Get a slice from the container by slice type or slice name.
get = <T, N extends string = string>(slice: SliceType<T, N> | N): Slice<T, N> => {
const context = typeof slice === 'string'
? [...this.sliceMap.values()].find(x => x.type.name === slice)
: this.sliceMap.get(slice.id)
if (!context) {
const name = typeof slice === 'string' ? slice : slice.sliceName
const name = typeof slice === 'string' ? slice : slice.name
throw contextNotFound(name)
}
return context as SliceValue<T, N>
return context as Slice<T, N>
}
const removeSlice = <T, N extends string = string>(slice: Slice<T, N> | N): void => {
const context
= typeof slice === 'string' ? [...sliceMap.values()].find(x => x.name === slice) : sliceMap.get(slice.id)
/// Remove a slice from the container by slice type or slice name.
remove = <T, N extends string = string>(slice: SliceType<T, N> | N): void => {
const context = typeof slice === 'string'
? [...this.sliceMap.values()].find(x => x.type.name === slice)
: this.sliceMap.get(slice.id)

@@ -33,6 +36,12 @@ if (!context)

sliceMap.delete(context.id)
this.sliceMap.delete(context.type.id)
}
return { sliceMap, getSlice, removeSlice }
/// Check if the container has a slice by slice type or slice name.
has = <T, N extends string = string>(slice: SliceType<T, N> | N): boolean => {
if (typeof slice === 'string')
return [...this.sliceMap.values()].some(x => x.type.name === slice)
return this.sliceMap.has(slice.id)
}
}
/* Copyright 2021, Milkdown by Mirone. */
import { describe, expect, it } from 'vitest'
import { createSlice } from './slice'
import { SliceType } from './slice'
describe('context/slice', () => {
it('primitive slice', () => {
const factory = createSlice(0, 'primitive')
const sliceType = new SliceType(0, 'primitive')
const map = new Map()
const ctx = factory(map)
const ctx = sliceType.create(map)
expect(factory.sliceName).toBe('primitive')
expect(factory.id).toBeTypeOf('symbol')
expect(ctx.name).toBe('primitive')
expect(ctx.id).toBe(factory.id)
expect(sliceType.name).toBe('primitive')
expect(sliceType.id).toBeTypeOf('symbol')
expect(ctx.type.name).toBe('primitive')
expect(ctx.type.id).toBe(sliceType.id)

@@ -27,28 +27,28 @@ expect(ctx.get()).toBe(0)

it('structure slice', () => {
const factory = createSlice<number[]>([], 'structure')
expect(factory.sliceName).toBe('structure')
const sliceType = new SliceType<number[], 'structure'>([], 'structure')
expect(sliceType.name).toBe('structure')
const map1 = new Map()
const ctx1 = factory(map1)
expect(ctx1.id).toBe(factory.id)
const slice1 = sliceType.create(map1)
expect(slice1.type.id).toBe(sliceType.id)
expect(ctx1.name).toBe('structure')
expect(slice1.type.name).toBe('structure')
const map2 = new Map()
const ctx2 = factory(map2)
const slice2 = sliceType.create(map2)
expect(ctx2.name).toBe('structure')
expect(ctx2.id).toBe(factory.id)
expect(slice2.type.name).toBe('structure')
expect(slice2.type.id).toBe(sliceType.id)
expect(ctx1.get()).toEqual([])
ctx1.set([1])
expect(ctx1.get()).toEqual([1])
expect(slice1.get()).toEqual([])
slice1.set([1])
expect(slice1.get()).toEqual([1])
expect(ctx2.get()).toEqual([])
expect(slice2.get()).toEqual([])
ctx1.update(x => x.concat(3))
expect(ctx1.get()).toEqual([1, 3])
slice1.update(x => x.concat(3))
expect(slice1.get()).toEqual([1, 3])
expect(ctx2.get()).toEqual([])
expect(slice2.get()).toEqual([])
})
})
/* Copyright 2021, Milkdown by Mirone. */
import { ctxCallOutOfScope } from '@milkdown/exception'
import type { SliceMap } from './container'
import { shallowClone } from './shallow-clone'
/// Slice is a value of slice type.
export class Slice<T = any, N extends string = string> {
/// The type of the slice.
readonly type: SliceType<T, N>
export interface SliceValue<T = unknown, N extends string = string> {
readonly id: symbol
readonly name: N
readonly set: (value: T) => void
readonly get: () => T
readonly update: (updater: (prev: T) => T) => void
/// @internal
#value: T
/// @internal
constructor(container: SliceMap, value: T, type: SliceType<T, N>) {
this.type = type
this.#value = value
container.set(type.id, this)
}
/// Set the value of the slice.
set = (value: T) => {
this.#value = value
}
/// Get the value of the slice.
get = () => this.#value
/// Update the value of the slice with a callback.
update = (updater: (prev: T) => T) => {
this.#value = updater(this.#value)
}
}
export type SliceMap = Map<symbol, SliceValue>
export interface Slice<T, N extends string = string> {
/// Slice type can be used to create slices in different containers.
export class SliceType<T = any, N extends string = string> {
/// The unique id of the slice type.
readonly id: symbol
readonly sliceName: N
/// The name of the slice type.
readonly name: N
/// @internal
readonly _typeInfo: () => T
(container: SliceMap, resetValue?: T): SliceValue<T>
}
/// @internal
readonly _defaultValue: T
export const createSlice = <T, N extends string = string>(value: T, name: N): Slice<T, N> => {
const id = Symbol(`Context-${name}`)
const factory = (container: SliceMap, resetValue = shallowClone(value)) => {
let inner = resetValue
const context: SliceValue<T> = {
name,
id,
set: (next) => {
inner = next
},
get: () => inner,
update: (updater) => {
inner = updater(inner)
},
/// Create a slice type with a default value and a name.
/// The name should be unique in the container.
constructor(value: T, name: N) {
this.id = Symbol(`Context-${name}`)
this.name = name
this._defaultValue = value
this._typeInfo = (): T => {
throw ctxCallOutOfScope()
}
container.set(id, context as SliceValue)
return context
}
factory.sliceName = name
factory.id = id
factory._typeInfo = (): T => {
throw ctxCallOutOfScope()
/// Create a slice with a container.
/// You can also pass a value to override the default value.
create(container: SliceMap, value: T = this._defaultValue): Slice<T, N> {
return new Slice(container, value, this)
}
}
return factory
}
/// Create a slice type with a default value and a name.
/// This is equivalent to `new SliceType(value, name)`.
export const createSlice = <T = any, N extends string = string>(value: T, name: N) => new SliceType(value, name)

@@ -5,2 +5,2 @@ /* Copyright 2021, Milkdown by Mirone. */

export * from './plugin'
export * from './timing'
export * from './timer'
/* Copyright 2021, Milkdown by Mirone. */
import type { Container, Slice, SliceValue } from '../context'
import type { Clock, Timer } from '../timing'
import type { Container, Slice, SliceType } from '../context'
import type { Clock, TimerType } from '../timer'
/**
* The ctx object that can be accessed in plugin and action.
*/
/// The ctx object that can be accessed in plugin and action.
export class Ctx {
/// @internal
#container: Container
/// @internal
#clock: Clock
/// Create a ctx object with container and clock.
constructor(container: Container, clock: Clock) {

@@ -17,76 +18,67 @@ this.#container = container

/**
* Get the slice instance.
*
* @param slice - The slice or slice name that needs to be used.
* @returns The slice instance.
*/
readonly use = <T, N extends string = string>(slice: Slice<T, N> | N): SliceValue<T, N> =>
this.#container.getSlice(slice)
/// Add a slice into the ctx.
readonly inject = <T>(sliceType: SliceType<T>, value?: T) => {
const slice = sliceType.create(this.#container.sliceMap)
if (value != null)
slice.set(value)
/**
* Get the slice value.
*
* @param slice - The slice needs to be used.
* @returns The slice value.
*/
readonly get = <T, N extends string>(slice: Slice<T, N>) => this.use(slice).get()
return this
}
/**
* Set the slice value.
*
* @param slice - The slice needs to be used.
* @param value - The default value.
* @returns
*/
readonly set = <T, N extends string>(slice: Slice<T, N>, value: T) => this.use(slice).set(value)
/// Remove a slice from the ctx.
readonly remove = <T, N extends string = string>(sliceType: SliceType<T, N> | N) => {
this.#container.remove(sliceType)
return this
}
/**
* Update the slice by its current value.
*
* @example
* ```
* update(NumberSlice, x => x + 1);
* ```
*
* @param slice - The slice needs to be used.
* @param updater - The update function, gets current value as parameter and returns new value.
* @returns
*/
readonly update = <T, N extends string>(slice: Slice<T, N>, updater: (prev: T) => T) =>
this.use(slice).update(updater)
/// Add a timer into the ctx.
readonly record = (timerType: TimerType) => {
timerType.create(this.#clock.store)
return this
}
/**
* Get the timer instance.
*
* @param timer - The timer needs to be used.
* @returns The timer instance.
*/
readonly timing = (timer: Timer) => this.#clock.get(timer)
/// Remove a timer from the ctx.
readonly clearTimer = (timerType: TimerType) => {
this.#clock.remove(timerType)
return this
}
/**
* Finish a timer
*
* @param timer - The timer needs to be finished.
* @returns
*/
readonly done = (timer: Timer) => this.timing(timer).done()
/// Check if the ctx has a slice.
readonly isInjected = <T, N extends string = string>(sliceType: SliceType<T, N> | N) => this.#container.has(sliceType)
/**
* Wait for a timer to finish.
*
* @param timer - The timer needs to be used.
* @returns A promise that will be resolved when timer finish.
*/
readonly wait = (timer: Timer) => this.timing(timer)()
/// Check if the ctx has a timer.
readonly isRecorded = (timerType: TimerType) => this.#clock.has(timerType)
/**
* Wait for a list of timers in target slice to be all finished.
*
* @param slice - The slice that holds a list of timer.
* @returns A promise that will be resolved when all timers finish.
*/
readonly waitTimers = async (slice: Slice<Timer[]>) => {
/// Get a slice from the ctx.
readonly use = <T, N extends string = string>(sliceType: SliceType<T, N> | N): Slice<T, N> =>
this.#container.get(sliceType)
/// Get a slice value from the ctx.
readonly get = <T, N extends string>(sliceType: SliceType<T, N>) => this.use(sliceType).get()
/// Get a slice value from the ctx.
readonly set = <T, N extends string>(sliceType: SliceType<T, N>, value: T) => this.use(sliceType).set(value)
/// Update a slice value from the ctx by a callback.
readonly update = <T, N extends string>(sliceType: SliceType<T, N>, updater: (prev: T) => T) =>
this.use(sliceType).update(updater)
/// Get a timer from the ctx.
readonly timer = (timer: TimerType) => this.#clock.get(timer)
/// Resolve a timer from the ctx.
readonly done = (timer: TimerType) => this.timer(timer).done()
/// Start a timer from the ctx.
readonly wait = (timer: TimerType) => this.timer(timer).start()
/// Start a list of timers from the ctx, the list is stored in a slice in the ctx.
/// This is equivalent to
///
/// ```typescript
/// Promise.all(ctx.get(slice).map(x => ctx.wait(x))).
/// ```
readonly waitTimers = async (slice: SliceType<TimerType[]>) => {
await Promise.all(this.get(slice).map(x => this.wait(x)))
}
}
/* Copyright 2021, Milkdown by Mirone. */
export * from './ctx'
export * from './env'
export * from './post'
export * from './pre'
export * from './types'
/* Copyright 2021, Milkdown by Mirone. */
import type { Ctx } from './ctx'
import type { Post } from './post'
import type { Pre } from './pre'
export type Cleanup = (post: Post) => void | Promise<void>
/// @internal
export type Cleanup = () => void | Promise<void>
export type HandlerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>
/// @internal
export type RunnerReturnType = void | Promise<void> | Cleanup | Promise<Cleanup>
export type CtxHandler = (ctx: Ctx) => HandlerReturnType
/// @internal
export type CtxRunner = () => RunnerReturnType
export type MilkdownPlugin = (pre: Pre) => CtxHandler
/// The type of the plugin.
///
/// ```typescript
/// // A full plugin example
/// const plugin1 = (ctx: Ctx) => {
/// // setup
/// return async () => {
/// // run
/// return async () => {
/// // cleanup
/// }
/// }
/// }
///
/// // A plugin doesn't need to return a cleanup function
/// const plugin2 = (ctx: Ctx) => {
/// // setup
/// return async () => {
/// // run
/// }
/// }
///
/// // A plugin doesn't need to be async
/// const plugin3 = (ctx: Ctx) => {
/// // setup
/// return () => {
/// // run
/// }
/// }
/// ```
export type MilkdownPlugin = (ctx: Ctx) => CtxRunner

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

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