@blokjs/react 0.2.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,149 @@
1
+ # @blokjs/react
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial public release of Blok packages.
8
+
9
+ This release includes:
10
+
11
+ - Core packages: @blokjs/shared, @blokjs/helper, @blokjs/runner
12
+ - Node packages: @blokjs/api-call, @blokjs/if-else, @blokjs/react
13
+ - Trigger packages: pubsub, queue, webhook, websocket, worker, cron, grpc
14
+ - CLI tool: blokctl
15
+ - Editor support: @blokjs/lsp-server, @blokjs/syntax
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @blokjs/shared@0.2.0
21
+ - @blokjs/helper@0.2.0
22
+ - @blokjs/runner@0.2.0
23
+
24
+ ## 0.0.17
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies
29
+ - @blokjs/runner@0.1.26
30
+
31
+ ## 0.0.16
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies
36
+ - @blokjs/runner@0.1.25
37
+
38
+ ## 0.0.15
39
+
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies
43
+ - @blokjs/runner@0.1.24
44
+
45
+ ## 0.0.14
46
+
47
+ ### Patch Changes
48
+
49
+ - Updated dependencies
50
+ - @blokjs/runner@0.1.23
51
+
52
+ ## 0.0.13
53
+
54
+ ### Patch Changes
55
+
56
+ - Updated dependencies
57
+ - @blokjs/runner@0.1.22
58
+
59
+ ## 0.0.12
60
+
61
+ ### Patch Changes
62
+
63
+ - Updated dependencies
64
+ - @blokjs/runner@0.1.21
65
+
66
+ ## 0.0.11
67
+
68
+ ### Patch Changes
69
+
70
+ - Updated dependencies
71
+ - @blokjs/helper@0.1.5
72
+ - @blokjs/runner@0.1.20
73
+
74
+ ## 0.0.10
75
+
76
+ ### Patch Changes
77
+
78
+ - Updated dependencies
79
+ - @blokjs/runner@0.1.19
80
+ - @blokjs/shared@0.0.9
81
+
82
+ ## 0.0.9
83
+
84
+ ### Patch Changes
85
+
86
+ - Added examples and create project' command to include examples and 'create node' command with options for type ('module' or 'class') and template ('class' or 'ui')
87
+ - Updated dependencies
88
+ - @blokjs/runner@0.1.18
89
+ - @blokjs/shared@0.0.8
90
+
91
+ ## 0.0.8
92
+
93
+ ### Patch Changes
94
+
95
+ - Updated dependencies
96
+ - @blokjs/runner@0.1.17
97
+
98
+ ## 0.0.7
99
+
100
+ ### Patch Changes
101
+
102
+ - Added support for YAML, XML and TOML in the workflow file. Upgraded package version recommended by Dependabot.
103
+ - Updated dependencies
104
+ - @blokjs/helper@0.1.4
105
+ - @blokjs/runner@0.1.16
106
+ - @blokjs/shared@0.0.7
107
+
108
+ ## 0.0.6
109
+
110
+ ### Patch Changes
111
+
112
+ - Improved the BlokService base class to accept a InputType. This force developer to always create a type to define the Node handle input. Added unit test for pending projects like if-else and api-call.
113
+ - Updated dependencies
114
+ - @blokjs/runner@0.1.15
115
+
116
+ ## 0.0.5
117
+
118
+ ### Patch Changes
119
+
120
+ - Added configuration options
121
+
122
+ ## 0.0.4
123
+
124
+ ### Patch Changes
125
+
126
+ - Updated dependencies
127
+ - @blokjs/shared@0.0.6
128
+ - @blokjs/runner@0.1.14
129
+
130
+ ## 0.0.3
131
+
132
+ ### Patch Changes
133
+
134
+ - Add index.html in the package
135
+
136
+ ## 0.0.2
137
+
138
+ ### Patch Changes
139
+
140
+ - fixed package.json and dependabot error
141
+
142
+ ## 0.0.1
143
+
144
+ ### Patch Changes
145
+
146
+ - Implemented a react node and the chatbot demo page
147
+ - Updated dependencies
148
+ - @blokjs/runner@0.1.13
149
+ - @blokjs/shared@0.0.5
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # Node Documentation
package/app/index.jsx ADDED
@@ -0,0 +1,74 @@
1
+ // public/app.js
2
+ const { useState, useEffect } = React;
3
+
4
+ function ChatBot() {
5
+ const [messages, setMessages] = useState([]);
6
+ const [input, setInput] = useState("");
7
+ const ctx = JSON.parse(atob(ctx_base64));
8
+
9
+ useEffect(() => {
10
+ setMessages((prevMessages) => [
11
+ ...prevMessages,
12
+ { text: "Hello! Let share a fact about cats. Ok?", sender: "bot", id: Math.floor(Math.random() * 100) },
13
+ ]);
14
+ setMessages((prevMessages) => [
15
+ ...prevMessages,
16
+ { text: ctx.response.data.fact, sender: "bot", id: Math.floor(Math.random() * 100) },
17
+ ]);
18
+ }, []);
19
+
20
+ const handleSubmit = (e) => {
21
+ e.preventDefault();
22
+ if (input.trim()) {
23
+ setMessages([...messages, { text: input, sender: "user", id: Math.floor(Math.random() * 100) }]);
24
+ setInput("");
25
+
26
+ // Simulate bot response
27
+ setTimeout(() => {
28
+ setMessages((prevMessages) => [
29
+ ...prevMessages,
30
+ { text: `You said: ${input}`, sender: "bot", id: Math.floor(Math.random() * 100) },
31
+ ]);
32
+ }, 500);
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div className="bg-white rounded-lg shadow-md w-96 overflow-hidden">
38
+ <h1 className="bg-blue-500 text-white text-center py-4 text-xl font-bold">
39
+ Simple {ctx.request.params.workflow}
40
+ </h1>
41
+ <div className="h-96 overflow-y-auto p-4 space-y-4">
42
+ {messages.map((message, index) => (
43
+ <div
44
+ key={message.id}
45
+ className={`p-2 rounded-lg ${
46
+ message.sender === "user" ? "bg-blue-500 text-white ml-auto" : "bg-gray-200 text-gray-800"
47
+ } max-w-[80%] ${message.sender === "user" ? "text-right" : "text-left"}`}
48
+ >
49
+ {message.text}
50
+ </div>
51
+ ))}
52
+ </div>
53
+ <form onSubmit={handleSubmit} className="p-4 border-t border-gray-200">
54
+ <div className="flex">
55
+ <input
56
+ type="text"
57
+ value={input}
58
+ onChange={(e) => setInput(e.target.value)}
59
+ placeholder="Type your message..."
60
+ className="flex-grow px-3 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"
61
+ />
62
+ <button
63
+ type="submit"
64
+ className="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
65
+ >
66
+ Send
67
+ </button>
68
+ </div>
69
+ </form>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ ReactDOM.render(<ChatBot />, document.getElementById("root"));
@@ -0,0 +1,12 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "useBuiltIns": false
7
+ }
8
+ ],
9
+ ["@babel/preset-react"],
10
+ ["minify"]
11
+ ]
12
+ }
package/config.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "node-name",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "group": "API",
6
+ "config": {
7
+ "type": "object",
8
+ "properties": {
9
+ "inputs": {
10
+ "type": "object",
11
+ "properties": {},
12
+ "required": []
13
+ }
14
+ },
15
+ "required": ["inputs"],
16
+ "example": {
17
+ "inputs": {
18
+ "properties": {
19
+ "url": "https://countriesnow.space/api/v0.1/countries/capital",
20
+ "method": "POST",
21
+ "headers": {
22
+ "Content-Type": "application/json"
23
+ },
24
+ "body": {
25
+ "data": "Hello World"
26
+ }
27
+ }
28
+ }
29
+ }
30
+ },
31
+ "input": {
32
+ "anyOf": [
33
+ {
34
+ "type": "object"
35
+ },
36
+ {
37
+ "type": "array"
38
+ },
39
+ {
40
+ "type": "string"
41
+ }
42
+ ],
43
+ "description": "This node accepts an object as input from the previous node or request body"
44
+ },
45
+ "output": {
46
+ "type": "object",
47
+ "description": "The response from the API call"
48
+ },
49
+ "steps": {
50
+ "type": "boolean",
51
+ "default": false
52
+ },
53
+ "functions": {
54
+ "type": "array"
55
+ }
56
+ }
@@ -0,0 +1,3 @@
1
+ declare function ChatBot(): React.JSX.Element;
2
+ declare const useState: typeof React.useState;
3
+ declare const useEffect: typeof React.useEffect;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // public/app.js
3
+ const { useState, useEffect } = React;
4
+ function ChatBot() {
5
+ const [messages, setMessages] = useState([]);
6
+ const [input, setInput] = useState("");
7
+ const ctx = JSON.parse(atob(ctx_base64));
8
+ useEffect(() => {
9
+ setMessages((prevMessages) => [
10
+ ...prevMessages,
11
+ { text: "Hello! Let share a fact about cats. Ok?", sender: "bot", id: Math.floor(Math.random() * 100) },
12
+ ]);
13
+ setMessages((prevMessages) => [
14
+ ...prevMessages,
15
+ { text: ctx.response.data.fact, sender: "bot", id: Math.floor(Math.random() * 100) },
16
+ ]);
17
+ }, []);
18
+ const handleSubmit = (e) => {
19
+ e.preventDefault();
20
+ if (input.trim()) {
21
+ setMessages([...messages, { text: input, sender: "user", id: Math.floor(Math.random() * 100) }]);
22
+ setInput("");
23
+ // Simulate bot response
24
+ setTimeout(() => {
25
+ setMessages((prevMessages) => [
26
+ ...prevMessages,
27
+ { text: `You said: ${input}`, sender: "bot", id: Math.floor(Math.random() * 100) },
28
+ ]);
29
+ }, 500);
30
+ }
31
+ };
32
+ return (<div className="bg-white rounded-lg shadow-md w-96 overflow-hidden">
33
+ <h1 className="bg-blue-500 text-white text-center py-4 text-xl font-bold">
34
+ Simple {ctx.request.params.workflow}
35
+ </h1>
36
+ <div className="h-96 overflow-y-auto p-4 space-y-4">
37
+ {messages.map((message, index) => (<div key={message.id} className={`p-2 rounded-lg ${message.sender === "user" ? "bg-blue-500 text-white ml-auto" : "bg-gray-200 text-gray-800"} max-w-[80%] ${message.sender === "user" ? "text-right" : "text-left"}`}>
38
+ {message.text}
39
+ </div>))}
40
+ </div>
41
+ <form onSubmit={handleSubmit} className="p-4 border-t border-gray-200">
42
+ <div className="flex">
43
+ <input type="text" value={input} onChange={(e) => setInput(e.target.value)} placeholder="Type your message..." className="flex-grow px-3 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"/>
44
+ <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">
45
+ Send
46
+ </button>
47
+ </div>
48
+ </form>
49
+ </div>);
50
+ }
51
+ ReactDOM.render(<ChatBot />, document.getElementById("root"));
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../app/index.jsx"],"names":[],"mappings":";AAAA,gBAAgB;AAChB,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;AAEtC,SAAS,OAAO;IACf,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEzC,SAAS,CAAC,GAAG,EAAE;QACd,WAAW,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;YAC7B,GAAG,YAAY;YACf,EAAE,IAAI,EAAE,yCAAyC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE;SACvG,CAAC,CAAC;QACH,WAAW,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;YAC7B,GAAG,YAAY;YACf,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE;SACpF,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,YAAY,GAAG,CAAC,CAAC,EAAE,EAAE;QAC1B,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClB,WAAW,CAAC,CAAC,GAAG,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YACjG,QAAQ,CAAC,EAAE,CAAC,CAAC;YAEb,wBAAwB;YACxB,UAAU,CAAC,GAAG,EAAE;gBACf,WAAW,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC;oBAC7B,GAAG,YAAY;oBACf,EAAE,IAAI,EAAE,aAAa,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE;iBAClF,CAAC,CAAC;YACJ,CAAC,EAAE,GAAG,CAAC,CAAC;QACT,CAAC;IACF,CAAC,CAAC;IAEF,OAAO,CACN,CAAC,GAAG,CAAC,SAAS,CAAC,oDAAoD,CAClE;GAAA,CAAC,EAAE,CAAC,SAAS,CAAC,2DAA2D,CACxE;WAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CACpC;GAAA,EAAE,EAAE,CACJ;GAAA,CAAC,GAAG,CAAC,SAAS,CAAC,oCAAoC,CAClD;IAAA,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CACjC,CAAC,GAAG,CACH,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAChB,SAAS,CAAC,CAAC,kBACV,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,2BAChE,gBAAgB,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAEzE;MAAA,CAAC,OAAO,CAAC,IAAI,CACd;KAAA,EAAE,GAAG,CAAC,CACN,CAAC,CACH;GAAA,EAAE,GAAG,CACL;GAAA,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,8BAA8B,CACrE;IAAA,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CACpB;KAAA,CAAC,KAAK,CACL,IAAI,CAAC,MAAM,CACX,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1C,WAAW,CAAC,sBAAsB,CAClC,SAAS,CAAC,6FAA6F,EAExG;KAAA,CAAC,MAAM,CACN,IAAI,CAAC,QAAQ,CACb,SAAS,CAAC,qHAAqH,CAE/H;;KACD,EAAE,MAAM,CACT;IAAA,EAAE,GAAG,CACN;GAAA,EAAE,IAAI,CACP;EAAA,EAAE,GAAG,CAAC,CACN,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,AAAD,EAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";// public/app.js
2
+ function _toConsumableArray(a){return _arrayWithoutHoles(a)||_iterableToArray(a)||_unsupportedIterableToArray(a)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _iterableToArray(a){if("undefined"!=typeof Symbol&&null!=a[Symbol.iterator]||null!=a["@@iterator"])return Array.from(a)}function _arrayWithoutHoles(a){if(Array.isArray(a))return _arrayLikeToArray(a)}function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_unsupportedIterableToArray(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(b,c){if(b){if("string"==typeof b)return _arrayLikeToArray(b,c);var a={}.toString.call(b).slice(8,-1);return"Object"===a&&b.constructor&&(a=b.constructor.name),"Map"===a||"Set"===a?Array.from(b):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?_arrayLikeToArray(b,c):void 0}}function _arrayLikeToArray(b,c){(null==c||c>b.length)&&(c=b.length);for(var d=0,f=Array(c);d<c;d++)f[d]=b[d];return f}function _iterableToArrayLimit(b,c){var d=null==b?null:"undefined"!=typeof Symbol&&b[Symbol.iterator]||b["@@iterator"];if(null!=d){var g,h,j,k,l=[],a=!0,m=!1;try{if(j=(d=d.call(b)).next,0===c){if(Object(d)!==d)return;a=!1}else for(;!(a=(g=j.call(d)).done)&&(l.push(g.value),l.length!==c);a=!0);}catch(a){m=!0,h=a}finally{try{if(!a&&null!=d["return"]&&(k=d["return"](),Object(k)!==k))return}finally{if(m)throw h}}return l}}function _arrayWithHoles(a){if(Array.isArray(a))return a}var _React=React,useState=_React.useState,useEffect=_React.useEffect;function ChatBot(){var a=Math.floor,b=useState([]),c=_slicedToArray(b,2),d=c[0],f=c[1],g=useState(""),h=_slicedToArray(g,2),i=h[0],j=h[1],k=JSON.parse(atob(ctx_base64));useEffect(function(){f(function(b){return[].concat(_toConsumableArray(b),[{text:"Hello! Let share a fact about cats. Ok?",sender:"bot",id:a(100*Math.random())}])}),f(function(b){return[].concat(_toConsumableArray(b),[{text:k.response.data.fact,sender:"bot",id:a(100*Math.random())}])})},[]);return/*#__PURE__*/React.createElement("div",{className:"bg-white rounded-lg shadow-md w-96 overflow-hidden"},/*#__PURE__*/React.createElement("h1",{className:"bg-blue-500 text-white text-center py-4 text-xl font-bold"},"Simple ",k.request.params.workflow),/*#__PURE__*/React.createElement("div",{className:"h-96 overflow-y-auto p-4 space-y-4"},d.map(function(a){return/*#__PURE__*/React.createElement("div",{key:a.id,className:"p-2 rounded-lg ".concat("user"===a.sender?"bg-blue-500 text-white ml-auto":"bg-gray-200 text-gray-800"," max-w-[80%] ").concat("user"===a.sender?"text-right":"text-left")},a.text)})),/*#__PURE__*/React.createElement("form",{onSubmit:function(b){b.preventDefault(),i.trim()&&(f([].concat(_toConsumableArray(d),[{text:i,sender:"user",id:a(100*Math.random())}])),j(""),setTimeout(function(){f(function(b){return[].concat(_toConsumableArray(b),[{text:"You said: ".concat(i),sender:"bot",id:a(100*Math.random())}])})},500))},className:"p-4 border-t border-gray-200"},/*#__PURE__*/React.createElement("div",{className:"flex"},/*#__PURE__*/React.createElement("input",{type:"text",value:i,onChange:function(a){return j(a.target.value)},placeholder:"Type your message...",className:"flex-grow px-3 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"}),/*#__PURE__*/React.createElement("button",{type:"submit",className:"bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"},"Send"))))}ReactDOM.render(/*#__PURE__*/React.createElement(ChatBot,null),document.getElementById("root"));
@@ -0,0 +1,61 @@
1
+ /**
2
+ * React Node - Function-First Implementation
3
+ *
4
+ * Renders React applications by loading a compiled React bundle and rendering it in an HTML template.
5
+ * Migrated from class-based to function-first pattern using defineNode.
6
+ */
7
+ import { z } from "zod";
8
+ declare const inputSchema: z.ZodObject<{
9
+ react_app: z.ZodString;
10
+ title: z.ZodDefault<z.ZodOptional<z.ZodString>>;
11
+ scripts: z.ZodDefault<z.ZodOptional<z.ZodString>>;
12
+ metas: z.ZodDefault<z.ZodOptional<z.ZodString>>;
13
+ index_html: z.ZodDefault<z.ZodOptional<z.ZodString>>;
14
+ styles: z.ZodDefault<z.ZodOptional<z.ZodString>>;
15
+ root_element: z.ZodDefault<z.ZodOptional<z.ZodString>>;
16
+ }, "strip", z.ZodTypeAny, {
17
+ react_app: string;
18
+ title: string;
19
+ scripts: string;
20
+ metas: string;
21
+ index_html: string;
22
+ styles: string;
23
+ root_element: string;
24
+ }, {
25
+ react_app: string;
26
+ title?: string | undefined;
27
+ scripts?: string | undefined;
28
+ metas?: string | undefined;
29
+ index_html?: string | undefined;
30
+ styles?: string | undefined;
31
+ root_element?: string | undefined;
32
+ }>;
33
+ declare const outputSchema: z.ZodString;
34
+ declare const _default: import("@blokjs/runner").FunctionNode<z.ZodObject<{
35
+ react_app: z.ZodString;
36
+ title: z.ZodDefault<z.ZodOptional<z.ZodString>>;
37
+ scripts: z.ZodDefault<z.ZodOptional<z.ZodString>>;
38
+ metas: z.ZodDefault<z.ZodOptional<z.ZodString>>;
39
+ index_html: z.ZodDefault<z.ZodOptional<z.ZodString>>;
40
+ styles: z.ZodDefault<z.ZodOptional<z.ZodString>>;
41
+ root_element: z.ZodDefault<z.ZodOptional<z.ZodString>>;
42
+ }, "strip", z.ZodTypeAny, {
43
+ react_app: string;
44
+ title: string;
45
+ scripts: string;
46
+ metas: string;
47
+ index_html: string;
48
+ styles: string;
49
+ root_element: string;
50
+ }, {
51
+ react_app: string;
52
+ title?: string | undefined;
53
+ scripts?: string | undefined;
54
+ metas?: string | undefined;
55
+ index_html?: string | undefined;
56
+ styles?: string | undefined;
57
+ root_element?: string | undefined;
58
+ }>, z.ZodString>;
59
+ export default _default;
60
+ export type InputType = z.infer<typeof inputSchema>;
61
+ export type OutputType = z.infer<typeof outputSchema>;
@@ -0,0 +1,23 @@
1
+ <!-- views/index.ejs -->
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <%-metas%>
8
+ <title><%=title%></title>
9
+ <%-styles%>
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <script type="text/javascript">
12
+ var ctx_base64 = '<%=ctx%>';
13
+ </script>
14
+ </head>
15
+ <body class="bg-gray-100 flex items-center justify-center min-h-screen">
16
+ <div id="<%=root_element%>"></div>
17
+ <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
18
+ <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
19
+ <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
20
+ <%-scripts%>
21
+ <%-react_app%>
22
+ </body>
23
+ </html>
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * React Node - Function-First Implementation
3
+ *
4
+ * Renders React applications by loading a compiled React bundle and rendering it in an HTML template.
5
+ * Migrated from class-based to function-first pattern using defineNode.
6
+ */
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ import { defineNode } from "@blokjs/runner";
10
+ import ejs from "ejs";
11
+ import { z } from "zod";
12
+ // Input schema using Zod (migrated from JSON Schema)
13
+ const inputSchema = z.object({
14
+ react_app: z.string({
15
+ description: "Path to the compiled React application bundle (e.g., './app/index.merged.min.js')",
16
+ }),
17
+ title: z.string().optional().default("React App"),
18
+ scripts: z.string().optional().default(""),
19
+ metas: z.string().optional().default(""),
20
+ index_html: z.string().optional().default("index.html"),
21
+ styles: z.string().optional().default(""),
22
+ root_element: z.string().optional().default("root"),
23
+ });
24
+ // Output is HTML string
25
+ const outputSchema = z.string();
26
+ // Helper: Resolve path relative to node directory
27
+ const rootDir = path.resolve(__dirname, ".");
28
+ function root(relPath) {
29
+ return path.resolve(rootDir, relPath);
30
+ }
31
+ export default defineNode({
32
+ name: "react",
33
+ description: "Renders React applications by loading a compiled bundle and rendering it in an HTML template",
34
+ contentType: "text/html",
35
+ input: inputSchema,
36
+ output: outputSchema,
37
+ async execute(ctx, inputs) {
38
+ // Resolve React app bundle path
39
+ const file_path = inputs.react_app || "./app/index.merged.min.js";
40
+ const react_script_template = '<script type="text/babel">REACT_SCRIPT</script>';
41
+ // Load React script from the node module location
42
+ const min_file = root(file_path);
43
+ let react_app = fs.readFileSync(min_file, "utf8");
44
+ react_app = react_script_template.replace("REACT_SCRIPT", `\n${react_app}\n`);
45
+ // Read index.html template from the node module location
46
+ const content = fs.readFileSync(root(inputs.index_html), "utf8");
47
+ const render = ejs.compile(content, { client: false });
48
+ // Clone context for template (removing sensitive data)
49
+ const ctxCloned = {
50
+ config: ctx.config,
51
+ inputs: inputs,
52
+ response: ctx.response,
53
+ request: {
54
+ body: ctx.request.body,
55
+ headers: ctx.request.headers,
56
+ url: ctx.request.url,
57
+ originalUrl: ctx.request.originalUrl,
58
+ query: ctx.request.query,
59
+ params: ctx.request.params,
60
+ cookies: ctx.request.cookies,
61
+ },
62
+ };
63
+ // Render HTML with EJS
64
+ const html = render({
65
+ title: inputs.title,
66
+ metas: inputs.metas,
67
+ styles: inputs.styles,
68
+ scripts: inputs.scripts,
69
+ root_element: inputs.root_element,
70
+ react_app,
71
+ ctx: btoa(JSON.stringify(ctxCloned)),
72
+ });
73
+ return html;
74
+ },
75
+ });
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export declare const inputSchema: {
2
+ $schema: string;
3
+ title: string;
4
+ type: string;
5
+ properties: {
6
+ title: {
7
+ type: string;
8
+ };
9
+ index_html: {
10
+ type: string;
11
+ };
12
+ scripts: {
13
+ type: string;
14
+ };
15
+ react_app: {
16
+ type: string;
17
+ };
18
+ styles: {
19
+ type: string;
20
+ };
21
+ root_element: {
22
+ type: string;
23
+ };
24
+ metas: {
25
+ type: string;
26
+ };
27
+ };
28
+ required: string[];
29
+ };
@@ -0,0 +1,30 @@
1
+ export const inputSchema = {
2
+ $schema: "http://json-schema.org/draft-07/schema#",
3
+ title: "Generated schema for Root",
4
+ type: "object",
5
+ properties: {
6
+ title: {
7
+ type: "string",
8
+ },
9
+ index_html: {
10
+ type: "string",
11
+ },
12
+ scripts: {
13
+ type: "string",
14
+ },
15
+ react_app: {
16
+ type: "string",
17
+ },
18
+ styles: {
19
+ type: "string",
20
+ },
21
+ root_element: {
22
+ type: "string",
23
+ },
24
+ metas: {
25
+ type: "string",
26
+ },
27
+ },
28
+ required: ["react_app"],
29
+ };
30
+ //# sourceMappingURL=inputSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputSchema.js","sourceRoot":"","sources":["../inputSchema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,OAAO,EAAE,yCAAyC;IAClD,KAAK,EAAE,2BAA2B;IAClC,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACX,KAAK,EAAE;YACN,IAAI,EAAE,QAAQ;SACd;QACD,UAAU,EAAE;YACX,IAAI,EAAE,QAAQ;SACd;QACD,OAAO,EAAE;YACR,IAAI,EAAE,QAAQ;SACd;QACD,SAAS,EAAE;YACV,IAAI,EAAE,QAAQ;SACd;QACD,MAAM,EAAE;YACP,IAAI,EAAE,QAAQ;SACd;QACD,YAAY,EAAE;YACb,IAAI,EAAE,QAAQ;SACd;QACD,KAAK,EAAE;YACN,IAAI,EAAE,QAAQ;SACd;KACD;IACD,QAAQ,EAAE,CAAC,WAAW,CAAC;CACvB,CAAC"}
package/index.html ADDED
@@ -0,0 +1,23 @@
1
+ <!-- views/index.ejs -->
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <%-metas%>
8
+ <title><%=title%></title>
9
+ <%-styles%>
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <script type="text/javascript">
12
+ var ctx_base64 = '<%=ctx%>';
13
+ </script>
14
+ </head>
15
+ <body class="bg-gray-100 flex items-center justify-center min-h-screen">
16
+ <div id="<%=root_element%>"></div>
17
+ <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
18
+ <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
19
+ <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
20
+ <%-scripts%>
21
+ <%-react_app%>
22
+ </body>
23
+ </html>
package/index.ts ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * React Node - Function-First Implementation
3
+ *
4
+ * Renders React applications by loading a compiled React bundle and rendering it in an HTML template.
5
+ * Migrated from class-based to function-first pattern using defineNode.
6
+ */
7
+
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { defineNode } from "@blokjs/runner";
11
+ import type { Context } from "@blokjs/shared";
12
+ import ejs from "ejs";
13
+ import { z } from "zod";
14
+
15
+ // Input schema using Zod (migrated from JSON Schema)
16
+ const inputSchema = z.object({
17
+ react_app: z.string({
18
+ description: "Path to the compiled React application bundle (e.g., './app/index.merged.min.js')",
19
+ }),
20
+ title: z.string().optional().default("React App"),
21
+ scripts: z.string().optional().default(""),
22
+ metas: z.string().optional().default(""),
23
+ index_html: z.string().optional().default("index.html"),
24
+ styles: z.string().optional().default(""),
25
+ root_element: z.string().optional().default("root"),
26
+ });
27
+
28
+ // Output is HTML string
29
+ const outputSchema = z.string();
30
+
31
+ // Helper: Resolve path relative to node directory
32
+ const rootDir = path.resolve(__dirname, ".");
33
+ function root(relPath: string): string {
34
+ return path.resolve(rootDir, relPath);
35
+ }
36
+
37
+ export default defineNode({
38
+ name: "react",
39
+ description: "Renders React applications by loading a compiled bundle and rendering it in an HTML template",
40
+ contentType: "text/html",
41
+
42
+ input: inputSchema,
43
+ output: outputSchema,
44
+
45
+ async execute(ctx: Context, inputs) {
46
+ // Resolve React app bundle path
47
+ const file_path = inputs.react_app || "./app/index.merged.min.js";
48
+ const react_script_template = '<script type="text/babel">REACT_SCRIPT</script>';
49
+
50
+ // Load React script from the node module location
51
+ const min_file = root(file_path);
52
+ let react_app = fs.readFileSync(min_file, "utf8");
53
+ react_app = react_script_template.replace("REACT_SCRIPT", `\n${react_app}\n`);
54
+
55
+ // Read index.html template from the node module location
56
+ const content = fs.readFileSync(root(inputs.index_html), "utf8");
57
+ const render = ejs.compile(content, { client: false });
58
+
59
+ // Clone context for template (removing sensitive data)
60
+ const ctxCloned = {
61
+ config: ctx.config,
62
+ inputs: inputs,
63
+ response: ctx.response,
64
+ request: {
65
+ body: ctx.request.body,
66
+ headers: ctx.request.headers,
67
+ url: ctx.request.url,
68
+ originalUrl: ctx.request.originalUrl,
69
+ query: ctx.request.query,
70
+ params: ctx.request.params,
71
+ cookies: ctx.request.cookies,
72
+ },
73
+ };
74
+
75
+ // Render HTML with EJS
76
+ const html = render({
77
+ title: inputs.title,
78
+ metas: inputs.metas,
79
+ styles: inputs.styles,
80
+ scripts: inputs.scripts,
81
+ root_element: inputs.root_element,
82
+ react_app,
83
+ ctx: btoa(JSON.stringify(ctxCloned)),
84
+ });
85
+
86
+ return html;
87
+ },
88
+ });
89
+
90
+ // Export types for backward compatibility
91
+ export type InputType = z.infer<typeof inputSchema>;
92
+ export type OutputType = z.infer<typeof outputSchema>;
@@ -0,0 +1,103 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { type INanoServiceResponse, NanoService, NanoServiceResponse } from "@blok/runner";
4
+ import { type Context, GlobalError } from "@blok/shared";
5
+ import ejs from "ejs";
6
+ import { inputSchema } from "./inputSchema";
7
+
8
+ type InputType = {
9
+ react_app: string;
10
+ title?: string;
11
+ scripts?: string;
12
+ metas?: string;
13
+ index_html?: string;
14
+ styles?: string;
15
+ root_element?: string;
16
+ };
17
+
18
+ const rootDir = path.resolve(__dirname, ".");
19
+
20
+ export default class React extends NanoService<InputType> {
21
+ constructor() {
22
+ super();
23
+
24
+ // Set the input "JSON Schema Format" here for automated validation
25
+ // Learn JSON Schema: https://json-schema.org/learn/getting-started-step-by-step
26
+ this.inputSchema = inputSchema;
27
+
28
+ // Set the output "JSON Schema Format" here for automated validation
29
+ // Learn JSON Schema: https://json-schema.org/learn/getting-started-step-by-step
30
+ this.outputSchema = {};
31
+
32
+ // Set html content type
33
+ this.contentType = "text/html";
34
+ }
35
+
36
+ /**
37
+ * Relative path to root
38
+ */
39
+ root(relPath: string): string {
40
+ return path.resolve(rootDir, relPath);
41
+ }
42
+
43
+ async handle(ctx: Context, inputs: InputType): Promise<INanoServiceResponse> {
44
+ // Create a new instance of the response
45
+ const response = new NanoServiceResponse();
46
+ let file_path = inputs.react_app;
47
+ if (file_path === undefined || file_path === "") file_path = "./app/index.merged.min.js";
48
+ const react_script_template = '<script type="text/babel">REACT_SCRIPT</script>';
49
+
50
+ const title = inputs.title || "React App";
51
+ const scripts = inputs.scripts || "";
52
+ const metas = inputs.metas || "";
53
+ const index_html = inputs.index_html || "index.html";
54
+ const styles = inputs.styles || "";
55
+ const root_element = inputs.root_element || "root";
56
+
57
+ try {
58
+ // Load React script from the current module location
59
+ const min_file = this.root(file_path);
60
+ let react_app = fs.readFileSync(min_file, "utf8");
61
+ react_app = react_script_template.replace("REACT_SCRIPT", `\n${react_app}\n`);
62
+
63
+ // Read index.html file from the current module location
64
+ const content = fs.readFileSync(this.root(index_html), "utf8");
65
+ const render = ejs.compile(content, { client: false });
66
+ const ctxCloned = {
67
+ config: ctx.config,
68
+ inputs: inputs,
69
+ response: ctx.response,
70
+ request: {
71
+ body: ctx.request.body,
72
+ headers: ctx.request.headers,
73
+ url: ctx.request.url,
74
+ originalUrl: ctx.request.originalUrl,
75
+ query: ctx.request.query,
76
+ params: ctx.request.params,
77
+ cookies: ctx.request.cookies,
78
+ },
79
+ };
80
+
81
+ const html = render({
82
+ title,
83
+ metas,
84
+ styles,
85
+ scripts,
86
+ root_element,
87
+ react_app,
88
+ ctx: btoa(JSON.stringify(ctxCloned)),
89
+ });
90
+
91
+ // Your code here
92
+ response.setSuccess(html); // Set the success
93
+ } catch (error: unknown) {
94
+ const nodeError: GlobalError = new GlobalError((error as Error).message);
95
+ nodeError.setCode(500);
96
+ nodeError.setStack((error as Error).stack);
97
+ nodeError.setName(this.name);
98
+ response.setError(nodeError); // Set the error
99
+ }
100
+
101
+ return response;
102
+ }
103
+ }
package/inputSchema.ts ADDED
@@ -0,0 +1,29 @@
1
+ export const inputSchema = {
2
+ $schema: "http://json-schema.org/draft-07/schema#",
3
+ title: "Generated schema for Root",
4
+ type: "object",
5
+ properties: {
6
+ title: {
7
+ type: "string",
8
+ },
9
+ index_html: {
10
+ type: "string",
11
+ },
12
+ scripts: {
13
+ type: "string",
14
+ },
15
+ react_app: {
16
+ type: "string",
17
+ },
18
+ styles: {
19
+ type: "string",
20
+ },
21
+ root_element: {
22
+ type: "string",
23
+ },
24
+ metas: {
25
+ type: "string",
26
+ },
27
+ },
28
+ required: ["react_app"],
29
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@blokjs/react",
3
+ "version": "0.2.0",
4
+ "description": "",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18.0.0"
8
+ },
9
+ "author": "Deskree Technologies Inc.",
10
+ "license": "Apache-2.0",
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "scripts": {
14
+ "test:dev": "vitest",
15
+ "test": "vitest run",
16
+ "build:ts": "rm -rf dist && tsc",
17
+ "build:dev": "tsc --watch",
18
+ "build:babel": "rm -rf dist/app/index.merged.min.js && babel dist/app --out-file dist/app/index.merged.min.js",
19
+ "build": "npm run build:ts && npm run build:babel && cp index.html dist/index.html"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.15.21",
23
+ "@types/ejs": "^3.1.5",
24
+ "typescript": "^5.8.3",
25
+ "vitest": "^4.0.18",
26
+ "@babel/cli": "^7.28.6",
27
+ "@babel/core": "^7.28.6",
28
+ "@babel/preset-env": "^7.28.6",
29
+ "@babel/preset-react": "^7.28.5",
30
+ "babel-minify": "^0.5.2"
31
+ },
32
+ "dependencies": {
33
+ "@blokjs/shared": "workspace:*",
34
+ "@blokjs/runner": "workspace:*",
35
+ "@blokjs/helper": "workspace:*",
36
+ "ejs": "^3.1.10",
37
+ "zod": "^3.24.2"
38
+ },
39
+ "private": false,
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }
package/test/helper.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { ParamsDictionary } from "@blokjs/runner";
2
+ import type { Context } from "@blokjs/shared";
3
+
4
+ export default function ctx(): Context {
5
+ const ctx: Context = {
6
+ response: {
7
+ data: {
8
+ title: "Chat bot",
9
+ },
10
+ error: null,
11
+ },
12
+ request: {
13
+ body: {},
14
+ },
15
+ config: {},
16
+ id: "",
17
+ error: {
18
+ message: "",
19
+ code: undefined,
20
+ json: undefined,
21
+ stack: undefined,
22
+ name: undefined,
23
+ },
24
+ logger: {
25
+ log: (message: string): void => {
26
+ throw new Error("Function not implemented.");
27
+ },
28
+ getLogs: (): string[] => {
29
+ throw new Error("Function not implemented.");
30
+ },
31
+ getLogsAsText: (): string => {
32
+ throw new Error("Function not implemented.");
33
+ },
34
+ getLogsAsBase64: (): string => {
35
+ throw new Error("Function not implemented.");
36
+ },
37
+ logLevel: (level: string, message: string): void => {
38
+ throw new Error("Function not implemented.");
39
+ },
40
+ error: (message: string, stack: string): void => {
41
+ throw new Error("Function not implemented.");
42
+ },
43
+ },
44
+ eventLogger: undefined,
45
+ _PRIVATE_: undefined,
46
+ };
47
+
48
+ ctx.config = {
49
+ react: {
50
+ inputs: {
51
+ react_app: "./dist/app/index.merged.min.js",
52
+ },
53
+ },
54
+ } as unknown as ParamsDictionary;
55
+
56
+ return ctx;
57
+ }
@@ -0,0 +1,27 @@
1
+ <!-- views/index.ejs -->
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+
8
+ <title>React App</title>
9
+
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <script type="text/javascript">
12
+ var ctx_base64 = 'eyJjb25maWciOnsicmVhY3QiOnsiaW5wdXRzIjp7InJlYWN0X2FwcCI6Ii4vZGlzdC9hcHAvaW5kZXgubWVyZ2VkLm1pbi5qcyJ9fX0sImlucHV0cyI6eyJyZWFjdF9hcHAiOiIuL2Rpc3QvYXBwL2luZGV4Lm1lcmdlZC5taW4uanMiLCJ0aXRsZSI6IlJlYWN0IEFwcCIsInNjcmlwdHMiOiIiLCJtZXRhcyI6IiIsImluZGV4X2h0bWwiOiJpbmRleC5odG1sIiwic3R5bGVzIjoiIiwicm9vdF9lbGVtZW50Ijoicm9vdCJ9LCJyZXNwb25zZSI6eyJkYXRhIjp7InRpdGxlIjoiQ2hhdCBib3QifSwiZXJyb3IiOm51bGx9LCJyZXF1ZXN0Ijp7ImJvZHkiOnt9fX0=';
13
+ </script>
14
+ </head>
15
+ <body class="bg-gray-100 flex items-center justify-center min-h-screen">
16
+ <div id="root"></div>
17
+ <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
18
+ <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
19
+ <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
20
+
21
+ <script type="text/babel">
22
+ "use strict";// public/app.js
23
+ function _toConsumableArray(a){return _arrayWithoutHoles(a)||_iterableToArray(a)||_unsupportedIterableToArray(a)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _iterableToArray(a){if("undefined"!=typeof Symbol&&null!=a[Symbol.iterator]||null!=a["@@iterator"])return Array.from(a)}function _arrayWithoutHoles(a){if(Array.isArray(a))return _arrayLikeToArray(a)}function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_unsupportedIterableToArray(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(b,c){if(b){if("string"==typeof b)return _arrayLikeToArray(b,c);var a={}.toString.call(b).slice(8,-1);return"Object"===a&&b.constructor&&(a=b.constructor.name),"Map"===a||"Set"===a?Array.from(b):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?_arrayLikeToArray(b,c):void 0}}function _arrayLikeToArray(b,c){(null==c||c>b.length)&&(c=b.length);for(var d=0,f=Array(c);d<c;d++)f[d]=b[d];return f}function _iterableToArrayLimit(b,c){var d=null==b?null:"undefined"!=typeof Symbol&&b[Symbol.iterator]||b["@@iterator"];if(null!=d){var g,h,j,k,l=[],a=!0,m=!1;try{if(j=(d=d.call(b)).next,0===c){if(Object(d)!==d)return;a=!1}else for(;!(a=(g=j.call(d)).done)&&(l.push(g.value),l.length!==c);a=!0);}catch(a){m=!0,h=a}finally{try{if(!a&&null!=d["return"]&&(k=d["return"](),Object(k)!==k))return}finally{if(m)throw h}}return l}}function _arrayWithHoles(a){if(Array.isArray(a))return a}var _React=React,useState=_React.useState,useEffect=_React.useEffect;function ChatBot(){var a=Math.floor,b=useState([]),c=_slicedToArray(b,2),d=c[0],f=c[1],g=useState(""),h=_slicedToArray(g,2),i=h[0],j=h[1],k=JSON.parse(atob(ctx_base64));useEffect(function(){f(function(b){return[].concat(_toConsumableArray(b),[{text:"Hello! Let share a fact about cats. Ok?",sender:"bot",id:a(100*Math.random())}])}),f(function(b){return[].concat(_toConsumableArray(b),[{text:k.response.data.fact,sender:"bot",id:a(100*Math.random())}])})},[]);return/*#__PURE__*/React.createElement("div",{className:"bg-white rounded-lg shadow-md w-96 overflow-hidden"},/*#__PURE__*/React.createElement("h1",{className:"bg-blue-500 text-white text-center py-4 text-xl font-bold"},"Simple ",k.request.params.workflow),/*#__PURE__*/React.createElement("div",{className:"h-96 overflow-y-auto p-4 space-y-4"},d.map(function(a){return/*#__PURE__*/React.createElement("div",{key:a.id,className:"p-2 rounded-lg ".concat("user"===a.sender?"bg-blue-500 text-white ml-auto":"bg-gray-200 text-gray-800"," max-w-[80%] ").concat("user"===a.sender?"text-right":"text-left")},a.text)})),/*#__PURE__*/React.createElement("form",{onSubmit:function(b){b.preventDefault(),i.trim()&&(f([].concat(_toConsumableArray(d),[{text:i,sender:"user",id:a(100*Math.random())}])),j(""),setTimeout(function(){f(function(b){return[].concat(_toConsumableArray(b),[{text:"You said: ".concat(i),sender:"bot",id:a(100*Math.random())}])})},500))},className:"p-4 border-t border-gray-200"},/*#__PURE__*/React.createElement("div",{className:"flex"},/*#__PURE__*/React.createElement("input",{type:"text",value:i,onChange:function(a){return j(a.target.value)},placeholder:"Type your message...",className:"flex-grow px-3 py-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"}),/*#__PURE__*/React.createElement("button",{type:"submit",className:"bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"},"Send"))))}ReactDOM.render(/*#__PURE__*/React.createElement(ChatBot,null),document.getElementById("root"));
24
+
25
+ </script>
26
+ </body>
27
+ </html>
@@ -0,0 +1,32 @@
1
+ /**
2
+ * React Node Tests - Updated for Function-First Implementation
3
+ *
4
+ * Tests migrated from class-based to function-first pattern.
5
+ * All existing behavior is preserved.
6
+ */
7
+
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import type { IBlokResponse } from "@blokjs/runner";
11
+ import { beforeAll, expect, test } from "vitest";
12
+ import ReactNode from "../index";
13
+ import ctx from "./helper";
14
+
15
+ let rootDir: string;
16
+
17
+ beforeAll(() => {
18
+ rootDir = path.resolve(__dirname, ".");
19
+ });
20
+
21
+ // Validate React rendering from Node
22
+ test("Render index.html page", async () => {
23
+ const context = ctx();
24
+ const inputs = { react_app: "./dist/app/index.merged.min.js" };
25
+
26
+ const response = (await ReactNode.handle(context, inputs)) as IBlokResponse;
27
+ const mockup_file = path.resolve(rootDir, "index.mockup.html");
28
+ const message: string = fs.readFileSync(mockup_file, "utf8");
29
+
30
+ expect(response.success).toEqual(true);
31
+ expect(response.data).toEqual(message);
32
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2022",
4
+ "allowJs": true,
5
+ "module": "es2022",
6
+ "moduleResolution": "bundler",
7
+ "declaration": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "strict": true,
13
+ "noUnusedLocals": true,
14
+ "noImplicitReturns": true,
15
+ "skipLibCheck": true
16
+ },
17
+ "include": ["./*", "app/**/*", "index.html"],
18
+ "exclude": ["node_modules", "dist"],
19
+ "compileOnSave": true
20
+ }