@witchcraft/editor 0.0.8 → 0.1.0

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 (100) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -2
  3. package/dist/runtime/components/CodeBlockThemePicker.d.vue.ts +4 -2
  4. package/dist/runtime/components/CodeBlockThemePicker.vue +0 -1
  5. package/dist/runtime/components/CodeBlockThemePicker.vue.d.ts +4 -2
  6. package/dist/runtime/components/Commands.d.vue.ts +2 -1
  7. package/dist/runtime/components/Commands.vue.d.ts +2 -1
  8. package/dist/runtime/components/Editor.d.vue.ts +3 -12
  9. package/dist/runtime/components/Editor.vue.d.ts +3 -12
  10. package/dist/runtime/components/EditorDemoApp.d.vue.ts +2 -1
  11. package/dist/runtime/components/EditorDemoApp.vue.d.ts +2 -1
  12. package/dist/runtime/components/EditorDemoControls.d.vue.ts +4 -2
  13. package/dist/runtime/components/EditorDemoControls.vue.d.ts +4 -2
  14. package/dist/runtime/components/TestWrapper.d.vue.ts +2 -1
  15. package/dist/runtime/components/TestWrapper.vue.d.ts +2 -1
  16. package/dist/runtime/demo/App.d.vue.ts +2 -1
  17. package/dist/runtime/demo/App.vue.d.ts +2 -1
  18. package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.d.vue.ts +2 -1
  19. package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.vue.d.ts +2 -1
  20. package/dist/runtime/pm/features/Blocks/components/ItemMenu.d.vue.ts +2 -1
  21. package/dist/runtime/pm/features/Blocks/components/ItemMenu.vue.d.ts +2 -1
  22. package/dist/runtime/pm/features/Blocks/components/ItemNodeView.d.vue.ts +2 -1
  23. package/dist/runtime/pm/features/Blocks/components/ItemNodeView.vue.d.ts +2 -1
  24. package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.d.vue.ts +2 -1
  25. package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.vue.d.ts +2 -1
  26. package/dist/runtime/pm/features/CommandsMenus/components/CommandBar.d.vue.ts +2 -1
  27. package/dist/runtime/pm/features/CommandsMenus/components/CommandBar.vue.d.ts +2 -1
  28. package/dist/runtime/pm/features/CommandsMenus/components/CommandBarItem.d.vue.ts +2 -1
  29. package/dist/runtime/pm/features/CommandsMenus/components/CommandBarItem.vue.d.ts +2 -1
  30. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.d.vue.ts +2 -1
  31. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.vue.d.ts +2 -1
  32. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuItem.d.vue.ts +2 -1
  33. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuItem.vue.d.ts +2 -1
  34. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuList.d.vue.ts +2 -1
  35. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuList.vue.d.ts +2 -1
  36. package/dist/runtime/pm/features/CommandsMenus/components/TextIcon.d.vue.ts +2 -1
  37. package/dist/runtime/pm/features/CommandsMenus/components/TextIcon.vue.d.ts +2 -1
  38. package/dist/runtime/pm/features/CommandsMenus/icons/HighlightIcon.d.vue.ts +2 -1
  39. package/dist/runtime/pm/features/CommandsMenus/icons/HighlightIcon.vue.d.ts +2 -1
  40. package/dist/runtime/pm/features/CommandsMenus/icons/SubscriptIcon.d.vue.ts +2 -1
  41. package/dist/runtime/pm/features/CommandsMenus/icons/SubscriptIcon.vue.d.ts +2 -1
  42. package/dist/runtime/pm/features/CommandsMenus/icons/SuperscriptIcon.d.vue.ts +2 -1
  43. package/dist/runtime/pm/features/CommandsMenus/icons/SuperscriptIcon.vue.d.ts +2 -1
  44. package/dist/runtime/pm/features/DocumentApi/DocumentApi.d.ts +3 -0
  45. package/dist/runtime/pm/features/DocumentApi/DocumentApi.js +2 -0
  46. package/dist/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.js +4 -1
  47. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.d.vue.ts +2 -1
  48. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +3 -3
  49. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue.d.ts +2 -1
  50. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.d.vue.ts +2 -1
  51. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.vue.d.ts +2 -1
  52. package/dist/runtime/pm/features/FileLoader/components/FileLoaderNodeView.d.vue.ts +2 -1
  53. package/dist/runtime/pm/features/FileLoader/components/FileLoaderNodeView.vue.d.ts +2 -1
  54. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.d.vue.ts +2 -1
  55. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.vue +0 -1
  56. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.vue.d.ts +2 -1
  57. package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.d.vue.ts +4 -2
  58. package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.vue.d.ts +4 -2
  59. package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.d.vue.ts +4 -2
  60. package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.vue.d.ts +4 -2
  61. package/dist/runtime/pm/features/Link/components/BubbleMenuLink.d.vue.ts +2 -1
  62. package/dist/runtime/pm/features/Link/components/BubbleMenuLink.vue.d.ts +2 -1
  63. package/dist/runtime/pm/features/Link/components/BubbleMenuLinkActions.d.vue.ts +2 -1
  64. package/dist/runtime/pm/features/Link/components/BubbleMenuLinkActions.vue.d.ts +2 -1
  65. package/dist/runtime/pm/features/Menus/components/MarkMenuManager.d.vue.ts +2 -1
  66. package/dist/runtime/pm/features/Menus/components/MarkMenuManager.vue.d.ts +2 -1
  67. package/dist/runtime/pm/generator.d.ts +30 -44
  68. package/dist/runtime/pm/generator.js +112 -109
  69. package/dist/runtime/pm/utils/generateRandomDoc.d.ts +3 -21
  70. package/dist/runtime/pm/utils/generateRandomDoc.js +36 -77
  71. package/dist/runtime/pm/utils/generator/createGeneratorConfig.d.ts +33 -0
  72. package/dist/runtime/pm/utils/generator/createGeneratorConfig.js +3 -0
  73. package/dist/runtime/pm/utils/generator/createPsuedoSentence.d.ts +4 -0
  74. package/dist/runtime/pm/utils/generator/createPsuedoSentence.js +16 -0
  75. package/dist/runtime/pm/utils/generator/createRandomChildCountGenerator.d.ts +14 -0
  76. package/dist/runtime/pm/utils/generator/createRandomChildCountGenerator.js +16 -0
  77. package/dist/runtime/pm/utils/generator/getTextOrMarkLength.d.ts +2 -0
  78. package/dist/runtime/pm/utils/generator/getTextOrMarkLength.js +7 -0
  79. package/dist/runtime/pm/utils/generator/influenceWithDepth.d.ts +6 -0
  80. package/dist/runtime/pm/utils/generator/influenceWithDepth.js +6 -0
  81. package/dist/runtime/pm/utils/generator/sometimesZero.d.ts +13 -0
  82. package/dist/runtime/pm/utils/generator/sometimesZero.js +7 -0
  83. package/package.json +59 -58
  84. package/src/runtime/components/CodeBlockThemePicker.vue +0 -1
  85. package/src/runtime/pm/features/DocumentApi/DocumentApi.ts +3 -0
  86. package/src/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.ts +2 -1
  87. package/src/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +3 -4
  88. package/src/runtime/pm/features/Iframe/components/IframeNodeView.vue +0 -1
  89. package/src/runtime/pm/generator.ts +104 -151
  90. package/src/runtime/pm/schema.ts +2 -2
  91. package/src/runtime/pm/utils/generateRandomDoc.ts +47 -124
  92. package/src/runtime/pm/utils/generator/createGeneratorConfig.ts +56 -0
  93. package/src/runtime/pm/utils/generator/createPsuedoSentence.ts +18 -0
  94. package/src/runtime/pm/utils/generator/createRandomChildCountGenerator.ts +31 -0
  95. package/src/runtime/pm/utils/generator/getTextOrMarkLength.ts +9 -0
  96. package/src/runtime/pm/utils/generator/influenceWithDepth.ts +11 -0
  97. package/src/runtime/pm/utils/generator/sometimesZero.ts +16 -0
  98. package/dist/runtime/pm/utils/generateRandomTree.d.ts +0 -50
  99. package/dist/runtime/pm/utils/generateRandomTree.js +0 -38
  100. package/src/runtime/pm/utils/generateRandomTree.ts +0 -100
@@ -1,140 +1,63 @@
1
- import { unreachable } from "@alanscodelog/utils/unreachable"
2
1
  import { faker } from "@faker-js/faker"
3
- import type { Mark, MarkType, Node } from "@tiptap/pm/model"
2
+ import type { Node } from "@tiptap/pm/model"
4
3
  import type { builders } from "prosemirror-test-builder"
5
4
 
6
- import { generateRandomTree } from "./generateRandomTree.js"
5
+ import type { GeneratorConfigEntry } from "./generator/createGeneratorConfig.js"
7
6
 
8
- import type { GeneratorConfigEntry } from "../generator.js"
9
7
  import { generatorConfig } from "../generator.js"
10
8
 
11
- /**
12
- * Generates a random doc using faker js.
13
- *
14
- * A config describing how to create the doc is required. One is available under `pm/generator`.
15
- *
16
- * @experimental
17
- */
18
9
  export function generateRandomDoc<
19
- TBuilder extends ReturnType<typeof builders>,
20
- TData extends string
10
+ TBuilder extends ReturnType<typeof builders>
21
11
  >(
22
- builder: TBuilder,
23
- config: GeneratorConfigEntry<any, any, any, any>[] = generatorConfig,
24
- treeOptions?: Parameters<typeof generateRandomTree>[1],
25
- {
26
- checkAfterNodeCreation = false
27
- }: {
28
- /**
29
- * Checks if every returned node is valid for debugging invalid generator configs.
30
- *
31
- * A check is always done on the root node at the end regardless of this option.
32
- *
33
- * It is very slow (each check, rechecks children).
34
- *
35
- * @default false
36
- */
37
- checkAfterNodeCreation?: boolean
38
- } = {}
39
- ): Node {
40
- const schema = builder.schema
41
-
42
- const map: Record<string, GeneratorConfigEntry<any, any, any, any>> = {}
43
- function schemaFilter(name: string) {
44
- return name !== "text" && name !== schema.topNodeType.name
45
- }
46
-
47
- for (const entry of config) {
48
- for (const node of entry.parents.names) {
49
- if (node === "") throw new Error(`Empty node name ""`)
50
- const type = entry.parents.type === "node" ? schema.nodes[node] : schema.marks[node]
51
- if (type === undefined && node !== "text") {
52
- throw new Error(`${node} (on entry of type ${entry.parents.type}) not found in schema. Valid names are ${Object.keys(entry.parents.type === "node" ? schema.nodes : schema.marks)}`)
53
- }
54
-
55
- for (const node of entry.parents.names) {
56
- const subEntry = { ...entry }
57
- if (entry.ignoreValidation === undefined || entry.ignoreValidation !== true) {
58
- const childrenToValidate = (entry.children.names ?? []).filter(_ => !Array.isArray(entry.ignoreValidation) || !entry.ignoreValidation.includes(_))
59
- for (const child of childrenToValidate) {
60
- if (child === "") throw new Error(`Empty node name ""`)
61
- if (entry.children.type === "node") {
62
- const childType = schema.nodes[child]
63
- const possibleNames = [childType.name, ...((childType.spec.group ?? "").split(" ").filter(e => e !== ""))]
64
- const isAllowedChild = type.spec.content && possibleNames.some(n => type.spec.content!.includes(n))
65
- // we don't use contentMatch.matchType because
66
- // it can give false negatives when the content expression
67
- // has multiple types like type1+ type2+,
68
- // it will only return true for type1 in those cases
69
- if (!(isAllowedChild && schemaFilter(childType.name))) {
70
- throw new Error(`Node ${node} cannot contain child of type ${child}`)
71
- }
72
- } else {
73
- const childType = schema.marks[child]
74
- if (entry.parents.type === "node") {
75
- // not sure why this is returning false when it shouldn't (e.g. paragraph can't contain bold)
76
- // if (!(type as NodeType).allowsMarkType(childType)) {
77
- // throw new Error(`Node ${node} cannot contain mark child of type ${child}`)
78
- // }
79
- } else {
80
- const possibleNames = [childType.name, ...((childType.spec.group ?? "").split(" ").filter(e => e !== ""))]
81
- const t = type as MarkType
82
- const isExcluded = t.spec.excludes !== undefined
83
- && possibleNames.some(n => {
84
- // _ this means exclude everything in prosemirror
85
- return t.spec.excludes!.includes(n)
86
- || t.spec.excludes!.includes("_")
87
- })
88
- if (isExcluded) {
89
- throw new Error(`Mark ${node} cannot contain mark child of type ${child}`)
90
- }
91
- }
92
- }
93
- }
94
- }
95
- map[node] = { ...subEntry }
12
+ pm: TBuilder,
13
+ config: GeneratorConfigEntry<any, any, any, any>[] = generatorConfig
14
+ ) {
15
+ function generateNode(
16
+ pm: TBuilder,
17
+ nodeConfig: GeneratorConfigEntry<TBuilder, any, any, any, any>,
18
+ parentType: string,
19
+ depth = 0
20
+ ): Node {
21
+ if (!nodeConfig.parents.names.includes(parentType)) {
22
+ throw new Error(`Node config ${nodeConfig.parents.names.join(", ")} does not include parent type ${parentType}`)
23
+ }
24
+ const nodes = []
25
+ if ("count" in nodeConfig) {
26
+ const childrenCount = nodeConfig.count(depth)
27
+ for (let i = 0; i < childrenCount; i++) {
28
+ const type = pickWeightedChild(nodeConfig.children.names)
29
+ if (type === undefined) continue
30
+ if (nodeConfig?.skipChild?.(parentType, type, depth)) continue
31
+ const childConfig = config.find(c => {
32
+ return c.parents.names.includes(type)
33
+ })
34
+ if (!childConfig) continue
35
+ const child = generateNode(pm, childConfig, type, depth + 1)
36
+ if (child === undefined) continue
37
+ nodes.push(child)
96
38
  }
97
39
  }
40
+ return nodeConfig.create(pm, parentType, nodes)
98
41
  }
42
+ const firstEntry = config[0]
43
+ if (firstEntry === undefined) throw new Error("No config entries found.")
44
+ if (!("children" in firstEntry)) throw new Error("No children found in the first config entry. The first entry must have children.")
45
+ const startingType = pickWeightedChild(firstEntry.parents.names.map(name => [name, 1]))
46
+ if (startingType === undefined) throw new Error("No starting type found.")
47
+ return generateNode(pm, config[0], startingType)
48
+ }
99
49
 
100
50
 
101
- const children = generateRandomTree<Node | Mark | string, TData>({
102
- parentData: (data?: TData): TData => {
103
- if (data === "") return "" as TData
104
- if (!data) unreachable()
105
- if (!map[data] || !map[data].children.names || map[data].children.names.length === 0) {
106
- // we must prematurely end the branch as the node can contain no children
107
- return "" as TData
108
- } else {
109
- const picked = faker.helpers.arrayElement(map[data].children.names)
110
- return picked as TData
111
- }
112
- },
113
- createNode: (children, _isLeaf, data): Node | Mark | string | undefined => {
114
- if (data === "") return undefined
115
- if (!data) unreachable()
116
- const type = schema.nodes[data]
117
- // type allows no children or isn't configured to generate children
118
- if (!type) return undefined
119
-
120
- const parent = map[data]?.create?.(data, children as any)
121
- ?? builder[data]({}, ...children as any)
122
-
123
- if (checkAfterNodeCreation) {
124
- ;(parent as any)?.check?.()
125
- }
126
-
127
- return parent
128
- }
129
- }, {
130
- ...treeOptions,
131
- initialData: schema.topNodeType.name as TData
132
- })
133
-
134
- const doc = map[schema.topNodeType.name]?.create?.(schema.topNodeType.name as any, children as any)
135
- ?? builder[schema.topNodeType.name]({}, ...children as any)
51
+ export function pickWeightedChild(options: [string, number][]): string | undefined {
52
+ const totalWeight = options.reduce((sum, [, weight]) => sum + weight, 0)
53
+ if (totalWeight === 0) return undefined
136
54
 
137
- ;(doc as any).check?.()
138
- return doc as Node
55
+ let random = faker.number.float({ min: 0, max: totalWeight })
56
+ for (const [name, weight] of options) {
57
+ if (random < weight) return name
58
+ random -= weight
59
+ }
60
+ // Fallback to first if rounding errors occur
61
+ return options[0][0]
139
62
  }
140
63
 
@@ -0,0 +1,56 @@
1
+ import type { Mark, Node } from "@tiptap/pm/model"
2
+ import type { builders } from "prosemirror-test-builder"
3
+
4
+ export function createGeneratorConfig<
5
+ TBuilder extends ReturnType<typeof builders>,
6
+ TChildType extends "node" | "text",
7
+ TParentType extends "node" | "text",
8
+ TChildNodeType extends TChildType extends "node" ? Node : (Mark | string),
9
+ TParentNodeType extends TParentType extends "node" ? Node : (Mark | string)
10
+ >(
11
+ config: GeneratorConfigEntry<TBuilder, TChildType, TParentType, TChildNodeType, TParentNodeType>
12
+ ): GeneratorConfigEntry<TBuilder, TChildType, TParentType, TChildNodeType, TParentNodeType> {
13
+ return config
14
+ }
15
+
16
+ export type GeneratorConfigEntry<
17
+ TBuilder extends ReturnType<typeof builders>,
18
+ TChildType extends "node" | "text" = "node" | "text",
19
+ TParentType extends "node" | "text" = "node" | "text",
20
+ TChildNodeType extends TChildType extends "node" ? Node : (Mark | string) = TChildType extends "node" ? Node : (Mark | string),
21
+ TParentNodeType extends TParentType extends "node" ? Node : (Mark | string) = TParentType extends "node" ? Node : (Mark | string)
22
+ > = {
23
+ /**
24
+ * Creates the node. Can return undefined to "terminate" the branch being created, the node will be filtered out of the nodes passed to it's parent.
25
+ *
26
+ * Note also, it is not required to use the children. You can ignore them or use a subset or create different ones if needed (some node types *require* text and if count returns 0 children will be empty, which can cause issues).
27
+ */
28
+ create: (pm: TBuilder, parentType: string, children: TChildNodeType[]) => TParentNodeType | undefined
29
+ parents: {
30
+ /**
31
+ * The type of the parent (node or "text") where "text" is a string or mark.
32
+ * Determines the call signature of the create function.
33
+ */
34
+ type: TParentType
35
+ /** Possible parent nodes that can have the given children. The children need not be direct descendants or real prosemirror nodes since `create` is what determines how they're created. */
36
+ names: string[]
37
+ }
38
+ } & (
39
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
40
+ {} | {
41
+ skipChild?: (parentType: string, childType: string, depth: number) => boolean
42
+ children: {
43
+ /** The type of children (node or "text") where "text" is a string or mark. */
44
+ type: TChildType
45
+ /* Children types and their relative weights. */
46
+ names: [name: string, weight: number][]
47
+ }
48
+ /**
49
+ * How many children to generate, is passed the depth so you can make the tree "slimmer" as it does deeper.
50
+ *
51
+ * See the `sometimesZero`, `influenceWithDepth` and `createRandomChildCountGenerator` helpers to make this easier to control.
52
+ */
53
+ count: (depth: number) => number
54
+ }
55
+ )
56
+
@@ -0,0 +1,18 @@
1
+ import { faker } from "@faker-js/faker"
2
+
3
+ export function createPsuedoSentence({ min = 0, max = 10}: { min?: number, max?: number } = {}) {
4
+ // sentence generated with string.sample (which contains all possible chars) instead of lorem.sentence
5
+ const sentenceLength = faker.number.int({ min, max })
6
+ const sentence = Array.from(
7
+ { length: sentenceLength },
8
+ () => {
9
+ const doLorem = faker.number.float() < 0.5
10
+ if (doLorem) {
11
+ return faker.lorem.word()
12
+ } else {
13
+ return faker.string.sample({ min: 1, max: 10 })
14
+ }
15
+ }
16
+ ).join(" ")
17
+ return sentence
18
+ }
@@ -0,0 +1,31 @@
1
+ import { faker } from "@faker-js/faker"
2
+
3
+ import { influenceWithDepth } from "./influenceWithDepth.js"
4
+ import { sometimesZero } from "./sometimesZero.js"
5
+
6
+ /**
7
+ * Combines `sometimesZero` and `influenceWithDepth` to create a function that returns a random number to pass to `count`.
8
+ *
9
+ * ```ts
10
+ * count: createRandomChildCountGenerator({ max: 10, depthInfluence: 0.5 })
11
+ * // or
12
+ * count: (depth: number) => createRandomChildCountGenerator({ max: 10, depthInfluence: 0.5 })(depth)
13
+ * ```
14
+ */
15
+ export function createRandomChildCountGenerator({
16
+ max = 10,
17
+ zeroChance = 0.05,
18
+ depthInfluence = 1
19
+ }: {
20
+ max: number
21
+ zeroChance?: number
22
+ depthInfluence?: number
23
+ }) {
24
+ return (depth: number) => {
25
+ depth++
26
+ return sometimesZero(
27
+ faker.number.int({ min: 1, max: influenceWithDepth(max, depth, depthInfluence) || 1 }),
28
+ zeroChance
29
+ )
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ import type { Mark } from "@tiptap/pm/model"
2
+
3
+ export function getTextOrMarkLength(textOrMark: string | Mark) {
4
+ if (typeof textOrMark === "string") {
5
+ return textOrMark.length
6
+ }
7
+ const textNodes = (textOrMark as any).flat.map((_: any) => "text" in _ ? _.text.length : getTextOrMarkLength(_ as any)) as number[]
8
+ return textNodes.reduce((a, b) => a + b, 0)
9
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Decreases the given number by dividing it by the depth + 1 (so it it's not zero). Returns a floored *int*.
3
+ *
4
+ * Intended to help decrease the number of nodes as the depth increases. Can be adjusted by the strength parameter, to, for example, decrease the adjustment for nodes that tend to be deeper.
5
+ */
6
+ export function influenceWithDepth(num: number, depth: number, strength: number = 1) {
7
+ if (num === 0) return 0
8
+ depth++ // don't allow it to be 0
9
+ const adjusted = Math.floor(num / (depth * strength))
10
+ return adjusted
11
+ }
@@ -0,0 +1,16 @@
1
+ import { faker } from "@faker-js/faker"
2
+
3
+ /**
4
+ * Returns 0 instead of the given number depending on the chance passed (0.05 by default).
5
+ *
6
+ * This is useful to avoid too many empty nodes without having to increase the max a lot.
7
+ *
8
+ * You can pass chance 0 to always return the given number.
9
+ */
10
+
11
+ export function sometimesZero(num: number, chance: number = 0.05, getRandom = faker.number.float) {
12
+ if (getRandom() < chance) {
13
+ return 0
14
+ }
15
+ return num
16
+ }
@@ -1,50 +0,0 @@
1
- /**
2
- * Generates a random tree structure where the probability of generating children
3
- * decreases linearly with depth, resulting in a tree that is bushy at the top
4
- * and sparse towards the maximum depth.
5
- *
6
- * This function is agnostic to the actual node structure, relying on the caller-provided
7
- * callbacks to create and link nodes.
8
- *
9
- * It uses faker.js for randomness (even on the option parameters).
10
- *
11
- * See {@link generateRandomDoc} for an example of how to use it.
12
- *
13
- * @experimental
14
- */
15
- export declare function generateRandomTree<T, TData>({ createNode, parentData }: {
16
- /** A function that creates a new node (type T) given its depth. */
17
- createNode: (children: T[], isLeaf: boolean, parentData?: TData) => T | undefined;
18
- /**
19
- * A function that is called before creating a a node or it's children whose result is passed to both. This allows creating children that will be compatible with the parent (via this same function, it is passed it's parent).
20
- *
21
- * For example, we could randomly generate a parent type. It's then passed both to the children, to limit the types of children nodes, and to the parent so it can actually create it.
22
- */
23
- parentData?: (parentData?: TData) => TData;
24
- }, { rootNodes: rootNodes, depth, minChildren, maxInitialChildren, initialData }?: {
25
- /**
26
- * The exact number of nodes at depth 0.
27
- *
28
- * @default faker.number.int({ min: 0, max: 5 })
29
- */
30
- rootNodes?: number;
31
- /**
32
- * The maximum depth of the tree (0-indexed). Nodes at this depth will have 0 children.
33
- *
34
- * @default faker.number.int({ min: 0, max: 5 })
35
- */
36
- depth?: number;
37
- /**
38
- * The absolute minimum number of children any node can have.
39
- *
40
- * @default 0
41
- */
42
- minChildren?: number;
43
- /**
44
- * The maximum children a node can have at depth 0. This value scales down as depth increases.
45
- *
46
- * @default 10
47
- */
48
- maxInitialChildren?: number;
49
- initialData?: TData;
50
- }): T[];
@@ -1,38 +0,0 @@
1
- import { faker } from "@faker-js/faker";
2
- export function generateRandomTree({
3
- createNode,
4
- parentData
5
- }, {
6
- rootNodes = faker.number.int({ min: 0, max: 5 }),
7
- depth = faker.number.int({ min: 0, max: 5 }),
8
- minChildren = 0,
9
- maxInitialChildren = 10,
10
- initialData
11
- } = {}) {
12
- const generateChildren = (currentDepth, childCount, pData) => {
13
- const res = [];
14
- for (let i = 0; i < childCount; i++) {
15
- const numChildren = calculateNumChildren(depth, currentDepth, minChildren, maxInitialChildren);
16
- const data = parentData?.(pData) ?? void 0;
17
- const parent = createNode(
18
- generateChildren(currentDepth + 1, numChildren, data),
19
- numChildren === 0,
20
- data
21
- );
22
- if (parent === void 0) continue;
23
- res.push(parent);
24
- }
25
- return res;
26
- };
27
- return generateChildren(0, rootNodes, initialData);
28
- }
29
- function calculateNumChildren(depth, currentDepth, minChildren, maxInitialChildren) {
30
- const decayFactor = depth > 0 ? (depth - currentDepth) / depth : 0;
31
- const maxAtDepth = minChildren + (maxInitialChildren - minChildren) * decayFactor;
32
- const maxChildrenAtDepth = Math.max(minChildren, Math.floor(maxAtDepth));
33
- let numChildren = 0;
34
- if (currentDepth < depth) {
35
- numChildren = faker.number.int({ min: minChildren, max: maxChildrenAtDepth });
36
- }
37
- return numChildren;
38
- }
@@ -1,100 +0,0 @@
1
- import { faker } from "@faker-js/faker"
2
- /**
3
- * Generates a random tree structure where the probability of generating children
4
- * decreases linearly with depth, resulting in a tree that is bushy at the top
5
- * and sparse towards the maximum depth.
6
- *
7
- * This function is agnostic to the actual node structure, relying on the caller-provided
8
- * callbacks to create and link nodes.
9
- *
10
- * It uses faker.js for randomness (even on the option parameters).
11
- *
12
- * See {@link generateRandomDoc} for an example of how to use it.
13
- *
14
- * @experimental
15
- */
16
- export function generateRandomTree<T, TData>(
17
- {
18
- createNode,
19
- parentData
20
- }: {
21
- /** A function that creates a new node (type T) given its depth. */
22
- createNode: (children: T[], isLeaf: boolean, parentData?: TData) => T | undefined
23
- /**
24
- * A function that is called before creating a a node or it's children whose result is passed to both. This allows creating children that will be compatible with the parent (via this same function, it is passed it's parent).
25
- *
26
- * For example, we could randomly generate a parent type. It's then passed both to the children, to limit the types of children nodes, and to the parent so it can actually create it.
27
- */
28
- parentData?: (parentData?: TData) => TData
29
- },
30
- {
31
- rootNodes: rootNodes = faker.number.int({ min: 0, max: 5 }),
32
- depth = faker.number.int({ min: 0, max: 5 }),
33
- minChildren = 0,
34
- maxInitialChildren = 10,
35
- initialData
36
- }: {
37
- /**
38
- * The exact number of nodes at depth 0.
39
- *
40
- * @default faker.number.int({ min: 0, max: 5 })
41
- */
42
- rootNodes?: number
43
- /**
44
- * The maximum depth of the tree (0-indexed). Nodes at this depth will have 0 children.
45
- *
46
- * @default faker.number.int({ min: 0, max: 5 })
47
- */
48
- depth?: number
49
- /**
50
- * The absolute minimum number of children any node can have.
51
- *
52
- * @default 0
53
- */
54
- minChildren?: number
55
- /**
56
- * The maximum children a node can have at depth 0. This value scales down as depth increases.
57
- *
58
- * @default 10
59
- */
60
- maxInitialChildren?: number
61
- initialData?: TData
62
- } = {}
63
- ): T[] {
64
- const generateChildren = (currentDepth: number, childCount: number, pData?: TData): T[] => {
65
- const res: T[] = []
66
- for (let i = 0; i < childCount; i++) {
67
- const numChildren = calculateNumChildren(depth, currentDepth, minChildren, maxInitialChildren)
68
-
69
- const data = parentData?.(pData) ?? undefined
70
- const parent = createNode(
71
- generateChildren(currentDepth + 1, numChildren, data),
72
- numChildren === 0,
73
- data
74
- )
75
- if (parent === undefined) continue
76
- res.push(parent)
77
- }
78
- return res
79
- }
80
-
81
- return generateChildren(0, rootNodes, initialData)
82
- }
83
-
84
- function calculateNumChildren(
85
- depth: number,
86
- currentDepth: number,
87
- minChildren: number,
88
- maxInitialChildren: number
89
- ): number {
90
- const decayFactor = depth > 0 ? (depth - currentDepth) / depth : 0
91
- const maxAtDepth = minChildren + (maxInitialChildren - minChildren) * decayFactor
92
- const maxChildrenAtDepth = Math.max(minChildren, Math.floor(maxAtDepth))
93
-
94
- let numChildren = 0
95
-
96
- if (currentDepth < depth) {
97
- numChildren = faker.number.int({ min: minChildren, max: maxChildrenAtDepth })
98
- }
99
- return numChildren
100
- }