@prosekit/core 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor-CfkZ4TNU.d.ts +748 -0
- package/dist/editor-CfkZ4TNU.d.ts.map +1 -0
- package/dist/{editor-DbMrpnmL.js → editor-CizSwUN8.js} +102 -192
- package/dist/editor-CizSwUN8.js.map +1 -0
- package/dist/prosekit-core-test.d.ts +20 -19
- package/dist/prosekit-core-test.d.ts.map +1 -0
- package/dist/prosekit-core-test.js +4 -5
- package/dist/prosekit-core-test.js.map +1 -0
- package/dist/prosekit-core.d.ts +782 -757
- package/dist/prosekit-core.d.ts.map +1 -0
- package/dist/prosekit-core.js +30 -45
- package/dist/prosekit-core.js.map +1 -0
- package/package.json +14 -11
- package/src/commands/add-mark.ts +53 -0
- package/src/commands/expand-mark.ts +96 -0
- package/src/commands/insert-default-block.spec.ts +102 -0
- package/src/commands/insert-default-block.ts +49 -0
- package/src/commands/insert-node.ts +71 -0
- package/src/commands/insert-text.ts +24 -0
- package/src/commands/remove-mark.ts +54 -0
- package/src/commands/remove-node.ts +43 -0
- package/src/commands/select-all.ts +16 -0
- package/src/commands/set-block-type.ts +64 -0
- package/src/commands/set-node-attrs.ts +68 -0
- package/src/commands/toggle-mark.ts +65 -0
- package/src/commands/toggle-node.ts +47 -0
- package/src/commands/toggle-wrap.spec.ts +35 -0
- package/src/commands/toggle-wrap.ts +42 -0
- package/src/commands/unset-block-type.spec.ts +49 -0
- package/src/commands/unset-block-type.ts +84 -0
- package/src/commands/unset-mark.spec.ts +35 -0
- package/src/commands/unset-mark.ts +38 -0
- package/src/commands/wrap.ts +50 -0
- package/src/editor/action.spec.ts +143 -0
- package/src/editor/action.ts +248 -0
- package/src/editor/editor.spec.ts +186 -0
- package/src/editor/editor.ts +563 -0
- package/src/editor/union.spec.ts +108 -0
- package/src/editor/union.ts +47 -0
- package/src/editor/with-priority.ts +25 -0
- package/src/error.ts +28 -0
- package/src/extensions/clipboard-serializer.ts +107 -0
- package/src/extensions/command.ts +121 -0
- package/src/extensions/default-state.spec.ts +60 -0
- package/src/extensions/default-state.ts +76 -0
- package/src/extensions/doc.ts +31 -0
- package/src/extensions/events/doc-change.ts +34 -0
- package/src/extensions/events/dom-event.spec.ts +70 -0
- package/src/extensions/events/dom-event.ts +117 -0
- package/src/extensions/events/editor-event.ts +293 -0
- package/src/extensions/events/focus.spec.ts +50 -0
- package/src/extensions/events/focus.ts +28 -0
- package/src/extensions/events/plugin-view.ts +132 -0
- package/src/extensions/history.ts +81 -0
- package/src/extensions/keymap-base.ts +60 -0
- package/src/extensions/keymap.spec.ts +89 -0
- package/src/extensions/keymap.ts +96 -0
- package/src/extensions/mark-spec.spec.ts +177 -0
- package/src/extensions/mark-spec.ts +181 -0
- package/src/extensions/mark-view-effect.ts +85 -0
- package/src/extensions/mark-view.ts +43 -0
- package/src/extensions/node-spec.spec.ts +224 -0
- package/src/extensions/node-spec.ts +199 -0
- package/src/extensions/node-view-effect.ts +85 -0
- package/src/extensions/node-view.ts +43 -0
- package/src/extensions/paragraph.ts +61 -0
- package/src/extensions/plugin.ts +91 -0
- package/src/extensions/text.ts +34 -0
- package/src/facets/base-extension.ts +54 -0
- package/src/facets/command.ts +21 -0
- package/src/facets/facet-extension.spec.ts +173 -0
- package/src/facets/facet-extension.ts +53 -0
- package/src/facets/facet-node.spec.ts +265 -0
- package/src/facets/facet-node.ts +185 -0
- package/src/facets/facet-types.ts +9 -0
- package/src/facets/facet.spec.ts +76 -0
- package/src/facets/facet.ts +84 -0
- package/src/facets/root.ts +44 -0
- package/src/facets/schema-spec.ts +30 -0
- package/src/facets/schema.ts +26 -0
- package/src/facets/state.ts +57 -0
- package/src/facets/union-extension.ts +41 -0
- package/src/index.ts +302 -0
- package/src/test/index.ts +4 -0
- package/src/test/test-builder.ts +68 -0
- package/src/test/test-editor.spec.ts +104 -0
- package/src/test/test-editor.ts +113 -0
- package/src/testing/index.ts +283 -0
- package/src/testing/keyboard.ts +5 -0
- package/src/types/any-function.ts +4 -0
- package/src/types/assert-type-equal.ts +8 -0
- package/src/types/attrs.ts +32 -0
- package/src/types/base-node-view-options.ts +33 -0
- package/src/types/dom-node.ts +1 -0
- package/src/types/extension-command.ts +52 -0
- package/src/types/extension-mark.ts +15 -0
- package/src/types/extension-node.ts +15 -0
- package/src/types/extension.spec.ts +56 -0
- package/src/types/extension.ts +168 -0
- package/src/types/model.ts +54 -0
- package/src/types/object-entries.ts +13 -0
- package/src/types/pick-string-literal.spec.ts +10 -0
- package/src/types/pick-string-literal.ts +6 -0
- package/src/types/pick-sub-type.spec.ts +20 -0
- package/src/types/pick-sub-type.ts +6 -0
- package/src/types/priority.ts +12 -0
- package/src/types/setter.ts +4 -0
- package/src/types/simplify-deeper.spec.ts +40 -0
- package/src/types/simplify-deeper.ts +6 -0
- package/src/types/simplify-union.spec.ts +21 -0
- package/src/types/simplify-union.ts +11 -0
- package/src/utils/array-grouping.spec.ts +29 -0
- package/src/utils/array-grouping.ts +25 -0
- package/src/utils/array.ts +21 -0
- package/src/utils/assert.ts +13 -0
- package/src/utils/attrs-match.ts +20 -0
- package/src/utils/can-use-regex-lookbehind.ts +12 -0
- package/src/utils/clsx.spec.ts +14 -0
- package/src/utils/clsx.ts +12 -0
- package/src/utils/collect-children.ts +21 -0
- package/src/utils/collect-nodes.ts +37 -0
- package/src/utils/combine-event-handlers.spec.ts +27 -0
- package/src/utils/combine-event-handlers.ts +27 -0
- package/src/utils/contains-inline-node.ts +17 -0
- package/src/utils/deep-equals.spec.ts +26 -0
- package/src/utils/deep-equals.ts +29 -0
- package/src/utils/default-block-at.ts +15 -0
- package/src/utils/editor-content.spec.ts +47 -0
- package/src/utils/editor-content.ts +77 -0
- package/src/utils/env.ts +6 -0
- package/src/utils/find-parent-node-of-type.ts +29 -0
- package/src/utils/find-parent-node.spec.ts +68 -0
- package/src/utils/find-parent-node.ts +55 -0
- package/src/utils/get-custom-selection.ts +19 -0
- package/src/utils/get-dom-api.ts +56 -0
- package/src/utils/get-id.spec.ts +14 -0
- package/src/utils/get-id.ts +13 -0
- package/src/utils/get-mark-type.ts +20 -0
- package/src/utils/get-node-type.ts +20 -0
- package/src/utils/get-node-types.ts +19 -0
- package/src/utils/includes-mark.ts +18 -0
- package/src/utils/is-at-block-start.ts +26 -0
- package/src/utils/is-in-code-block.ts +18 -0
- package/src/utils/is-mark-absent.spec.ts +53 -0
- package/src/utils/is-mark-absent.ts +42 -0
- package/src/utils/is-mark-active.ts +27 -0
- package/src/utils/is-node-active.ts +25 -0
- package/src/utils/is-subset.spec.ts +12 -0
- package/src/utils/is-subset.ts +11 -0
- package/src/utils/maybe-run.spec.ts +39 -0
- package/src/utils/maybe-run.ts +11 -0
- package/src/utils/merge-objects.spec.ts +30 -0
- package/src/utils/merge-objects.ts +11 -0
- package/src/utils/merge-specs.ts +35 -0
- package/src/utils/object-equal.spec.ts +26 -0
- package/src/utils/object-equal.ts +28 -0
- package/src/utils/output-spec.test.ts +95 -0
- package/src/utils/output-spec.ts +130 -0
- package/src/utils/parse.spec.ts +46 -0
- package/src/utils/parse.ts +321 -0
- package/src/utils/remove-undefined-values.spec.ts +15 -0
- package/src/utils/remove-undefined-values.ts +9 -0
- package/src/utils/set-selection-around.ts +11 -0
- package/src/utils/type-assertion.ts +91 -0
- package/src/utils/unicode.spec.ts +10 -0
- package/src/utils/unicode.ts +4 -0
- package/src/utils/with-skip-code-block.ts +15 -0
- package/dist/editor-CjVyjJqw.d.ts +0 -739
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Attrs } from '@prosekit/pm/model'
|
|
2
|
+
|
|
3
|
+
import type { Extension } from '../types/extension'
|
|
4
|
+
|
|
5
|
+
import { defineNodeSpec } from './node-spec'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export type TextExtension = Extension<{
|
|
11
|
+
Nodes: {
|
|
12
|
+
text: Attrs
|
|
13
|
+
}
|
|
14
|
+
}>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @public
|
|
18
|
+
*
|
|
19
|
+
* @deprecated Use the following import instead:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { defineText } from 'prosekit/extensions/text'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function defineText(): TextExtension {
|
|
26
|
+
console.warn(
|
|
27
|
+
'[prosekit] The `defineText` function from `prosekit/core` is deprecated. Use the following import instead: `import { defineText } from "prosekit/extensions/text"`.',
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return defineNodeSpec({
|
|
31
|
+
name: 'text',
|
|
32
|
+
group: 'inline',
|
|
33
|
+
})
|
|
34
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Schema } from '@prosekit/pm/model'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
Extension,
|
|
5
|
+
ExtensionTyping,
|
|
6
|
+
} from '../types/extension'
|
|
7
|
+
import { Priority } from '../types/priority'
|
|
8
|
+
|
|
9
|
+
import type { Facet } from './facet'
|
|
10
|
+
import type { FacetNode } from './facet-node'
|
|
11
|
+
import type { Tuple5 } from './facet-types'
|
|
12
|
+
import { schemaFacet } from './schema'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export abstract class BaseExtension<T extends ExtensionTyping = ExtensionTyping> implements Extension<T> {
|
|
18
|
+
abstract extension: Extension | Extension[]
|
|
19
|
+
priority?: Priority
|
|
20
|
+
_type?: T
|
|
21
|
+
|
|
22
|
+
private trees: Tuple5<FacetNode | null> = [null, null, null, null, null]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
abstract createTree(priority: Priority): FacetNode
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
getTree(priority?: Priority): FacetNode {
|
|
33
|
+
const pri = priority ?? this.priority ?? Priority.default
|
|
34
|
+
return (this.trees[pri] ||= this.createTree(pri))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
findFacetOutput<I, O>(facet: Facet<I, O>): Tuple5<O | null> | null {
|
|
41
|
+
let node: FacetNode | undefined = this.getTree()
|
|
42
|
+
|
|
43
|
+
for (const index of facet.path) {
|
|
44
|
+
node = node?.children.get(index)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return node?.getOutput() ?? null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get schema(): Schema | null {
|
|
51
|
+
const output = this.findFacetOutput(schemaFacet)
|
|
52
|
+
return output?.find(Boolean)?.schema ?? null
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CommandCreators } from '../types/extension-command'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
defineFacet,
|
|
5
|
+
type Facet,
|
|
6
|
+
} from './facet'
|
|
7
|
+
import {
|
|
8
|
+
rootFacet,
|
|
9
|
+
type RootPayload,
|
|
10
|
+
} from './root'
|
|
11
|
+
|
|
12
|
+
type CommandPayload = CommandCreators
|
|
13
|
+
|
|
14
|
+
export const commandFacet: Facet<CommandPayload, RootPayload> = defineFacet({
|
|
15
|
+
reducer: (inputs) => {
|
|
16
|
+
const commands = Object.assign({}, ...inputs) as CommandPayload
|
|
17
|
+
return { commands }
|
|
18
|
+
},
|
|
19
|
+
parent: rootFacet,
|
|
20
|
+
singleton: true,
|
|
21
|
+
})
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describe,
|
|
3
|
+
expect,
|
|
4
|
+
it,
|
|
5
|
+
vi,
|
|
6
|
+
} from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { Priority } from '../types/priority'
|
|
9
|
+
import { isNotNullish } from '../utils/type-assertion'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
defineFacet,
|
|
13
|
+
Facet,
|
|
14
|
+
} from './facet'
|
|
15
|
+
import { FacetExtensionImpl } from './facet-extension'
|
|
16
|
+
import {
|
|
17
|
+
subtractFacetNode,
|
|
18
|
+
unionFacetNode,
|
|
19
|
+
} from './facet-node'
|
|
20
|
+
import type { FacetReducer } from './facet-types'
|
|
21
|
+
import { UnionExtensionImpl } from './union-extension'
|
|
22
|
+
|
|
23
|
+
describe('facet extension', () => {
|
|
24
|
+
type FooHandler = (foo: string) => void
|
|
25
|
+
type BarHandler = (bar: string) => void
|
|
26
|
+
|
|
27
|
+
interface RootInput {
|
|
28
|
+
onFoo?: FooHandler
|
|
29
|
+
onBar?: BarHandler
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface RootOutput {
|
|
33
|
+
fooHandlers: FooHandler[]
|
|
34
|
+
barHandlers: BarHandler[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type FooInput = FooHandler
|
|
38
|
+
type BarInput = BarHandler
|
|
39
|
+
|
|
40
|
+
const rootReducerImpl: FacetReducer<RootInput, RootOutput> = (input) => {
|
|
41
|
+
const fooHandlers = input.map((i) => i.onFoo).filter(isNotNullish)
|
|
42
|
+
const barHandlers = input.map((i) => i.onBar).filter(isNotNullish)
|
|
43
|
+
return { fooHandlers, barHandlers }
|
|
44
|
+
}
|
|
45
|
+
const rootReducer = vi.fn(rootReducerImpl)
|
|
46
|
+
const rootFacet = new Facet(null, true, rootReducer)
|
|
47
|
+
|
|
48
|
+
// Foo facet uses `reduce` closure to return the same `onFoo` function every time.
|
|
49
|
+
const fooReduce: () => FacetReducer<FooInput, RootInput> = () => {
|
|
50
|
+
let fooHandlers: FooHandler[] | undefined
|
|
51
|
+
|
|
52
|
+
const onFoo = (value: string): void => {
|
|
53
|
+
fooHandlers?.forEach((handler) => handler(value))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (input) => {
|
|
57
|
+
fooHandlers = input
|
|
58
|
+
return { onFoo }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const fooFacet = defineFacet<FooInput, RootInput>({
|
|
62
|
+
parent: rootFacet,
|
|
63
|
+
reduce: fooReduce,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Bar facet uses `reducer` directly thus the `onBar` function is a different instance every time.
|
|
67
|
+
const barReducer: FacetReducer<BarInput, RootInput> = (input) => {
|
|
68
|
+
return {
|
|
69
|
+
onBar: (value: string) => {
|
|
70
|
+
input.forEach((handler) => handler(value))
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const barFacet = defineFacet<BarInput, RootInput>({
|
|
75
|
+
parent: rootFacet,
|
|
76
|
+
reducer: barReducer,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('can merge payloads', () => {
|
|
80
|
+
const fooHandler1: FooHandler = vi.fn()
|
|
81
|
+
const fooExtension1 = new FacetExtensionImpl(fooFacet, [fooHandler1])
|
|
82
|
+
|
|
83
|
+
const fooHandler2: FooHandler = vi.fn()
|
|
84
|
+
const fooExtension2 = new FacetExtensionImpl(fooFacet, [fooHandler2])
|
|
85
|
+
|
|
86
|
+
const barHandler1: BarHandler = vi.fn()
|
|
87
|
+
const barExtension1 = new FacetExtensionImpl(barFacet, [barHandler1])
|
|
88
|
+
|
|
89
|
+
const extension1 = new UnionExtensionImpl([
|
|
90
|
+
fooExtension1,
|
|
91
|
+
fooExtension2,
|
|
92
|
+
barExtension1,
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
const tree = extension1.createTree(Priority.default)
|
|
96
|
+
const rootOutput = tree.getSingletonOutput() as RootOutput
|
|
97
|
+
expect(rootOutput.fooHandlers).toHaveLength(1)
|
|
98
|
+
expect(rootOutput.barHandlers).toHaveLength(1)
|
|
99
|
+
|
|
100
|
+
expect(fooHandler1).toHaveBeenCalledTimes(0)
|
|
101
|
+
expect(fooHandler2).toHaveBeenCalledTimes(0)
|
|
102
|
+
expect(barHandler1).toHaveBeenCalledTimes(0)
|
|
103
|
+
|
|
104
|
+
rootOutput.fooHandlers.forEach((handler) => handler('a'))
|
|
105
|
+
expect(fooHandler1).toHaveBeenCalledWith('a')
|
|
106
|
+
expect(fooHandler1).toHaveBeenCalledTimes(1)
|
|
107
|
+
expect(fooHandler2).toHaveBeenCalledWith('a')
|
|
108
|
+
expect(fooHandler2).toHaveBeenCalledTimes(1)
|
|
109
|
+
|
|
110
|
+
rootOutput.barHandlers.forEach((handler) => handler('b'))
|
|
111
|
+
expect(barHandler1).toHaveBeenCalledWith('b')
|
|
112
|
+
expect(barHandler1).toHaveBeenCalledTimes(1)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('can skip unnecessary update', () => {
|
|
116
|
+
const fooHandler1: FooHandler = vi.fn()
|
|
117
|
+
const fooExtension1 = new FacetExtensionImpl(fooFacet, [fooHandler1])
|
|
118
|
+
|
|
119
|
+
const fooHandler2: FooHandler = vi.fn()
|
|
120
|
+
const fooExtension2 = new FacetExtensionImpl(fooFacet, [fooHandler2])
|
|
121
|
+
|
|
122
|
+
const barHandler1: BarHandler = vi.fn()
|
|
123
|
+
const barExtension1 = new FacetExtensionImpl(barFacet, [barHandler1])
|
|
124
|
+
|
|
125
|
+
const barHandler2: BarHandler = vi.fn()
|
|
126
|
+
const barExtension2 = new FacetExtensionImpl(barFacet, [barHandler2])
|
|
127
|
+
|
|
128
|
+
// Initial the root output with fooExtension1 and barExtension1.
|
|
129
|
+
const rootExtension = new UnionExtensionImpl([fooExtension1, barExtension1])
|
|
130
|
+
let tree = rootExtension.createTree(Priority.default)
|
|
131
|
+
let rootOutput = tree.getSingletonOutput() as RootOutput
|
|
132
|
+
|
|
133
|
+
// Save the initial root output.
|
|
134
|
+
const rootFooHandlers1 = [...rootOutput.fooHandlers]
|
|
135
|
+
const rootBarHandlers1 = [...rootOutput.barHandlers]
|
|
136
|
+
|
|
137
|
+
// Add fooExtension2.
|
|
138
|
+
// This should not trigger any updates to the root output.
|
|
139
|
+
tree = unionFacetNode(tree, fooExtension2.getTree())
|
|
140
|
+
rootOutput = tree.getSingletonOutput() as RootOutput
|
|
141
|
+
const rootFooHandlers2 = [...rootOutput.fooHandlers]
|
|
142
|
+
const rootBarHandlers2 = [...rootOutput.barHandlers]
|
|
143
|
+
expect(rootFooHandlers2).toEqual(rootFooHandlers1)
|
|
144
|
+
expect(rootBarHandlers2).toEqual(rootBarHandlers1)
|
|
145
|
+
|
|
146
|
+
// Add barExtension2.
|
|
147
|
+
// This should change rootOutput.barHandlers
|
|
148
|
+
tree = unionFacetNode(tree, barExtension2.getTree())
|
|
149
|
+
rootOutput = tree.getSingletonOutput() as RootOutput
|
|
150
|
+
const rootFooHandlers3 = [...rootOutput.fooHandlers]
|
|
151
|
+
const rootBarHandlers3 = [...rootOutput.barHandlers]
|
|
152
|
+
expect(rootFooHandlers3).toEqual(rootFooHandlers2)
|
|
153
|
+
expect(rootBarHandlers3).not.toEqual(rootBarHandlers2)
|
|
154
|
+
|
|
155
|
+
// Remove fooExtension1
|
|
156
|
+
// This should not trigger any updates to the root output.
|
|
157
|
+
tree = subtractFacetNode(tree, fooExtension1.getTree())
|
|
158
|
+
rootOutput = tree.getSingletonOutput() as RootOutput
|
|
159
|
+
const rootFooHandlers4 = [...rootOutput.fooHandlers]
|
|
160
|
+
const rootBarHandlers4 = [...rootOutput.barHandlers]
|
|
161
|
+
expect(rootFooHandlers4).toEqual(rootFooHandlers3)
|
|
162
|
+
expect(rootBarHandlers4).toEqual(rootBarHandlers3)
|
|
163
|
+
|
|
164
|
+
// Remove barExtension2
|
|
165
|
+
// This should change rootOutput.barHandlers
|
|
166
|
+
tree = subtractFacetNode(tree, barExtension2.getTree())
|
|
167
|
+
rootOutput = tree.getSingletonOutput() as RootOutput
|
|
168
|
+
const rootFooHandlers5 = [...rootOutput.fooHandlers]
|
|
169
|
+
const rootBarHandlers5 = [...rootOutput.barHandlers]
|
|
170
|
+
expect(rootFooHandlers5).toEqual(rootFooHandlers4)
|
|
171
|
+
expect(rootBarHandlers5).not.toEqual(rootBarHandlers4)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Extension } from '../types/extension'
|
|
2
|
+
import type { Priority } from '../types/priority'
|
|
3
|
+
|
|
4
|
+
import { BaseExtension } from './base-extension'
|
|
5
|
+
import type { Facet } from './facet'
|
|
6
|
+
import { FacetNode } from './facet-node'
|
|
7
|
+
import type { Tuple5 } from './facet-types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export class FacetExtensionImpl<Input, Output> extends BaseExtension {
|
|
13
|
+
declare extension: Extension
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
constructor(
|
|
19
|
+
readonly facet: Facet<Input, Output>,
|
|
20
|
+
readonly payloads: Input[],
|
|
21
|
+
) {
|
|
22
|
+
super()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
createTree(priority: Priority): FacetNode {
|
|
29
|
+
const pri = this.priority ?? priority
|
|
30
|
+
|
|
31
|
+
const inputs: Tuple5<Input[] | null> = [null, null, null, null, null]
|
|
32
|
+
inputs[pri] = [...this.payloads]
|
|
33
|
+
|
|
34
|
+
let node: FacetNode = new FacetNode(this.facet, inputs)
|
|
35
|
+
|
|
36
|
+
while (node.facet.parent) {
|
|
37
|
+
const children = new Map([[node.facet.index, node]])
|
|
38
|
+
node = new FacetNode(node.facet.parent, undefined, children)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return node
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
export function defineFacetPayload<Input>(
|
|
49
|
+
facet: Facet<Input, any>,
|
|
50
|
+
payloads: Input[],
|
|
51
|
+
): Extension {
|
|
52
|
+
return new FacetExtensionImpl(facet, payloads)
|
|
53
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import {
|
|
2
|
+
expect,
|
|
3
|
+
test,
|
|
4
|
+
} from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { Priority } from '../types/priority'
|
|
7
|
+
|
|
8
|
+
import { Facet } from './facet'
|
|
9
|
+
import {
|
|
10
|
+
FacetNode,
|
|
11
|
+
subtractFacetNode,
|
|
12
|
+
unionFacetNode,
|
|
13
|
+
} from './facet-node'
|
|
14
|
+
|
|
15
|
+
const sum = (input: number[]) => input.reduce((acc, cur) => acc + cur, 0)
|
|
16
|
+
|
|
17
|
+
const CounterFacet = new Facet<number, number>(null, false, sum)
|
|
18
|
+
const SingletonCounterFacet = new Facet<number, number>(null, true, sum)
|
|
19
|
+
|
|
20
|
+
const value1 = [0, 1, 2]
|
|
21
|
+
const value2 = [3, 4, 5]
|
|
22
|
+
const value3 = [6, 7, 8]
|
|
23
|
+
|
|
24
|
+
test('Root Facet Node', () => {
|
|
25
|
+
const rootNode = new FacetNode(CounterFacet, [
|
|
26
|
+
[...value1],
|
|
27
|
+
null,
|
|
28
|
+
null,
|
|
29
|
+
[...value1],
|
|
30
|
+
null,
|
|
31
|
+
])
|
|
32
|
+
const childNode1 = new FacetNode(CounterFacet, [
|
|
33
|
+
null,
|
|
34
|
+
[...value2],
|
|
35
|
+
null,
|
|
36
|
+
[...value2],
|
|
37
|
+
null,
|
|
38
|
+
])
|
|
39
|
+
const childNode2 = new FacetNode(CounterFacet, [
|
|
40
|
+
null,
|
|
41
|
+
null,
|
|
42
|
+
[...value3],
|
|
43
|
+
[...value3],
|
|
44
|
+
null,
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
rootNode.children.set(0, childNode1)
|
|
48
|
+
rootNode.children.set(1, childNode2)
|
|
49
|
+
|
|
50
|
+
expect(rootNode.facet).toBe(CounterFacet)
|
|
51
|
+
expect(rootNode.children.size).toBe(2)
|
|
52
|
+
expect(rootNode.getOutput()).toEqual([
|
|
53
|
+
sum(value1),
|
|
54
|
+
sum(value2),
|
|
55
|
+
sum(value3),
|
|
56
|
+
sum([...value1, ...value2, ...value3]),
|
|
57
|
+
null,
|
|
58
|
+
])
|
|
59
|
+
expect(() => rootNode.getRootOutput()).toThrow()
|
|
60
|
+
expect(rootNode.isRoot()).toBeTruthy()
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('Singleton Root Facet Node', () => {
|
|
64
|
+
const singletonRootNode = new FacetNode(SingletonCounterFacet, [
|
|
65
|
+
[...value1],
|
|
66
|
+
null,
|
|
67
|
+
null,
|
|
68
|
+
null,
|
|
69
|
+
null,
|
|
70
|
+
])
|
|
71
|
+
const childNode1 = new FacetNode(CounterFacet, [
|
|
72
|
+
null,
|
|
73
|
+
[...value2],
|
|
74
|
+
null,
|
|
75
|
+
[...value2],
|
|
76
|
+
null,
|
|
77
|
+
])
|
|
78
|
+
const childNode2 = new FacetNode(CounterFacet, [
|
|
79
|
+
null,
|
|
80
|
+
null,
|
|
81
|
+
[...value3],
|
|
82
|
+
[...value3],
|
|
83
|
+
null,
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
singletonRootNode.children.set(0, childNode1)
|
|
87
|
+
singletonRootNode.children.set(1, childNode2)
|
|
88
|
+
|
|
89
|
+
expect(singletonRootNode.facet).toBe(SingletonCounterFacet)
|
|
90
|
+
expect(singletonRootNode.children.size).toBe(2)
|
|
91
|
+
expect(childNode1.getOutput()).toEqual([
|
|
92
|
+
null,
|
|
93
|
+
sum(value2),
|
|
94
|
+
null,
|
|
95
|
+
sum(value2),
|
|
96
|
+
null,
|
|
97
|
+
])
|
|
98
|
+
expect(childNode2.getOutput()).toEqual([
|
|
99
|
+
null,
|
|
100
|
+
null,
|
|
101
|
+
sum(value3),
|
|
102
|
+
sum(value3),
|
|
103
|
+
null,
|
|
104
|
+
])
|
|
105
|
+
expect(singletonRootNode.getOutput()).toEqual([
|
|
106
|
+
null,
|
|
107
|
+
null,
|
|
108
|
+
sum([...value1, sum(value2), sum(value3), ...[sum(value2), sum(value3)]]),
|
|
109
|
+
null,
|
|
110
|
+
null,
|
|
111
|
+
])
|
|
112
|
+
expect(singletonRootNode.getRootOutput()).toEqual(
|
|
113
|
+
singletonRootNode.getOutput()[Priority.default],
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('Union Facet Node', () => {
|
|
118
|
+
const rootNode1 = new FacetNode(CounterFacet, [
|
|
119
|
+
[...value1],
|
|
120
|
+
null,
|
|
121
|
+
null,
|
|
122
|
+
[...value1, ...value3],
|
|
123
|
+
null,
|
|
124
|
+
])
|
|
125
|
+
const rootNode2 = new FacetNode(CounterFacet, [
|
|
126
|
+
null,
|
|
127
|
+
[...value1],
|
|
128
|
+
null,
|
|
129
|
+
[...value1, ...value2],
|
|
130
|
+
null,
|
|
131
|
+
])
|
|
132
|
+
const childNode1 = new FacetNode(CounterFacet, [
|
|
133
|
+
null,
|
|
134
|
+
null,
|
|
135
|
+
[...value2],
|
|
136
|
+
[...value2],
|
|
137
|
+
null,
|
|
138
|
+
])
|
|
139
|
+
const childNode2 = new FacetNode(CounterFacet, [
|
|
140
|
+
null,
|
|
141
|
+
null,
|
|
142
|
+
[...value3],
|
|
143
|
+
[...value3],
|
|
144
|
+
null,
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
rootNode1.children.set(0, childNode1)
|
|
148
|
+
rootNode2.children.set(0, childNode2)
|
|
149
|
+
|
|
150
|
+
const unioned = unionFacetNode(rootNode1, rootNode2)
|
|
151
|
+
expect(unioned.facet).toBe(CounterFacet)
|
|
152
|
+
expect(unioned.children.size).toBe(1)
|
|
153
|
+
expect(unioned.children.get(0)).toEqual(
|
|
154
|
+
unionFacetNode(childNode1, childNode2),
|
|
155
|
+
)
|
|
156
|
+
expect(unioned.inputs).toEqual([
|
|
157
|
+
[...value1],
|
|
158
|
+
[...value1],
|
|
159
|
+
null,
|
|
160
|
+
[...value1, ...value3, ...value2],
|
|
161
|
+
null,
|
|
162
|
+
])
|
|
163
|
+
expect(unioned.children.get(0)?.inputs).toEqual([
|
|
164
|
+
null,
|
|
165
|
+
null,
|
|
166
|
+
[...value2, ...value3],
|
|
167
|
+
[...value2, ...value3],
|
|
168
|
+
null,
|
|
169
|
+
])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('Subtract Facet Node', () => {
|
|
173
|
+
const rootNode1 = new FacetNode(CounterFacet, [
|
|
174
|
+
[...value1],
|
|
175
|
+
null,
|
|
176
|
+
null,
|
|
177
|
+
[...value1, ...value3],
|
|
178
|
+
null,
|
|
179
|
+
])
|
|
180
|
+
const rootNode2 = new FacetNode(CounterFacet, [
|
|
181
|
+
null,
|
|
182
|
+
[...value1],
|
|
183
|
+
null,
|
|
184
|
+
[...value1, ...value2],
|
|
185
|
+
null,
|
|
186
|
+
])
|
|
187
|
+
const childNode1 = new FacetNode(CounterFacet, [
|
|
188
|
+
null,
|
|
189
|
+
null,
|
|
190
|
+
[...value2],
|
|
191
|
+
[...value2],
|
|
192
|
+
null,
|
|
193
|
+
])
|
|
194
|
+
const childNode2 = new FacetNode(CounterFacet, [
|
|
195
|
+
null,
|
|
196
|
+
null,
|
|
197
|
+
[...value2],
|
|
198
|
+
[...value3],
|
|
199
|
+
null,
|
|
200
|
+
])
|
|
201
|
+
|
|
202
|
+
rootNode1.children.set(0, childNode1)
|
|
203
|
+
rootNode2.children.set(0, childNode2)
|
|
204
|
+
|
|
205
|
+
const subtracted = subtractFacetNode(rootNode1, rootNode2)
|
|
206
|
+
expect(subtracted.facet).toBe(CounterFacet)
|
|
207
|
+
expect(subtracted.children.size).toBe(1)
|
|
208
|
+
expect(subtracted.children.get(0)).toEqual(
|
|
209
|
+
subtractFacetNode(childNode1, childNode2),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
expect(subtracted.inputs).toEqual([
|
|
213
|
+
[...value1],
|
|
214
|
+
null,
|
|
215
|
+
null,
|
|
216
|
+
[...value3],
|
|
217
|
+
null,
|
|
218
|
+
])
|
|
219
|
+
|
|
220
|
+
expect(subtracted.children.get(0)?.inputs).toEqual([
|
|
221
|
+
null,
|
|
222
|
+
null,
|
|
223
|
+
[],
|
|
224
|
+
[...value2],
|
|
225
|
+
null,
|
|
226
|
+
])
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
test('Union Facet Node with Different Facet', () => {
|
|
230
|
+
const rootNode1 = new FacetNode(CounterFacet, [
|
|
231
|
+
[...value1],
|
|
232
|
+
null,
|
|
233
|
+
null,
|
|
234
|
+
[...value1, ...value3],
|
|
235
|
+
null,
|
|
236
|
+
])
|
|
237
|
+
const rootNode2 = new FacetNode(SingletonCounterFacet, [
|
|
238
|
+
null,
|
|
239
|
+
[...value1],
|
|
240
|
+
null,
|
|
241
|
+
[...value1, ...value2],
|
|
242
|
+
null,
|
|
243
|
+
])
|
|
244
|
+
|
|
245
|
+
expect(() => unionFacetNode(rootNode1, rootNode2)).toThrow()
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('Subtract Facet Node with Different Facet', () => {
|
|
249
|
+
const rootNode1 = new FacetNode(CounterFacet, [
|
|
250
|
+
[...value1],
|
|
251
|
+
null,
|
|
252
|
+
null,
|
|
253
|
+
[...value1, ...value3],
|
|
254
|
+
null,
|
|
255
|
+
])
|
|
256
|
+
const rootNode2 = new FacetNode(SingletonCounterFacet, [
|
|
257
|
+
null,
|
|
258
|
+
[...value1],
|
|
259
|
+
null,
|
|
260
|
+
[...value1, ...value2],
|
|
261
|
+
null,
|
|
262
|
+
])
|
|
263
|
+
|
|
264
|
+
expect(() => subtractFacetNode(rootNode1, rootNode2)).toThrow()
|
|
265
|
+
})
|