@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.
Files changed (168) hide show
  1. package/dist/editor-CfkZ4TNU.d.ts +748 -0
  2. package/dist/editor-CfkZ4TNU.d.ts.map +1 -0
  3. package/dist/{editor-DbMrpnmL.js → editor-CizSwUN8.js} +102 -192
  4. package/dist/editor-CizSwUN8.js.map +1 -0
  5. package/dist/prosekit-core-test.d.ts +20 -19
  6. package/dist/prosekit-core-test.d.ts.map +1 -0
  7. package/dist/prosekit-core-test.js +4 -5
  8. package/dist/prosekit-core-test.js.map +1 -0
  9. package/dist/prosekit-core.d.ts +782 -757
  10. package/dist/prosekit-core.d.ts.map +1 -0
  11. package/dist/prosekit-core.js +30 -45
  12. package/dist/prosekit-core.js.map +1 -0
  13. package/package.json +14 -11
  14. package/src/commands/add-mark.ts +53 -0
  15. package/src/commands/expand-mark.ts +96 -0
  16. package/src/commands/insert-default-block.spec.ts +102 -0
  17. package/src/commands/insert-default-block.ts +49 -0
  18. package/src/commands/insert-node.ts +71 -0
  19. package/src/commands/insert-text.ts +24 -0
  20. package/src/commands/remove-mark.ts +54 -0
  21. package/src/commands/remove-node.ts +43 -0
  22. package/src/commands/select-all.ts +16 -0
  23. package/src/commands/set-block-type.ts +64 -0
  24. package/src/commands/set-node-attrs.ts +68 -0
  25. package/src/commands/toggle-mark.ts +65 -0
  26. package/src/commands/toggle-node.ts +47 -0
  27. package/src/commands/toggle-wrap.spec.ts +35 -0
  28. package/src/commands/toggle-wrap.ts +42 -0
  29. package/src/commands/unset-block-type.spec.ts +49 -0
  30. package/src/commands/unset-block-type.ts +84 -0
  31. package/src/commands/unset-mark.spec.ts +35 -0
  32. package/src/commands/unset-mark.ts +38 -0
  33. package/src/commands/wrap.ts +50 -0
  34. package/src/editor/action.spec.ts +143 -0
  35. package/src/editor/action.ts +248 -0
  36. package/src/editor/editor.spec.ts +186 -0
  37. package/src/editor/editor.ts +563 -0
  38. package/src/editor/union.spec.ts +108 -0
  39. package/src/editor/union.ts +47 -0
  40. package/src/editor/with-priority.ts +25 -0
  41. package/src/error.ts +28 -0
  42. package/src/extensions/clipboard-serializer.ts +107 -0
  43. package/src/extensions/command.ts +121 -0
  44. package/src/extensions/default-state.spec.ts +60 -0
  45. package/src/extensions/default-state.ts +76 -0
  46. package/src/extensions/doc.ts +31 -0
  47. package/src/extensions/events/doc-change.ts +34 -0
  48. package/src/extensions/events/dom-event.spec.ts +70 -0
  49. package/src/extensions/events/dom-event.ts +117 -0
  50. package/src/extensions/events/editor-event.ts +293 -0
  51. package/src/extensions/events/focus.spec.ts +50 -0
  52. package/src/extensions/events/focus.ts +28 -0
  53. package/src/extensions/events/plugin-view.ts +132 -0
  54. package/src/extensions/history.ts +81 -0
  55. package/src/extensions/keymap-base.ts +60 -0
  56. package/src/extensions/keymap.spec.ts +89 -0
  57. package/src/extensions/keymap.ts +96 -0
  58. package/src/extensions/mark-spec.spec.ts +177 -0
  59. package/src/extensions/mark-spec.ts +181 -0
  60. package/src/extensions/mark-view-effect.ts +85 -0
  61. package/src/extensions/mark-view.ts +43 -0
  62. package/src/extensions/node-spec.spec.ts +224 -0
  63. package/src/extensions/node-spec.ts +199 -0
  64. package/src/extensions/node-view-effect.ts +85 -0
  65. package/src/extensions/node-view.ts +43 -0
  66. package/src/extensions/paragraph.ts +61 -0
  67. package/src/extensions/plugin.ts +91 -0
  68. package/src/extensions/text.ts +34 -0
  69. package/src/facets/base-extension.ts +54 -0
  70. package/src/facets/command.ts +21 -0
  71. package/src/facets/facet-extension.spec.ts +173 -0
  72. package/src/facets/facet-extension.ts +53 -0
  73. package/src/facets/facet-node.spec.ts +265 -0
  74. package/src/facets/facet-node.ts +185 -0
  75. package/src/facets/facet-types.ts +9 -0
  76. package/src/facets/facet.spec.ts +76 -0
  77. package/src/facets/facet.ts +84 -0
  78. package/src/facets/root.ts +44 -0
  79. package/src/facets/schema-spec.ts +30 -0
  80. package/src/facets/schema.ts +26 -0
  81. package/src/facets/state.ts +57 -0
  82. package/src/facets/union-extension.ts +41 -0
  83. package/src/index.ts +302 -0
  84. package/src/test/index.ts +4 -0
  85. package/src/test/test-builder.ts +68 -0
  86. package/src/test/test-editor.spec.ts +104 -0
  87. package/src/test/test-editor.ts +113 -0
  88. package/src/testing/index.ts +283 -0
  89. package/src/testing/keyboard.ts +5 -0
  90. package/src/types/any-function.ts +4 -0
  91. package/src/types/assert-type-equal.ts +8 -0
  92. package/src/types/attrs.ts +32 -0
  93. package/src/types/base-node-view-options.ts +33 -0
  94. package/src/types/dom-node.ts +1 -0
  95. package/src/types/extension-command.ts +52 -0
  96. package/src/types/extension-mark.ts +15 -0
  97. package/src/types/extension-node.ts +15 -0
  98. package/src/types/extension.spec.ts +56 -0
  99. package/src/types/extension.ts +168 -0
  100. package/src/types/model.ts +54 -0
  101. package/src/types/object-entries.ts +13 -0
  102. package/src/types/pick-string-literal.spec.ts +10 -0
  103. package/src/types/pick-string-literal.ts +6 -0
  104. package/src/types/pick-sub-type.spec.ts +20 -0
  105. package/src/types/pick-sub-type.ts +6 -0
  106. package/src/types/priority.ts +12 -0
  107. package/src/types/setter.ts +4 -0
  108. package/src/types/simplify-deeper.spec.ts +40 -0
  109. package/src/types/simplify-deeper.ts +6 -0
  110. package/src/types/simplify-union.spec.ts +21 -0
  111. package/src/types/simplify-union.ts +11 -0
  112. package/src/utils/array-grouping.spec.ts +29 -0
  113. package/src/utils/array-grouping.ts +25 -0
  114. package/src/utils/array.ts +21 -0
  115. package/src/utils/assert.ts +13 -0
  116. package/src/utils/attrs-match.ts +20 -0
  117. package/src/utils/can-use-regex-lookbehind.ts +12 -0
  118. package/src/utils/clsx.spec.ts +14 -0
  119. package/src/utils/clsx.ts +12 -0
  120. package/src/utils/collect-children.ts +21 -0
  121. package/src/utils/collect-nodes.ts +37 -0
  122. package/src/utils/combine-event-handlers.spec.ts +27 -0
  123. package/src/utils/combine-event-handlers.ts +27 -0
  124. package/src/utils/contains-inline-node.ts +17 -0
  125. package/src/utils/deep-equals.spec.ts +26 -0
  126. package/src/utils/deep-equals.ts +29 -0
  127. package/src/utils/default-block-at.ts +15 -0
  128. package/src/utils/editor-content.spec.ts +47 -0
  129. package/src/utils/editor-content.ts +77 -0
  130. package/src/utils/env.ts +6 -0
  131. package/src/utils/find-parent-node-of-type.ts +29 -0
  132. package/src/utils/find-parent-node.spec.ts +68 -0
  133. package/src/utils/find-parent-node.ts +55 -0
  134. package/src/utils/get-custom-selection.ts +19 -0
  135. package/src/utils/get-dom-api.ts +56 -0
  136. package/src/utils/get-id.spec.ts +14 -0
  137. package/src/utils/get-id.ts +13 -0
  138. package/src/utils/get-mark-type.ts +20 -0
  139. package/src/utils/get-node-type.ts +20 -0
  140. package/src/utils/get-node-types.ts +19 -0
  141. package/src/utils/includes-mark.ts +18 -0
  142. package/src/utils/is-at-block-start.ts +26 -0
  143. package/src/utils/is-in-code-block.ts +18 -0
  144. package/src/utils/is-mark-absent.spec.ts +53 -0
  145. package/src/utils/is-mark-absent.ts +42 -0
  146. package/src/utils/is-mark-active.ts +27 -0
  147. package/src/utils/is-node-active.ts +25 -0
  148. package/src/utils/is-subset.spec.ts +12 -0
  149. package/src/utils/is-subset.ts +11 -0
  150. package/src/utils/maybe-run.spec.ts +39 -0
  151. package/src/utils/maybe-run.ts +11 -0
  152. package/src/utils/merge-objects.spec.ts +30 -0
  153. package/src/utils/merge-objects.ts +11 -0
  154. package/src/utils/merge-specs.ts +35 -0
  155. package/src/utils/object-equal.spec.ts +26 -0
  156. package/src/utils/object-equal.ts +28 -0
  157. package/src/utils/output-spec.test.ts +95 -0
  158. package/src/utils/output-spec.ts +130 -0
  159. package/src/utils/parse.spec.ts +46 -0
  160. package/src/utils/parse.ts +321 -0
  161. package/src/utils/remove-undefined-values.spec.ts +15 -0
  162. package/src/utils/remove-undefined-values.ts +9 -0
  163. package/src/utils/set-selection-around.ts +11 -0
  164. package/src/utils/type-assertion.ts +91 -0
  165. package/src/utils/unicode.spec.ts +10 -0
  166. package/src/utils/unicode.ts +4 -0
  167. package/src/utils/with-skip-code-block.ts +15 -0
  168. 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
+ })