json-to-spec 19.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 (217) hide show
  1. package/README.md +118 -0
  2. package/docs/demo.html +426 -0
  3. package/docs/dist/min.js +164 -0
  4. package/docs/dist/v1/min.js +124 -0
  5. package/docs/dist/v17/min.js +355 -0
  6. package/docs/dist/v18/min.js +160 -0
  7. package/docs/dist/v19/min.js +164 -0
  8. package/docs/dist/v2/min.js +129 -0
  9. package/docs/dist/v3/min.js +201 -0
  10. package/docs/index.html +248 -0
  11. package/docs/pages/architecture.html +115 -0
  12. package/docs/pages/how-it-works.html +125 -0
  13. package/docs/pages/what-not.html +100 -0
  14. package/docs/pages/what.html +105 -0
  15. package/docs/pages/why.html +88 -0
  16. package/docs/samples/form.html +265 -0
  17. package/docs/samples/index.html +112 -0
  18. package/docs/samples/table.html +306 -0
  19. package/index.js +2 -0
  20. package/package.json +50 -0
  21. package/samples/dataList/index.html +31 -0
  22. package/samples/dataList/index.js +62 -0
  23. package/samples/dataList/input/data.json +5043 -0
  24. package/samples/dataList/input/structure.json +38 -0
  25. package/samples/dataList copy/index.html +30 -0
  26. package/samples/dataList copy/index.js +61 -0
  27. package/samples/dataList copy/input/data.json +35307 -0
  28. package/samples/dataList copy/input/structure.json +18 -0
  29. package/samples/form/index.html +29 -0
  30. package/samples/form/index.js +64 -0
  31. package/samples/form/input/data.json +9 -0
  32. package/samples/form/input/structure.json +192 -0
  33. package/samples/form-/data.json +42 -0
  34. package/samples/form-/structure.json +110 -0
  35. package/samples/select/index.html +31 -0
  36. package/samples/select/index.js +61 -0
  37. package/samples/select/input/data.json +35287 -0
  38. package/samples/select/input/select.json +18 -0
  39. package/samples/select/input/structure.json +47 -0
  40. package/samples/table/index.html +29 -0
  41. package/samples/table/index.js +61 -0
  42. package/samples/table/input/data.json +35287 -0
  43. package/samples/table/input/structure copy.json +47 -0
  44. package/samples/table/input/structure.json +86 -0
  45. package/samples/table/input/table.json +135 -0
  46. package/samples/table-/data.json +68 -0
  47. package/samples/table-/structure.json +235 -0
  48. package/src/index.js +2 -0
  49. package/src/v10/index.js +96 -0
  50. package/src/v10/instructionEngine/compile/compileTree.js +191 -0
  51. package/src/v10/instructionEngine/compileTree.js +1 -0
  52. package/src/v10/instructionEngine/expandIteration.js +1 -0
  53. package/src/v10/instructionEngine/index.js +29 -0
  54. package/src/v10/instructionEngine/interpolate.js +2 -0
  55. package/src/v10/instructionEngine/iteration/expandIteration.js +95 -0
  56. package/src/v10/instructionEngine/operation/operation.js +237 -0
  57. package/src/v10/instructionEngine/other/other.js +150 -0
  58. package/src/v10/instructionEngine/pipeline/pipeline.js +47 -0
  59. package/src/v10/instructionEngine/replace/replace.js +173 -0
  60. package/src/v10/instructionEngine/resolve/resolvePath.js +18 -0
  61. package/src/v10/instructionEngine/resolve/resolveTokenValue.js +22 -0
  62. package/src/v10/instructionEngine/resolvePath.js +1 -0
  63. package/src/v10/instructionEngine/resolveTokenValue.js +1 -0
  64. package/src/v10/instructionEngine/story/storyInjector.js +50 -0
  65. package/src/v10/instructionEngine/storyInjector.js +1 -0
  66. package/src/v10/instructionEngine/value/interpolate.js +38 -0
  67. package/src/v11/index.js +116 -0
  68. package/src/v11/iterate.js +270 -0
  69. package/src/v11/replace.js +169 -0
  70. package/src/v11/resolvePath.js +29 -0
  71. package/src/v12/index.js +122 -0
  72. package/src/v12/iterate/bindFormValues.js +74 -0
  73. package/src/v12/iterate/expandIteration.js +181 -0
  74. package/src/v12/iterate/index.js +10 -0
  75. package/src/v12/iterate/iterate.js +56 -0
  76. package/src/v12/iterate/iterateNode.js +86 -0
  77. package/src/v12/iterate/resolveCollection.js +37 -0
  78. package/src/v12/iterate.js +270 -0
  79. package/src/v12/replace.js +169 -0
  80. package/src/v12/resolvePath.js +29 -0
  81. package/src/v13/index.js +110 -0
  82. package/src/v13/iterate/bindFormValues.js +74 -0
  83. package/src/v13/iterate/expandIteration.js +188 -0
  84. package/src/v13/iterate/index.js +9 -0
  85. package/src/v13/iterate/iterate.js +32 -0
  86. package/src/v13/iterate/iterateNode.js +86 -0
  87. package/src/v13/iterate/resolveCollection.js +37 -0
  88. package/src/v13/iterate/walk.js +90 -0
  89. package/src/v13/iterate.js +270 -0
  90. package/src/v13/replace.js +169 -0
  91. package/src/v13/resolvePath.js +29 -0
  92. package/src/v14/index.js +112 -0
  93. package/src/v14/iterate/bindFormValues.js +74 -0
  94. package/src/v14/iterate/expandIteration.js +188 -0
  95. package/src/v14/iterate/index.js +9 -0
  96. package/src/v14/iterate/iterate.js +32 -0
  97. package/src/v14/iterate/resolveCollection.js +37 -0
  98. package/src/v14/iterate/walk/walk copy 2.js +114 -0
  99. package/src/v14/iterate/walk/walk copy.js +90 -0
  100. package/src/v14/iterate/walk/walk.js +98 -0
  101. package/src/v14/iterate.js +270 -0
  102. package/src/v14/replace/index.js +16 -0
  103. package/src/v14/replace/walk.js +90 -0
  104. package/src/v14/replace.js +153 -0
  105. package/src/v14/resolvePath.js +29 -0
  106. package/src/v15/index.js +123 -0
  107. package/src/v15/iterate/bindFormValues.js +74 -0
  108. package/src/v15/iterate/expandIteration.js +188 -0
  109. package/src/v15/iterate/index.js +9 -0
  110. package/src/v15/iterate/iterate.js +32 -0
  111. package/src/v15/iterate/resolveCollection.js +37 -0
  112. package/src/v15/iterate/walk/walk copy 2.js +114 -0
  113. package/src/v15/iterate/walk/walk copy.js +90 -0
  114. package/src/v15/iterate/walk/walk.js +195 -0
  115. package/src/v15/iterate.js +270 -0
  116. package/src/v15/replace/index.js +16 -0
  117. package/src/v15/replace/walk.js +90 -0
  118. package/src/v15/replace.js +155 -0
  119. package/src/v15/resolvePath.js +29 -0
  120. package/src/v16/index.js +129 -0
  121. package/src/v16/iterate/bindFormValues.js +74 -0
  122. package/src/v16/iterate/expandIteration.js +188 -0
  123. package/src/v16/iterate/index.js +9 -0
  124. package/src/v16/iterate/iterate.js +32 -0
  125. package/src/v16/iterate/resolveCollection.js +37 -0
  126. package/src/v16/iterate/walk/walk copy 2.js +114 -0
  127. package/src/v16/iterate/walk/walk copy.js +90 -0
  128. package/src/v16/iterate/walk/walk.js +196 -0
  129. package/src/v16/iterate.js +270 -0
  130. package/src/v16/replace/index.js +16 -0
  131. package/src/v16/replace/walk.js +90 -0
  132. package/src/v16/replace.js +155 -0
  133. package/src/v16/resolvePath.js +29 -0
  134. package/src/v17/index.js +119 -0
  135. package/src/v17/iterate/bindFormValues.js +74 -0
  136. package/src/v17/iterate/expandIteration.js +188 -0
  137. package/src/v17/iterate/index.js +9 -0
  138. package/src/v17/iterate/iterate.js +32 -0
  139. package/src/v17/iterate/resolveCollection.js +37 -0
  140. package/src/v17/iterate/walk/iterateDo.js +42 -0
  141. package/src/v17/iterate/walk/replaceWithData.js +60 -0
  142. package/src/v17/iterate/walk/walk.js +94 -0
  143. package/src/v17/iterate.js +270 -0
  144. package/src/v17/replace/index.js +16 -0
  145. package/src/v17/replace/walk.js +90 -0
  146. package/src/v17/replace.js +160 -0
  147. package/src/v17/resolvePath.js +29 -0
  148. package/src/v18/index.js +66 -0
  149. package/src/v18/meta.js +7 -0
  150. package/src/v18/registerGlobal.js +17 -0
  151. package/src/v18/replace.js +18 -0
  152. package/src/v18/resolvePath.js +29 -0
  153. package/src/v18/walk/iterate/v1/index.js +42 -0
  154. package/src/v18/walk/iterate/v2/index.js +32 -0
  155. package/src/v18/walk/iterate/v3/index.js +46 -0
  156. package/src/v18/walk/iterate/v4/createChildren.js +24 -0
  157. package/src/v18/walk/iterate/v4/index.js +25 -0
  158. package/src/v18/walk/replaceWithData/v1/index.js +42 -0
  159. package/src/v18/walk/replaceWithData/v1/replaceCommon.js +21 -0
  160. package/src/v18/walk/replaceWithData/v2/index.js +105 -0
  161. package/src/v18/walk/replaceWithData/v2/replaceCommon.js +21 -0
  162. package/src/v18/walk/replaceWithData/v3/index.js +15 -0
  163. package/src/v18/walk/replaceWithData/v3/replaceAttributes.js +29 -0
  164. package/src/v18/walk/replaceWithData/v3/replaceCommon.js +23 -0
  165. package/src/v18/walk/replaceWithData/v3/replaceNodeValue.js +27 -0
  166. package/src/v18/walk/walk.js +94 -0
  167. package/src/v19/index.js +55 -0
  168. package/src/v19/meta.js +7 -0
  169. package/src/v19/registerGlobal.js +17 -0
  170. package/src/v19/replace.js +18 -0
  171. package/src/v19/resolvePath.js +29 -0
  172. package/src/v19/walk/iterate/v1/index.js +42 -0
  173. package/src/v19/walk/iterate/v2/index.js +32 -0
  174. package/src/v19/walk/iterate/v3/index.js +46 -0
  175. package/src/v19/walk/iterate/v4/createChildren.js +27 -0
  176. package/src/v19/walk/iterate/v4/index.js +25 -0
  177. package/src/v19/walk/replaceWithData/v1/index.js +42 -0
  178. package/src/v19/walk/replaceWithData/v1/replaceCommon.js +21 -0
  179. package/src/v19/walk/replaceWithData/v2/index.js +105 -0
  180. package/src/v19/walk/replaceWithData/v2/replaceCommon.js +21 -0
  181. package/src/v19/walk/replaceWithData/v3/index.js +15 -0
  182. package/src/v19/walk/replaceWithData/v3/replaceAttributes.js +29 -0
  183. package/src/v19/walk/replaceWithData/v3/replaceCommon.js +23 -0
  184. package/src/v19/walk/replaceWithData/v3/replaceNodeValue.js +27 -0
  185. package/src/v19/walk/walk.js +97 -0
  186. package/src/v8/index.js +69 -0
  187. package/src/v8/instructionEngine/compile/compileTree.js +191 -0
  188. package/src/v8/instructionEngine/compileTree.js +1 -0
  189. package/src/v8/instructionEngine/expandIteration.js +1 -0
  190. package/src/v8/instructionEngine/index.js +16 -0
  191. package/src/v8/instructionEngine/interpolate.js +2 -0
  192. package/src/v8/instructionEngine/iteration/expandIteration.js +95 -0
  193. package/src/v8/instructionEngine/resolve/resolvePath.js +18 -0
  194. package/src/v8/instructionEngine/resolve/resolveTokenValue.js +22 -0
  195. package/src/v8/instructionEngine/resolvePath.js +1 -0
  196. package/src/v8/instructionEngine/resolveTokenValue.js +1 -0
  197. package/src/v8/instructionEngine/story/storyInjector.js +50 -0
  198. package/src/v8/instructionEngine/storyInjector.js +1 -0
  199. package/src/v8/instructionEngine/value/interpolate.js +38 -0
  200. package/src/v9/index.js +96 -0
  201. package/src/v9/instructionEngine/compile/compileTree.js +191 -0
  202. package/src/v9/instructionEngine/compileTree.js +1 -0
  203. package/src/v9/instructionEngine/expandIteration.js +1 -0
  204. package/src/v9/instructionEngine/index.js +29 -0
  205. package/src/v9/instructionEngine/interpolate.js +2 -0
  206. package/src/v9/instructionEngine/iteration/expandIteration.js +95 -0
  207. package/src/v9/instructionEngine/operation/operation.js +237 -0
  208. package/src/v9/instructionEngine/other/other.js +150 -0
  209. package/src/v9/instructionEngine/pipeline/pipeline.js +47 -0
  210. package/src/v9/instructionEngine/replace/replace.js +173 -0
  211. package/src/v9/instructionEngine/resolve/resolvePath.js +18 -0
  212. package/src/v9/instructionEngine/resolve/resolveTokenValue.js +22 -0
  213. package/src/v9/instructionEngine/resolvePath.js +1 -0
  214. package/src/v9/instructionEngine/resolveTokenValue.js +1 -0
  215. package/src/v9/instructionEngine/story/storyInjector.js +50 -0
  216. package/src/v9/instructionEngine/storyInjector.js +1 -0
  217. package/src/v9/instructionEngine/value/interpolate.js +38 -0
@@ -0,0 +1,169 @@
1
+ import resolvePath from "./resolvePath.js";
2
+
3
+ /**
4
+ * Resolves a token expression path against local context first, then root data.
5
+ */
6
+ const resolveTokenValue = ({ inPath, inContext, inData }) => {
7
+ const localPath = inPath;
8
+ const localContext = inContext;
9
+ const localData = inData;
10
+
11
+ let value = resolvePath({ inData: localContext, inPath: localPath });
12
+
13
+ if (value === undefined && localContext && typeof localContext === "object") {
14
+ if (localContext.item && typeof localContext.item === "object") {
15
+ value = resolvePath({ inData: localContext.item, inPath: localPath });
16
+ }
17
+ if (value === undefined && localContext.row && typeof localContext.row === "object") {
18
+ value = resolvePath({ inData: localContext.row, inPath: localPath });
19
+ }
20
+ }
21
+
22
+ if (value === undefined && localData) {
23
+ value = resolvePath({ inData: localData, inPath: localPath });
24
+ }
25
+
26
+ return value;
27
+ };
28
+
29
+ /**
30
+ * Replaces ${path} tokens in a string value.
31
+ */
32
+ const replaceValue = ({ inValue, inContext, inData }) => {
33
+ const localValue = inValue;
34
+ const localContext = inContext;
35
+ const localData = inData;
36
+
37
+ if (typeof localValue !== "string" || !localValue.includes("${")) {
38
+ return localValue;
39
+ }
40
+
41
+ const hasItemContext = Boolean(localContext && (localContext.item || Object.keys(localContext).length > 0));
42
+
43
+ // Exact match: "${path}" -> preserve primitive types (boolean, number, object)
44
+ const exactMatch = localValue.match(/^\$\{([^}]+)\}$/);
45
+ if (exactMatch) {
46
+ const token = resolveTokenValue({
47
+ inPath: exactMatch[1].trim(),
48
+ inContext: localContext,
49
+ inData: localData
50
+ });
51
+ return token !== undefined && token !== null ? token : (hasItemContext ? "" : localValue);
52
+ }
53
+
54
+ // Substring interpolation: "Hello ${name}" -> string replacement
55
+ return localValue.replace(/\$\{([^}]+)\}/g, (fullMatch, expr) => {
56
+ const token = resolveTokenValue({
57
+ inPath: expr.trim(),
58
+ inContext: localContext,
59
+ inData: localData
60
+ });
61
+ return token !== undefined && token !== null ? String(token) : (hasItemContext ? "" : fullMatch);
62
+ });
63
+ };
64
+
65
+ /**
66
+ * Recursively walks a JSON tree and replaces all ${...} tokens against data.
67
+ */
68
+ export const replaceNode = ({ inNode, inContext = {}, inData = {} } = {}) => {
69
+ const localNode = inNode;
70
+ const localContext = inContext;
71
+ const localData = inData;
72
+
73
+ if (localNode === null || localNode === undefined) return localNode;
74
+
75
+ if (Array.isArray(localNode)) {
76
+ return localNode.map(child => replaceNode({
77
+ inNode: child,
78
+ inContext: localContext,
79
+ inData: localData
80
+ }));
81
+ }
82
+
83
+ if (typeof localNode === "string") {
84
+ return replaceValue({
85
+ inValue: localNode,
86
+ inContext: localContext,
87
+ inData: localData
88
+ });
89
+ }
90
+
91
+ if (typeof localNode !== "object") {
92
+ return localNode;
93
+ }
94
+
95
+ const clone = { ...localNode };
96
+
97
+ if (clone.textContent && typeof clone.textContent === "string") {
98
+ clone.textContent = replaceValue({
99
+ inValue: clone.textContent,
100
+ inContext: localContext,
101
+ inData: localData
102
+ });
103
+ }
104
+
105
+ if (clone.attributes) {
106
+ clone.attributes = { ...clone.attributes };
107
+ for (const [name, val] of Object.entries(clone.attributes)) {
108
+ if (typeof val === "string") {
109
+ clone.attributes[name] = replaceValue({
110
+ inValue: val,
111
+ inContext: localContext,
112
+ inData: localData
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+ if (clone.properties) {
119
+ clone.properties = { ...clone.properties };
120
+ for (const [name, val] of Object.entries(clone.properties)) {
121
+ if (typeof val === "string") {
122
+ clone.properties[name] = replaceValue({
123
+ inValue: val,
124
+ inContext: localContext,
125
+ inData: localData
126
+ });
127
+ }
128
+ }
129
+ }
130
+
131
+ if (Array.isArray(clone.children)) {
132
+ clone.children = clone.children.map(child => replaceNode({
133
+ inNode: child,
134
+ inContext: localContext,
135
+ inData: localData
136
+ }));
137
+ }
138
+
139
+ return clone;
140
+ };
141
+
142
+ /**
143
+ * Main replace function: (structure, data) -> replaced JSON structure
144
+ */
145
+ export const replace = (inStructureOrOptions, inData = {}) => {
146
+ let localStructure = inStructureOrOptions;
147
+ let localData = inData;
148
+ let localContext = {};
149
+
150
+ if (inStructureOrOptions && typeof inStructureOrOptions === "object" && !Array.isArray(inStructureOrOptions)) {
151
+ if ("inStructure" in inStructureOrOptions || "inTree" in inStructureOrOptions || "inNode" in inStructureOrOptions) {
152
+ localStructure = inStructureOrOptions.inStructure || inStructureOrOptions.inTree || inStructureOrOptions.inNode;
153
+ localData = inStructureOrOptions.inData || localData;
154
+ localContext = inStructureOrOptions.inContext || localContext;
155
+ } else if ("structure" in inStructureOrOptions) {
156
+ localStructure = inStructureOrOptions.structure;
157
+ localData = inStructureOrOptions.data || localData;
158
+ localContext = inStructureOrOptions.context || localContext;
159
+ }
160
+ }
161
+
162
+ return replaceNode({
163
+ inNode: localStructure,
164
+ inContext: localContext,
165
+ inData: localData
166
+ });
167
+ };
168
+
169
+ export default replace;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Resolves a dot-delimited path or array of keys from a data object.
3
+ *
4
+ * @param {Object} inArgs
5
+ * @param {Object} inArgs.inData - Target object to resolve from
6
+ * @param {string|Array} inArgs.inPath - Key or dot-path (e.g. "meta.formTitle", "columns")
7
+ * @returns {*} Resolved value or undefined
8
+ */
9
+ export const resolvePath = ({ inData, inPath }) => {
10
+ const localData = inData;
11
+ const localPath = inPath;
12
+
13
+ if (localData === null || localData === undefined) return undefined;
14
+ if (localPath === undefined || localPath === null || localPath === "" || localPath === ".") {
15
+ return localData;
16
+ }
17
+
18
+ const segments = Array.isArray(localPath) ? localPath : String(localPath).split(".");
19
+ let current = localData;
20
+
21
+ for (const segment of segments) {
22
+ if (current === null || current === undefined) return undefined;
23
+ current = current[segment];
24
+ }
25
+
26
+ return current;
27
+ };
28
+
29
+ export default resolvePath;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * json-to-spec v11
3
+ *
4
+ * Minimalist 2-Layer Specification Compiler:
5
+ * 1. replace: Pure ${...} token replacement across JSON structures.
6
+ * 2. iterate: Evaluates jsonToSpec operations, expanding collections into clean children.
7
+ */
8
+
9
+ import { replace, replaceNode } from "./replace.js";
10
+
11
+ // import { iterate, iterateNode, operation } from "./iterate.js";
12
+
13
+ import {
14
+ iterate,
15
+ operation
16
+ } from "./iterate/index.js";
17
+
18
+ export const meta = {
19
+ version: "11.0.0",
20
+ name: "json-to-spec/v11",
21
+ description: "Minimalist 2-layer compiler: pure value replace + jsonToSpec iterate"
22
+ };
23
+
24
+ /**
25
+ * Executes a custom pipeline of transformations over the structure.
26
+ */
27
+ export const pipeline = ({ inStructure, inData = {}, inSteps = [replace, iterate] } = {}) => {
28
+ const localData = inData;
29
+ const localSteps = inSteps;
30
+
31
+ let current = inStructure;
32
+ for (const step of localSteps) {
33
+ if (typeof step === "function") {
34
+ current = step({ inStructure: current, inData: localData });
35
+ };
36
+ }
37
+ return current;
38
+ };
39
+
40
+ export const pipe = (...inSteps) => {
41
+ const localSteps = inSteps.length > 0 ? inSteps : [replace, iterate];
42
+ return (inStructure, inData = {}) => {
43
+ return pipeline({ inStructure, inData, inSteps: localSteps });
44
+ };
45
+ };
46
+
47
+ /**
48
+ * Main compiler entry point:
49
+ * 1. Replace values: replace(structure, data)
50
+ * 2. Iterate/Operate: iterate(replacedStructure, data)
51
+ */
52
+ export const compile = (inStructureOrOptions, inData = {}) => {
53
+ let localStructure = inStructureOrOptions;
54
+ let localData = inData;
55
+
56
+ if (inStructureOrOptions && typeof inStructureOrOptions === "object" && !Array.isArray(inStructureOrOptions)) {
57
+ if ("inStructure" in inStructureOrOptions || "inTree" in inStructureOrOptions || "inNode" in inStructureOrOptions) {
58
+ localStructure = inStructureOrOptions.inStructure || inStructureOrOptions.inTree || inStructureOrOptions.inNode;
59
+ localData = inStructureOrOptions.inData || localData;
60
+ } else if ("structure" in inStructureOrOptions) {
61
+ localStructure = inStructureOrOptions.structure;
62
+ localData = inStructureOrOptions.data || localData;
63
+ }
64
+ }
65
+
66
+ // Step 1: Replace tokens in the structure
67
+ const replaced = replace({
68
+ inStructure: localStructure,
69
+ inData: localData
70
+ });
71
+
72
+ // Step 2: Expand jsonToSpec operations on the replaced structure
73
+ const spec = iterate({
74
+ inStructure: replaced,
75
+ inData: localData
76
+ });
77
+
78
+ return spec;
79
+ };
80
+
81
+ export const jsonToSpec = {
82
+ replace,
83
+ iterate,
84
+ operation,
85
+ pipeline,
86
+ pipe,
87
+ compile
88
+ };
89
+
90
+ export {
91
+ replace,
92
+ replaceNode,
93
+ iterate,
94
+ operation
95
+ };
96
+
97
+ if (typeof globalThis !== "undefined") {
98
+ globalThis.ks ??= {};
99
+ globalThis.ks["json-to-spec"] = {
100
+ meta,
101
+ compile,
102
+ replace,
103
+ iterate,
104
+ operation,
105
+ pipeline,
106
+ jsonToSpec
107
+ };
108
+ }
109
+
110
+ export default compile;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Recursively binds form field values into
3
+ * input, textarea, select and option nodes.
4
+ */
5
+ const bindFormValues = ({ inNode, inItem, inData }) => {
6
+ const localNode = inNode;
7
+ const localItem = inItem;
8
+ const localData = inData;
9
+
10
+ if (!localNode || typeof localNode !== "object") {
11
+ return localNode;
12
+ }
13
+
14
+ if (localNode.attributes) {
15
+ const isFormField = [
16
+ "input",
17
+ "textarea",
18
+ "select",
19
+ "option"
20
+ ].includes(localNode.tagName?.toLowerCase());
21
+
22
+ if (isFormField) {
23
+ const fieldName =
24
+ localItem.field ||
25
+ localItem.columnName ||
26
+ localItem.name;
27
+
28
+ if (
29
+ localItem.value !== undefined &&
30
+ localItem.value !== null
31
+ ) {
32
+ localNode.attributes.value = String(localItem.value);
33
+
34
+ if (
35
+ localNode.tagName === "textarea" &&
36
+ !localNode.textContent
37
+ ) {
38
+ localNode.textContent = String(localItem.value);
39
+ }
40
+ } else if (
41
+ fieldName &&
42
+ localData[fieldName] !== undefined &&
43
+ localData[fieldName] !== null
44
+ ) {
45
+ localNode.attributes.value = String(
46
+ localData[fieldName]
47
+ );
48
+
49
+ if (
50
+ localNode.tagName === "textarea" &&
51
+ !localNode.textContent
52
+ ) {
53
+ localNode.textContent = String(
54
+ localData[fieldName]
55
+ );
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ if (Array.isArray(localNode.children)) {
62
+ localNode.children.forEach(child => {
63
+ bindFormValues({
64
+ inNode: child,
65
+ inItem: localItem,
66
+ inData: localData
67
+ });
68
+ });
69
+ }
70
+
71
+ return localNode;
72
+ };
73
+
74
+ export default bindFormValues;
@@ -0,0 +1,188 @@
1
+ import resolveCollection from "./resolveCollection.js";
2
+ import bindFormValues from "./bindFormValues.js";
3
+ import { replaceNode } from "../replace.js";
4
+
5
+ /**
6
+ * Expands a single iteration directive into an array of
7
+ * specification nodes.
8
+ */
9
+ const expandIteration = ({
10
+ inNode,
11
+ inContext,
12
+ inData,
13
+ walk
14
+ }) => {
15
+ const localNode = inNode;
16
+ const localContext = inContext;
17
+ const localData = inData;
18
+
19
+ const instruction = localNode && (
20
+ localNode.jsonToSpec ||
21
+ (localNode.operation ? localNode : null)
22
+ );
23
+
24
+ if (!instruction) return undefined;
25
+
26
+ const operationType = instruction.operation;
27
+
28
+ if (
29
+ operationType !== "iterate" &&
30
+ !instruction.source &&
31
+ !instruction.$iterate
32
+ ) {
33
+ return undefined;
34
+ }
35
+
36
+ const sourceKey =
37
+ instruction.source ||
38
+ instruction.iterateOn ||
39
+ instruction.$iterate;
40
+
41
+ const collection = resolveCollection({
42
+ inNode: localNode,
43
+ inContext: localContext,
44
+ inData: localData
45
+ }) || [];
46
+
47
+ if (!Array.isArray(collection)) {
48
+ if (localNode && localNode.tagName) {
49
+ const cloned = { ...localNode };
50
+ delete cloned.jsonToSpec;
51
+ cloned.children = [];
52
+ return cloned;
53
+ }
54
+
55
+ return [];
56
+ }
57
+
58
+ const template =
59
+ instruction.template ||
60
+ instruction.item ||
61
+ {};
62
+
63
+ const filter = instruction.filter;
64
+
65
+ const filtered = collection.filter(item => {
66
+ if (!item || typeof item !== "object") {
67
+ return true;
68
+ }
69
+
70
+ if (filter && typeof filter === "object") {
71
+ for (const [key, value] of Object.entries(filter)) {
72
+ if (item[key] !== value) {
73
+ return false;
74
+ }
75
+ }
76
+ }
77
+
78
+ if (item.isVisible === false) {
79
+ return false;
80
+ }
81
+
82
+ return true;
83
+ });
84
+
85
+ const expandedChildren = [];
86
+
87
+ filtered.forEach((item, index) => {
88
+ const itemContext = {
89
+ ...localContext,
90
+ item,
91
+ ...(typeof item === "object" && item !== null ? item : {}),
92
+ $index: index,
93
+ $number: index + 1
94
+ };
95
+
96
+ if (
97
+ sourceKey === "rows" ||
98
+ sourceKey.endsWith(".rows") ||
99
+ sourceKey === "items" ||
100
+ sourceKey.endsWith("Rows")
101
+ ) {
102
+ itemContext.row = item;
103
+ }
104
+
105
+ if (
106
+ localContext.row &&
107
+ (item.field || item.columnName || item.name)
108
+ ) {
109
+ const fieldKey =
110
+ item.field ||
111
+ item.columnName ||
112
+ item.name;
113
+
114
+ const cellValue = localContext.row[fieldKey];
115
+
116
+ if (cellValue !== undefined) {
117
+ itemContext.cellValue = cellValue;
118
+ itemContext.value = cellValue;
119
+
120
+ if (
121
+ typeof item === "object" &&
122
+ item !== null &&
123
+ item.value === undefined
124
+ ) {
125
+ item.value = cellValue;
126
+ }
127
+ }
128
+ }
129
+
130
+ const rawTemplate =
131
+ typeof template === "object" &&
132
+ template !== null
133
+ ? JSON.parse(JSON.stringify(template))
134
+ : {
135
+ tagName: "span",
136
+ textContent: template
137
+ };
138
+
139
+ const iteratedTemplate = walk({
140
+ inNode: rawTemplate,
141
+ inContext: itemContext,
142
+ inData: localData,
143
+ inOperation: ({ inNode, inContext, inData }) => {
144
+ return expandIteration({
145
+ inNode,
146
+ inContext,
147
+ inData,
148
+ walk
149
+ });
150
+ }
151
+ });
152
+
153
+ const replacedTemplate = replaceNode({
154
+ inNode: iteratedTemplate,
155
+ inContext: itemContext,
156
+ inData: localData
157
+ });
158
+
159
+ bindFormValues({
160
+ inNode: replacedTemplate,
161
+ inItem: item,
162
+ inData: localData
163
+ });
164
+
165
+ if (Array.isArray(replacedTemplate)) {
166
+ expandedChildren.push(...replacedTemplate);
167
+ } else if (
168
+ replacedTemplate !== null &&
169
+ replacedTemplate !== undefined
170
+ ) {
171
+ expandedChildren.push(replacedTemplate);
172
+ }
173
+ });
174
+
175
+ if (localNode && localNode.tagName) {
176
+ const cloned = { ...localNode };
177
+
178
+ delete cloned.jsonToSpec;
179
+
180
+ cloned.children = expandedChildren;
181
+
182
+ return cloned;
183
+ }
184
+
185
+ return expandedChildren;
186
+ };
187
+
188
+ export default expandIteration;
@@ -0,0 +1,9 @@
1
+ import iterate from "./iterate.js";
2
+
3
+ export {
4
+ iterate
5
+ };
6
+
7
+ export const operation = iterate;
8
+
9
+ export default iterate;
@@ -0,0 +1,32 @@
1
+ import walk from "./walk.js";
2
+ import expandIteration from "./expandIteration.js";
3
+
4
+ /**
5
+ * Iterates a structure by walking the tree
6
+ * and applying the iteration operation to each node.
7
+ */
8
+ const iterate = ({
9
+ inStructure,
10
+ inData = {},
11
+ inContext = {}
12
+ } = {}) => {
13
+ return walk({
14
+ inNode: inStructure,
15
+ inData,
16
+ inContext,
17
+ inOperation: ({ inNode, inContext, inData }) => {
18
+ return expandIteration({
19
+ inNode,
20
+ inContext,
21
+ inData,
22
+ walk
23
+ });
24
+ }
25
+ });
26
+ };
27
+
28
+ export {
29
+ iterate
30
+ };
31
+
32
+ export default iterate;
@@ -0,0 +1,86 @@
1
+ import expandIteration from "./expandIteration.js";
2
+
3
+ /**
4
+ * Recursively walks a JSON structure and resolves
5
+ * jsonToSpec iteration directives.
6
+ */
7
+ const iterateNode = ({
8
+ inNode,
9
+ inContext = {},
10
+ inData = {}
11
+ } = {}) => {
12
+ const localNode = inNode;
13
+ const localContext = inContext;
14
+ const localData = inData;
15
+
16
+ if (localNode === null || localNode === undefined) {
17
+ return localNode;
18
+ }
19
+
20
+ if (Array.isArray(localNode)) {
21
+ const flattened = [];
22
+
23
+ for (const child of localNode) {
24
+ const processed = iterateNode({
25
+ inNode: child,
26
+ inContext: localContext,
27
+ inData: localData
28
+ });
29
+
30
+ if (Array.isArray(processed)) {
31
+ flattened.push(...processed);
32
+ } else if (
33
+ processed !== null &&
34
+ processed !== undefined
35
+ ) {
36
+ flattened.push(processed);
37
+ }
38
+ }
39
+
40
+ return flattened;
41
+ }
42
+
43
+ if (typeof localNode !== "object") {
44
+ return localNode;
45
+ }
46
+
47
+ const iterationResult = expandIteration({
48
+ inNode: localNode,
49
+ inContext: localContext,
50
+ inData: localData,
51
+ iterateNode
52
+ });
53
+
54
+ if (iterationResult !== undefined) {
55
+ return iterationResult;
56
+ }
57
+
58
+ const clone = { ...localNode };
59
+
60
+ if (Array.isArray(clone.children)) {
61
+ const processedChildren = [];
62
+
63
+ for (const child of clone.children) {
64
+ const processed = iterateNode({
65
+ inNode: child,
66
+ inContext: localContext,
67
+ inData: localData
68
+ });
69
+
70
+ if (Array.isArray(processed)) {
71
+ processedChildren.push(...processed);
72
+ } else if (
73
+ processed !== null &&
74
+ processed !== undefined
75
+ ) {
76
+ processedChildren.push(processed);
77
+ }
78
+ }
79
+
80
+ clone.children = processedChildren;
81
+ }
82
+
83
+ return clone;
84
+ };
85
+
86
+ export default iterateNode;