hotstaq 0.5.24

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 (299) hide show
  1. package/.eslintignore +17 -0
  2. package/.eslintrc.js +11 -0
  3. package/.vscode/launch.json +199 -0
  4. package/.vscode/settings.json +2 -0
  5. package/.vscode/tasks.json +78 -0
  6. package/CONTRIBUTING.md +157 -0
  7. package/README.md +126 -0
  8. package/bin/hotstaq +3 -0
  9. package/bin/hotstaq.cmd +1 -0
  10. package/build/src/Hot.d.ts +167 -0
  11. package/build/src/Hot.d.ts.map +1 -0
  12. package/build/src/Hot.js +365 -0
  13. package/build/src/Hot.js.map +1 -0
  14. package/build/src/HotAPI.d.ts +130 -0
  15. package/build/src/HotAPI.d.ts.map +1 -0
  16. package/build/src/HotAPI.js +353 -0
  17. package/build/src/HotAPI.js.map +1 -0
  18. package/build/src/HotAgentAPI.d.ts +22 -0
  19. package/build/src/HotAgentAPI.d.ts.map +1 -0
  20. package/build/src/HotAgentAPI.js +85 -0
  21. package/build/src/HotAgentAPI.js.map +1 -0
  22. package/build/src/HotAgentRoute.d.ts +17 -0
  23. package/build/src/HotAgentRoute.d.ts.map +1 -0
  24. package/build/src/HotAgentRoute.js +109 -0
  25. package/build/src/HotAgentRoute.js.map +1 -0
  26. package/build/src/HotBuilder.d.ts +52 -0
  27. package/build/src/HotBuilder.d.ts.map +1 -0
  28. package/build/src/HotBuilder.js +239 -0
  29. package/build/src/HotBuilder.js.map +1 -0
  30. package/build/src/HotClient.d.ts +31 -0
  31. package/build/src/HotClient.d.ts.map +1 -0
  32. package/build/src/HotClient.js +19 -0
  33. package/build/src/HotClient.js.map +1 -0
  34. package/build/src/HotComponent.d.ts +118 -0
  35. package/build/src/HotComponent.d.ts.map +1 -0
  36. package/build/src/HotComponent.js +89 -0
  37. package/build/src/HotComponent.js.map +1 -0
  38. package/build/src/HotCreator.d.ts +92 -0
  39. package/build/src/HotCreator.d.ts.map +1 -0
  40. package/build/src/HotCreator.js +465 -0
  41. package/build/src/HotCreator.js.map +1 -0
  42. package/build/src/HotDB.d.ts +69 -0
  43. package/build/src/HotDB.d.ts.map +1 -0
  44. package/build/src/HotDB.js +29 -0
  45. package/build/src/HotDB.js.map +1 -0
  46. package/build/src/HotDBConnectionInterface.d.ts +40 -0
  47. package/build/src/HotDBConnectionInterface.d.ts.map +1 -0
  48. package/build/src/HotDBConnectionInterface.js +3 -0
  49. package/build/src/HotDBConnectionInterface.js.map +1 -0
  50. package/build/src/HotFile.d.ts +134 -0
  51. package/build/src/HotFile.d.ts.map +1 -0
  52. package/build/src/HotFile.js +454 -0
  53. package/build/src/HotFile.js.map +1 -0
  54. package/build/src/HotHTTPServer.d.ts +162 -0
  55. package/build/src/HotHTTPServer.d.ts.map +1 -0
  56. package/build/src/HotHTTPServer.js +863 -0
  57. package/build/src/HotHTTPServer.js.map +1 -0
  58. package/build/src/HotIO.d.ts +39 -0
  59. package/build/src/HotIO.d.ts.map +1 -0
  60. package/build/src/HotIO.js +195 -0
  61. package/build/src/HotIO.js.map +1 -0
  62. package/build/src/HotLog.d.ts +60 -0
  63. package/build/src/HotLog.d.ts.map +1 -0
  64. package/build/src/HotLog.js +126 -0
  65. package/build/src/HotLog.js.map +1 -0
  66. package/build/src/HotPage.d.ts +125 -0
  67. package/build/src/HotPage.d.ts.map +1 -0
  68. package/build/src/HotPage.js +178 -0
  69. package/build/src/HotPage.js.map +1 -0
  70. package/build/src/HotRoute.d.ts +82 -0
  71. package/build/src/HotRoute.d.ts.map +1 -0
  72. package/build/src/HotRoute.js +83 -0
  73. package/build/src/HotRoute.js.map +1 -0
  74. package/build/src/HotRouteMethod.d.ts +129 -0
  75. package/build/src/HotRouteMethod.d.ts.map +1 -0
  76. package/build/src/HotRouteMethod.js +84 -0
  77. package/build/src/HotRouteMethod.js.map +1 -0
  78. package/build/src/HotServer.d.ts +152 -0
  79. package/build/src/HotServer.d.ts.map +1 -0
  80. package/build/src/HotServer.js +109 -0
  81. package/build/src/HotServer.js.map +1 -0
  82. package/build/src/HotSetAsWeb.d.ts +2 -0
  83. package/build/src/HotSetAsWeb.d.ts.map +1 -0
  84. package/build/src/HotSetAsWeb.js +5 -0
  85. package/build/src/HotSetAsWeb.js.map +1 -0
  86. package/build/src/HotStaq.d.ts +601 -0
  87. package/build/src/HotStaq.d.ts.map +1 -0
  88. package/build/src/HotStaq.js +1258 -0
  89. package/build/src/HotStaq.js.map +1 -0
  90. package/build/src/HotStaqWeb.d.ts +18 -0
  91. package/build/src/HotStaqWeb.d.ts.map +1 -0
  92. package/build/src/HotStaqWeb.js +44 -0
  93. package/build/src/HotStaqWeb.js.map +1 -0
  94. package/build/src/HotTestDriver.d.ts +63 -0
  95. package/build/src/HotTestDriver.d.ts.map +1 -0
  96. package/build/src/HotTestDriver.js +187 -0
  97. package/build/src/HotTestDriver.js.map +1 -0
  98. package/build/src/HotTestElement.d.ts +71 -0
  99. package/build/src/HotTestElement.d.ts.map +1 -0
  100. package/build/src/HotTestElement.js +37 -0
  101. package/build/src/HotTestElement.js.map +1 -0
  102. package/build/src/HotTestMap.d.ts +83 -0
  103. package/build/src/HotTestMap.d.ts.map +1 -0
  104. package/build/src/HotTestMap.js +58 -0
  105. package/build/src/HotTestMap.js.map +1 -0
  106. package/build/src/HotTestSeleniumDriver.d.ts +82 -0
  107. package/build/src/HotTestSeleniumDriver.d.ts.map +1 -0
  108. package/build/src/HotTestSeleniumDriver.js +398 -0
  109. package/build/src/HotTestSeleniumDriver.js.map +1 -0
  110. package/build/src/HotTester.d.ts +188 -0
  111. package/build/src/HotTester.d.ts.map +1 -0
  112. package/build/src/HotTester.js +623 -0
  113. package/build/src/HotTester.js.map +1 -0
  114. package/build/src/HotTesterAPI.d.ts +15 -0
  115. package/build/src/HotTesterAPI.d.ts.map +1 -0
  116. package/build/src/HotTesterAPI.js +161 -0
  117. package/build/src/HotTesterAPI.js.map +1 -0
  118. package/build/src/HotTesterMocha.d.ts +50 -0
  119. package/build/src/HotTesterMocha.d.ts.map +1 -0
  120. package/build/src/HotTesterMocha.js +205 -0
  121. package/build/src/HotTesterMocha.js.map +1 -0
  122. package/build/src/HotTesterMochaSelenium.d.ts +70 -0
  123. package/build/src/HotTesterMochaSelenium.d.ts.map +1 -0
  124. package/build/src/HotTesterMochaSelenium.js +257 -0
  125. package/build/src/HotTesterMochaSelenium.js.map +1 -0
  126. package/build/src/HotTesterServer.d.ts +114 -0
  127. package/build/src/HotTesterServer.d.ts.map +1 -0
  128. package/build/src/HotTesterServer.js +575 -0
  129. package/build/src/HotTesterServer.js.map +1 -0
  130. package/build/src/api.d.ts +33 -0
  131. package/build/src/api.d.ts.map +1 -0
  132. package/build/src/api.js +78 -0
  133. package/build/src/api.js.map +1 -0
  134. package/build/src/cli.d.ts +2 -0
  135. package/build/src/cli.d.ts.map +1 -0
  136. package/build/src/cli.js +935 -0
  137. package/build/src/cli.js.map +1 -0
  138. package/build/src/schemas/HotDBInflux.d.ts +63 -0
  139. package/build/src/schemas/HotDBInflux.d.ts.map +1 -0
  140. package/build/src/schemas/HotDBInflux.js +239 -0
  141. package/build/src/schemas/HotDBInflux.js.map +1 -0
  142. package/build/src/schemas/HotDBMigration.d.ts +19 -0
  143. package/build/src/schemas/HotDBMigration.d.ts.map +1 -0
  144. package/build/src/schemas/HotDBMigration.js +15 -0
  145. package/build/src/schemas/HotDBMigration.js.map +1 -0
  146. package/build/src/schemas/HotDBMySQL.d.ts +65 -0
  147. package/build/src/schemas/HotDBMySQL.d.ts.map +1 -0
  148. package/build/src/schemas/HotDBMySQL.js +387 -0
  149. package/build/src/schemas/HotDBMySQL.js.map +1 -0
  150. package/build/src/schemas/HotDBSchema.d.ts +15 -0
  151. package/build/src/schemas/HotDBSchema.d.ts.map +1 -0
  152. package/build/src/schemas/HotDBSchema.js +19 -0
  153. package/build/src/schemas/HotDBSchema.js.map +1 -0
  154. package/build/src/schemas/influx/InfluxSchema.d.ts +14 -0
  155. package/build/src/schemas/influx/InfluxSchema.d.ts.map +1 -0
  156. package/build/src/schemas/influx/InfluxSchema.js +33 -0
  157. package/build/src/schemas/influx/InfluxSchema.js.map +1 -0
  158. package/build/src/schemas/mysql/MySQLSchema.d.ts +39 -0
  159. package/build/src/schemas/mysql/MySQLSchema.d.ts.map +1 -0
  160. package/build/src/schemas/mysql/MySQLSchema.js +151 -0
  161. package/build/src/schemas/mysql/MySQLSchema.js.map +1 -0
  162. package/build/src/schemas/mysql/MySQLSchemaField.d.ts +168 -0
  163. package/build/src/schemas/mysql/MySQLSchemaField.d.ts.map +1 -0
  164. package/build/src/schemas/mysql/MySQLSchemaField.js +260 -0
  165. package/build/src/schemas/mysql/MySQLSchemaField.js.map +1 -0
  166. package/build/src/schemas/mysql/MySQLSchemaTable.d.ts +49 -0
  167. package/build/src/schemas/mysql/MySQLSchemaTable.d.ts.map +1 -0
  168. package/build/src/schemas/mysql/MySQLSchemaTable.js +310 -0
  169. package/build/src/schemas/mysql/MySQLSchemaTable.js.map +1 -0
  170. package/build-web/HotStaq.js +2 -0
  171. package/build-web/HotStaq.min.js +125 -0
  172. package/builder/docker/Dockerfile.linux.gen +42 -0
  173. package/builder/docker/README.md +36 -0
  174. package/builder/docker/app/start.sh +8 -0
  175. package/builder/docker/dockerignore +3 -0
  176. package/builder/docker/scripts/build.bat +11 -0
  177. package/builder/docker/scripts/build.sh +11 -0
  178. package/builder/docker/scripts/start-app.bat +7 -0
  179. package/builder/docker/scripts/start-app.sh +7 -0
  180. package/builder/docker/scripts/stop-app.bat +5 -0
  181. package/builder/docker/scripts/stop-app.sh +5 -0
  182. package/builder/docker-compose/docker-compose.gen.yaml +41 -0
  183. package/builder/docker-compose/env-skeleton +4 -0
  184. package/creator/project/.vscode/launch.json +59 -0
  185. package/creator/project/README.md +20 -0
  186. package/creator/project/gitignore +118 -0
  187. package/creator/project/npmignore +118 -0
  188. package/creator/public/api-test.hott +28 -0
  189. package/creator/public/index.hott +12 -0
  190. package/creator/ts/src/AppAPI.ts +30 -0
  191. package/creator/ts/src/HelloWorld.ts +39 -0
  192. package/creator/ts/src/WebExport.ts +7 -0
  193. package/creator/ts/tsconfig-web.json +73 -0
  194. package/creator/ts/tsconfig.json +73 -0
  195. package/creator/ts/webpack-api.config.js +57 -0
  196. package/dbstart.sh +19 -0
  197. package/dbstop.sh +4 -0
  198. package/docs/.nojekyll +1 -0
  199. package/docs/README.md +128 -0
  200. package/docs/classes/Hot.md +477 -0
  201. package/docs/classes/HotAPI.md +369 -0
  202. package/docs/classes/HotClient.md +95 -0
  203. package/docs/classes/HotComponent.md +279 -0
  204. package/docs/classes/HotDB.md +247 -0
  205. package/docs/classes/HotDBInflux.md +404 -0
  206. package/docs/classes/HotDBMigration.md +80 -0
  207. package/docs/classes/HotDBMySQL.md +310 -0
  208. package/docs/classes/HotDBSchema.md +51 -0
  209. package/docs/classes/HotFile.md +353 -0
  210. package/docs/classes/HotHTTPServer.md +700 -0
  211. package/docs/classes/HotLog.md +162 -0
  212. package/docs/classes/HotPage.md +357 -0
  213. package/docs/classes/HotRoute.md +312 -0
  214. package/docs/classes/HotRouteMethod.md +271 -0
  215. package/docs/classes/HotServer.md +311 -0
  216. package/docs/classes/HotStaq.md +1153 -0
  217. package/docs/classes/HotTestDestination.md +58 -0
  218. package/docs/classes/HotTestDriver.md +332 -0
  219. package/docs/classes/HotTestElement.md +88 -0
  220. package/docs/classes/HotTestElementOptions.md +71 -0
  221. package/docs/classes/HotTestMap.md +92 -0
  222. package/docs/classes/HotTestSeleniumDriver.md +529 -0
  223. package/docs/classes/HotTester.md +653 -0
  224. package/docs/classes/HotTesterAPI.md +493 -0
  225. package/docs/classes/HotTesterMocha.md +843 -0
  226. package/docs/classes/HotTesterMochaSelenium.md +896 -0
  227. package/docs/classes/HotTesterServer.md +633 -0
  228. package/docs/classes/InfluxSchema.md +74 -0
  229. package/docs/classes/MySQLSchema.md +199 -0
  230. package/docs/classes/MySQLSchemaField.md +330 -0
  231. package/docs/classes/MySQLSchemaTable.md +176 -0
  232. package/docs/enums/ConnectionStatus.md +43 -0
  233. package/docs/enums/DeveloperMode.md +38 -0
  234. package/docs/enums/EventExecutionType.md +43 -0
  235. package/docs/enums/HTTPMethod.md +32 -0
  236. package/docs/enums/HotDBGenerationType.md +30 -0
  237. package/docs/enums/HotLogLevel.md +88 -0
  238. package/docs/interfaces/HotDBConnectionInterface.md +116 -0
  239. package/docs/interfaces/HotDestination.md +62 -0
  240. package/docs/interfaces/HotSite.md +187 -0
  241. package/docs/interfaces/HotSiteMapPath.md +37 -0
  242. package/docs/interfaces/HotSiteRoute.md +79 -0
  243. package/docs/interfaces/HotStartOptions.md +115 -0
  244. package/docs/interfaces/HotTestPage.md +44 -0
  245. package/docs/interfaces/HotTestStop.md +62 -0
  246. package/docs/interfaces/IHotComponent.md +135 -0
  247. package/docs/interfaces/IHotStaq.md +118 -0
  248. package/docs/interfaces/IHotTestElement.md +54 -0
  249. package/docs/interfaces/IHotTestElementOptions.md +43 -0
  250. package/docs/interfaces/MySQLResults.md +43 -0
  251. package/docs/interfaces/MySQLSchemaFieldResult.md +75 -0
  252. package/docs/modules.md +162 -0
  253. package/package.json +65 -0
  254. package/selenium-start.sh +7 -0
  255. package/selenium-stop.sh +3 -0
  256. package/src/Hot.ts +319 -0
  257. package/src/HotAPI.ts +378 -0
  258. package/src/HotAgentAPI.ts +43 -0
  259. package/src/HotAgentRoute.ts +44 -0
  260. package/src/HotBuilder.ts +221 -0
  261. package/src/HotClient.ts +40 -0
  262. package/src/HotComponent.ts +158 -0
  263. package/src/HotCreator.ts +482 -0
  264. package/src/HotDB.ts +79 -0
  265. package/src/HotDBConnectionInterface.ts +40 -0
  266. package/src/HotFile.ts +617 -0
  267. package/src/HotHTTPServer.ts +954 -0
  268. package/src/HotIO.ts +128 -0
  269. package/src/HotLog.ts +158 -0
  270. package/src/HotPage.ts +206 -0
  271. package/src/HotRoute.ts +137 -0
  272. package/src/HotRouteMethod.ts +216 -0
  273. package/src/HotServer.ts +211 -0
  274. package/src/HotSetAsWeb.ts +3 -0
  275. package/src/HotStaq.ts +1877 -0
  276. package/src/HotStaqWeb.ts +59 -0
  277. package/src/HotTestDriver.ts +172 -0
  278. package/src/HotTestElement.ts +97 -0
  279. package/src/HotTestMap.ts +130 -0
  280. package/src/HotTestSeleniumDriver.ts +374 -0
  281. package/src/HotTester.ts +697 -0
  282. package/src/HotTesterAPI.ts +126 -0
  283. package/src/HotTesterMocha.ts +133 -0
  284. package/src/HotTesterMochaSelenium.ts +189 -0
  285. package/src/HotTesterServer.ts +551 -0
  286. package/src/api.ts +102 -0
  287. package/src/cli.ts +1147 -0
  288. package/src/schemas/HotDBInflux.ts +211 -0
  289. package/src/schemas/HotDBMigration.ts +24 -0
  290. package/src/schemas/HotDBMySQL.ts +312 -0
  291. package/src/schemas/HotDBSchema.ts +21 -0
  292. package/src/schemas/influx/InfluxSchema.ts +19 -0
  293. package/src/schemas/mysql/MySQLSchema.ts +90 -0
  294. package/src/schemas/mysql/MySQLSchemaField.ts +408 -0
  295. package/src/schemas/mysql/MySQLSchemaTable.ts +353 -0
  296. package/tsconfig-web.json +74 -0
  297. package/tsconfig.json +73 -0
  298. package/webpack.config.js +51 -0
  299. package/webpack.config.tests.js +56 -0
package/src/HotStaq.ts ADDED
@@ -0,0 +1,1877 @@
1
+ import * as fs from "fs";
2
+ import * as ppath from "path";
3
+
4
+ import fetch from "cross-fetch";
5
+ import validateModuleName from "validate-npm-package-name";
6
+
7
+ import { HotPage } from "./HotPage";
8
+ import { HotFile } from "./HotFile";
9
+
10
+ import { HotComponent } from "./HotComponent";
11
+ import { HotLog, HotLogLevel } from "./HotLog";
12
+ import { HotAPI } from "./HotAPI";
13
+ import { HotServer } from "./HotServer";
14
+ import { DeveloperMode } from "./Hot";
15
+ import { HotClient } from "./HotClient";
16
+
17
+ import { HotTester } from "./HotTester";
18
+ import { HotTesterAPI } from "./HotTesterAPI";
19
+ import { HotTestDriver } from "./HotTestDriver";
20
+ import { HotTestDestination, HotTestMap } from "./HotTestMap";
21
+
22
+ var HotTesterMocha: any = null;
23
+ var HotTesterMochaSelenium: any = null;
24
+ var HotTestSeleniumDriver: any = null;
25
+
26
+ /**
27
+ * A map path for testing.
28
+ */
29
+ export interface HotSiteMapPath
30
+ {
31
+ /**
32
+ * If set to true, this will start automatically when tests start.
33
+ * The default is true.
34
+ */
35
+ autoStart?: boolean;
36
+ /**
37
+ * The path to the
38
+ */
39
+ path?: string;
40
+ }
41
+
42
+ /**
43
+ * A route used in a HotSite.
44
+ */
45
+ export interface HotSiteRoute
46
+ {
47
+ /**
48
+ * The name of the route. Will appear in the title.
49
+ */
50
+ name: string;
51
+ /**
52
+ * The url to the file to load.
53
+ */
54
+ url: string;
55
+ /**
56
+ * The name of the API to interface with.
57
+ */
58
+ api?: string;
59
+ /**
60
+ * The order in which destinations are supposed to execute. This is
61
+ * ignored if the destinations are an array.
62
+ */
63
+ destinationOrder?: string[];
64
+ /**
65
+ * The HotTesterMap to use. This can be the name of an
66
+ * existing one attached to the selected tester, or
67
+ * can be an array of destinations that will be used to
68
+ * create a new map.
69
+ */
70
+ map?: string | string[] | { [name: string]: string | HotSiteMapPath; } | HotSiteMapPath[];
71
+ }
72
+
73
+ /**
74
+ * A HotSite to load. This SHOULD NOT contain any private secret keys, passwords,
75
+ * or database connection information related to the server. As such, future
76
+ * versions of the HotSite interface should not contain any database related
77
+ * connection info.
78
+ */
79
+ export interface HotSite
80
+ {
81
+ /**
82
+ * The name of this HotSite.
83
+ */
84
+ name: string;
85
+ /**
86
+ * The path to the current HotSite. This is filled in during parsing.
87
+ */
88
+ hotsitePath?: string;
89
+ /**
90
+ * Additional web server configuration.
91
+ */
92
+ server?: {
93
+ /**
94
+ * The default name for a served Hott file.
95
+ */
96
+ name?: string;
97
+ /**
98
+ * Serve hott files when requested.
99
+ */
100
+ serveHottFiles?: boolean;
101
+ /**
102
+ * The name of the API to interface with across all pages.
103
+ */
104
+ globalApi?: string;
105
+ /**
106
+ * The base url for the server.
107
+ */
108
+ url?: string;
109
+ /**
110
+ * The JavaScript source path.
111
+ */
112
+ jsSrcPath?: string;
113
+ /**
114
+ * The ports to use.
115
+ */
116
+ ports?: {
117
+ /**
118
+ * The HTTP port to serve on.
119
+ */
120
+ http?: number;
121
+ /**
122
+ * The HTTPS port to serve on.
123
+ */
124
+ https?: number;
125
+ /**
126
+ * If set to true, this will redirect from HTTP to HTTPS.
127
+ */
128
+ redirectHTTPtoHTTPS?: boolean;
129
+ };
130
+ /**
131
+ * The list of directory to serve to the client from the server.
132
+ */
133
+ serveDirectories?: {
134
+ /**
135
+ * The web route to take.
136
+ */
137
+ route: string;
138
+ /**
139
+ * The local filesystem path to serve pages from.
140
+ */
141
+ localPath: string;
142
+ }[];
143
+ };
144
+ /**
145
+ * Testing related functionality.
146
+ */
147
+ testing?: {
148
+ web?: {
149
+ /**
150
+ * The tester class to use. EX: HotTesterMochaSelenium
151
+ */
152
+ tester?: string;
153
+ /**
154
+ * The name of the tester to use.
155
+ */
156
+ testerName?: string;
157
+ /**
158
+ * If set to true, this will create a new tester.
159
+ * Default Value: true
160
+ */
161
+ createNewTester?: boolean;
162
+ /**
163
+ * The url that connects to the tester api server.
164
+ */
165
+ testerAPIUrl?: string;
166
+ /**
167
+ * The name of the test driver to use.
168
+ */
169
+ driver?: string;
170
+ /**
171
+ * The url to the html that loads the hott files.
172
+ */
173
+ launchpadUrl?: string;
174
+ /**
175
+ * The maps to test in order.
176
+ */
177
+ maps?: string[];
178
+ },
179
+ api?: {
180
+ /**
181
+ * The tester class to use. EX: HotTesterMocha
182
+ */
183
+ tester?: string;
184
+ /**
185
+ * The name of the tester to use.
186
+ */
187
+ testerName?: string;
188
+ /**
189
+ * If set to true, this will create a new tester.
190
+ * Default Value: true
191
+ */
192
+ createNewTester?: boolean;
193
+ /**
194
+ * The url that connects to the tester api server.
195
+ */
196
+ testerAPIUrl?: string;
197
+ /**
198
+ * The name of the test driver to use.
199
+ */
200
+ driver?: string;
201
+ /**
202
+ * The url to the html that loads the hott files.
203
+ */
204
+ launchpadUrl?: string;
205
+ /**
206
+ * The maps to test in order.
207
+ */
208
+ maps?: string[];
209
+ }
210
+ };
211
+ /**
212
+ * The routes to load.
213
+ */
214
+ routes?: {
215
+ [routeName: string]: HotSiteRoute;
216
+ };
217
+ /**
218
+ * The available APIs on the server. The server must already have these
219
+ * loaded.
220
+ */
221
+ apis?: {
222
+ [name: string]: {
223
+ /**
224
+ * The JS API file to load.
225
+ */
226
+ jsapi?: string;
227
+ /**
228
+ * The exported JS library name to use.
229
+ */
230
+ libraryName?: string;
231
+ /**
232
+ * The name of the api to use.
233
+ */
234
+ apiName?: string;
235
+ /**
236
+ * The port to use.
237
+ */
238
+ port?: number;
239
+ /**
240
+ * The public base url for the api.
241
+ */
242
+ url?: string;
243
+ /**
244
+ * The server-side filepath for the api.
245
+ */
246
+ filepath?: string;
247
+ /**
248
+ * The maps to test in order.
249
+ */
250
+ map?: string[];
251
+ };
252
+ };
253
+ /**
254
+ * Secrets that can be publicly embedded into the page.
255
+ */
256
+ publicSecrets?: {
257
+ [name: string]: string | {
258
+ /**
259
+ * The key of an API secret to pass to the site to
260
+ * be used publicly.
261
+ */
262
+ passSecretFromAPI?: string;
263
+ /**
264
+ * Get the public secret from an environment variable.
265
+ */
266
+ env?: string;
267
+ };
268
+ };
269
+ /**
270
+ * The components to load and register.
271
+ */
272
+ components?: {
273
+ [name: string]: {
274
+ /**
275
+ * The url to the component to load and register.
276
+ */
277
+ url: string;
278
+ };
279
+ };
280
+ /**
281
+ * The files to load and save in memory.
282
+ */
283
+ files?: {
284
+ [name: string]: {
285
+ /**
286
+ * The url to the file to load.
287
+ */
288
+ url: string;
289
+ };
290
+ };
291
+ }
292
+
293
+ /**
294
+ * The options to use when starting a page.
295
+ */
296
+ export interface HotStartOptions
297
+ {
298
+ /**
299
+ * The Hott site to load.
300
+ */
301
+ url: string;
302
+ /**
303
+ * The name of the page to load.
304
+ */
305
+ name?: string;
306
+ /**
307
+ * The processor to use to load the page.
308
+ */
309
+ processor?: HotStaq;
310
+ /**
311
+ * Any arguments to pass to the new page.
312
+ */
313
+ args?: any;
314
+ /**
315
+ * The name of the tester to use.
316
+ */
317
+ testerName?: string;
318
+ /**
319
+ * The name of the tester map to use.
320
+ */
321
+ testerMap?: string;
322
+ /**
323
+ * The base url for the tester api.
324
+ */
325
+ testerAPIBaseUrl?: string;
326
+ /**
327
+ * The url to the html that loads the hott file that's
328
+ * pointed at the url above.
329
+ */
330
+ testerLaunchpadUrl?: string;
331
+ }
332
+
333
+ /**
334
+ * The main class that handles all HTML preprocessing, then outputs the
335
+ * results.
336
+ */
337
+ export interface IHotStaq
338
+ {
339
+ /**
340
+ * The api that's used to communicate with.
341
+ */
342
+ api?: HotAPI;
343
+ /**
344
+ * The tester api that's used to communicate with.
345
+ */
346
+ testerAPI?: HotAPI;
347
+ /**
348
+ * Indicates what type of execution this is.
349
+ */
350
+ mode?: DeveloperMode;
351
+ /**
352
+ * The pages that can be constructed.
353
+ */
354
+ pages?: { [name: string]: HotPage };
355
+ /**
356
+ * The components that can be constructed.
357
+ */
358
+ components?: { [name: string]: HotComponent };
359
+ /**
360
+ * The files that can be stored for later use.
361
+ */
362
+ files?: { [name: string]: HotFile };
363
+ /**
364
+ * The loaded hotsite.
365
+ */
366
+ hotSite?: HotSite;
367
+ }
368
+
369
+ /**
370
+ * The main class that handles all HTML preprocessing, then outputs the
371
+ * results.
372
+ */
373
+ export class HotStaq implements IHotStaq
374
+ {
375
+ /**
376
+ * Indicates if this is a web build.
377
+ */
378
+ static isWeb: boolean = false;
379
+ /**
380
+ * Indicates if this is ready for testing.
381
+ */
382
+ static isReadyForTesting: boolean = false;
383
+ /**
384
+ * Executes this event when this page is ready for testing.
385
+ */
386
+ static onReadyForTesting: () => Promise<void> = null;
387
+ /**
388
+ * Indicates what type of execution this is.
389
+ */
390
+ mode: DeveloperMode;
391
+ /**
392
+ * The api that's used to communicate with.
393
+ */
394
+ api: HotAPI;
395
+ /**
396
+ * The tester api that's used to communicate with.
397
+ */
398
+ testerAPI: HotAPI;
399
+ /**
400
+ * The pages that can be constructed.
401
+ */
402
+ pages: { [name: string]: HotPage };
403
+ /**
404
+ * The components that can be constructed.
405
+ */
406
+ components: { [name: string]: HotComponent };
407
+ /**
408
+ * The files that can be stored for later use.
409
+ */
410
+ files: { [name: string]: HotFile };
411
+ /**
412
+ * The loaded hotsite.
413
+ */
414
+ hotSite: HotSite;
415
+ /**
416
+ * The api content to use when about to load HotStaq.
417
+ */
418
+ apiContent: string;
419
+ /**
420
+ * The tester api content to use when about to load HotStaq.
421
+ */
422
+ testerApiContent: string;
423
+ /**
424
+ * The page content to use when about to load HotStaq.
425
+ */
426
+ pageContent: string;
427
+ /**
428
+ * The logger.
429
+ */
430
+ logger: HotLog;
431
+ /**
432
+ * The secrets that can be exposed publicly.
433
+ */
434
+ publicSecrets: any;
435
+ /**
436
+ * The secrets that can be exposed publicly.
437
+ */
438
+ testers: { [name: string]: HotTester };
439
+
440
+ constructor (copy: IHotStaq = {})
441
+ {
442
+ this.api = copy.api || null;
443
+ this.testerAPI = copy.testerAPI || null;
444
+ this.mode = copy.mode || DeveloperMode.Production;
445
+ this.pages = copy.pages || {};
446
+ this.components = copy.components || {};
447
+ this.files = copy.files || {};
448
+ this.hotSite = copy.hotSite || null;
449
+ this.apiContent = `
450
+ var %api_name% = %api_exported_name%.%api_name%;
451
+ var newHotClient = new HotClient (processor);
452
+ var newapi = new %api_name% (%base_url%, newHotClient);
453
+ newHotClient.api = newapi;
454
+ processor.api = newapi;`;
455
+ this.testerApiContent = `
456
+ var HotTesterAPI = HotStaqWeb.HotTesterAPI;
457
+ var newHotTesterClient = new HotClient (processor);
458
+ var newtesterapi = new HotTesterAPI (%base_tester_url%, newHotTesterClient);
459
+ newHotTesterClient.testerAPI = newtesterapi;
460
+ processor.testerAPI = newtesterapi;`;
461
+ this.pageContent =
462
+ `<!DOCTYPE html>
463
+ <html>
464
+
465
+ <head>
466
+ <title>%title%</title>
467
+
468
+ <script type = "text/javascript" src = "%hotstaq_js_src%"></script>
469
+
470
+ %apis_to_load%
471
+
472
+ <script type = "text/javascript">
473
+ function hotstaq_startApp ()
474
+ {
475
+ let tempMode = 0;
476
+
477
+ if (window["Hot"] != null)
478
+ tempMode = Hot.Mode;
479
+
480
+ window.HotStaq = HotStaqWeb.HotStaq;
481
+ window.HotClient = HotStaqWeb.HotClient;
482
+ window.Hot = HotStaqWeb.Hot;
483
+
484
+ %load_hot_site%
485
+
486
+ var processor = new HotStaq ();
487
+ var promises = [];
488
+ %developer_mode%
489
+
490
+ %api_code%
491
+
492
+ %public_secrets%
493
+ %tester_api%
494
+ %load_files%
495
+
496
+ processor.mode = tempMode;
497
+
498
+ Promise.all (promises).then (function ()
499
+ {
500
+ HotStaq.displayUrl ({
501
+ url: "%url%",
502
+ name: "%title%",
503
+ processor: processor,
504
+ args: %args%,
505
+ testerName: %tester_name%,
506
+ testerMap: %tester_map%,
507
+ testerAPIBaseUrl: %tester_api_base_url%,
508
+ testerLaunchpadUrl: %tester_launchpad_url%
509
+ });
510
+ });
511
+ }
512
+
513
+ hotstaq_startApp ();
514
+ </script>
515
+ </head>
516
+
517
+ <body>
518
+ </body>
519
+
520
+ </html>`;
521
+ this.logger = new HotLog (HotLogLevel.None);
522
+ this.publicSecrets = {};
523
+ this.testers = {};
524
+ }
525
+
526
+ /**
527
+ * Parse a boolean value.
528
+ */
529
+ static parseBoolean (value: string): boolean
530
+ {
531
+ value = value.toLowerCase ();
532
+
533
+ if (value === "true")
534
+ return (true);
535
+
536
+ if (value === "false")
537
+ return (false);
538
+
539
+ if (value === "yes")
540
+ return (true);
541
+
542
+ if (value === "no")
543
+ return (false);
544
+
545
+ if (value === "yep")
546
+ return (true);
547
+
548
+ if (value === "nah")
549
+ return (false);
550
+
551
+ try
552
+ {
553
+ if (parseInt (value) != 0)
554
+ return (true);
555
+ }
556
+ catch (ex)
557
+ {
558
+ }
559
+
560
+ return (false);
561
+ }
562
+
563
+ /**
564
+ * Check if a required parameter exists inside an object. If it exists, return the value.
565
+ */
566
+ static getParam (name: string, objWithParam: any, required: boolean = true, throwException: boolean = true): any
567
+ {
568
+ let value: any = objWithParam[name];
569
+
570
+ if (value == null)
571
+ {
572
+ if (required === true)
573
+ {
574
+ if (throwException === true)
575
+ throw new Error (`Missing required parameter ${name}.`);
576
+ }
577
+ }
578
+
579
+ if (typeof (value) === "string")
580
+ {
581
+ if (required === true)
582
+ {
583
+ if (value === "")
584
+ {
585
+ if (throwException === true)
586
+ throw new Error (`Missing required parameter ${name}.`);
587
+ }
588
+ }
589
+ }
590
+
591
+ return (value);
592
+ }
593
+
594
+ /**
595
+ * Check if a required parameter exists inside an object. If it exists, return the value.
596
+ * If it does not exist, return a default value instead.
597
+ */
598
+ static getParamDefault (name: string, objWithParam: any, defaultValue: any): any
599
+ {
600
+ let value: any = objWithParam[name];
601
+
602
+ if (value == null)
603
+ return (defaultValue);
604
+
605
+ if (typeof (value) === "string")
606
+ {
607
+ if (value === "")
608
+ return (defaultValue);
609
+ }
610
+
611
+ return (value);
612
+ }
613
+
614
+ /**
615
+ * Wait for a number of milliseconds.
616
+ */
617
+ static async wait (numMilliseconds: number): Promise<void>
618
+ {
619
+ return (await new Promise ((resolve, reject) =>
620
+ {
621
+ setTimeout (() =>
622
+ {
623
+ resolve ();
624
+ }, numMilliseconds);
625
+ }));
626
+ }
627
+
628
+ /**
629
+ * Add a page.
630
+ */
631
+ addPage (page: HotPage): void
632
+ {
633
+ this.pages[page.name] = page;
634
+ }
635
+
636
+ /**
637
+ * Get a page to process.
638
+ */
639
+ getPage (pageName: string): HotPage
640
+ {
641
+ return (this.pages[pageName]);
642
+ }
643
+
644
+ /**
645
+ * Add a file.
646
+ */
647
+ addFile (file: HotFile): void
648
+ {
649
+ let name: string = file.name;
650
+
651
+ if (name === "")
652
+ name = file.localFile;
653
+
654
+ if (name === "")
655
+ name = file.url;
656
+
657
+ this.files[name] = file;
658
+ }
659
+
660
+ /**
661
+ * Get a file.
662
+ */
663
+ getFile (name: string): HotFile
664
+ {
665
+ if (this.files[name] == null)
666
+ throw new Error (`Unable to find file ${name}`);
667
+
668
+ return (this.files[name]);
669
+ }
670
+
671
+ /**
672
+ * Add and register a component.
673
+ */
674
+ addComponent (component: HotComponent): void
675
+ {
676
+ this.components[component.name] = component;
677
+ this.registerComponent (component);
678
+ }
679
+
680
+ /**
681
+ * Register a component for use as a HTML tag.
682
+ */
683
+ registerComponent (component: HotComponent): void
684
+ {
685
+ customElements.define (component.tag, class extends HTMLElement
686
+ {
687
+ constructor ()
688
+ {
689
+ super ();
690
+
691
+ /// @fixme Is this bad? Could create race conditions.
692
+ (async () =>
693
+ {
694
+ this.onclick = component.click.bind (component);
695
+
696
+ for (let key in component.events)
697
+ {
698
+ let event = component.events[key];
699
+
700
+ // @ts-ignore
701
+ this.addEventListener (event.type, event.func, event.options);
702
+ }
703
+
704
+ component.htmlElement = await component.onCreated (this);
705
+
706
+ if (component.handleAttributes != null)
707
+ await component.handleAttributes (this.attributes);
708
+ else
709
+ {
710
+ for (let iIdx = 0; iIdx < this.attributes.length; iIdx++)
711
+ {
712
+ let attr: Attr = this.attributes[iIdx];
713
+ let attrName: string = attr.name.toLowerCase ();
714
+ let attrValue: string = attr.value;
715
+
716
+ if (attrName === "id")
717
+ component.name = attrValue;
718
+
719
+ if (attrName === "name")
720
+ component.name = attrValue;
721
+
722
+ if (attrName === "value")
723
+ component.value = attrValue;
724
+ }
725
+ }
726
+
727
+ let str: string = await component.output ();
728
+ let newDOM: Document = new DOMParser ().parseFromString (str, "text/html");
729
+ let shadow: ShadowRoot = this.attachShadow ({ mode: "open" });
730
+
731
+ for (let iIdx = 0; iIdx < newDOM.body.children.length; iIdx++)
732
+ {
733
+ let child = newDOM.body.children[iIdx];
734
+ shadow.appendChild (child);
735
+ }
736
+ })();
737
+ }
738
+ }, component.elementOptions);
739
+ }
740
+
741
+ /**
742
+ * Get a component to process.
743
+ */
744
+ getComponent (name: string): HotComponent
745
+ {
746
+ return (this.components[name]);
747
+ }
748
+
749
+ /**
750
+ * Add a new HTML element(s) to the current document.
751
+ */
752
+ static addHtml (parent: string | HTMLElement, html: string | HTMLElement): HTMLElement | HTMLElement[]
753
+ {
754
+ let foundParent: HTMLElement = null;
755
+
756
+ if (typeof (parent) === "string")
757
+ foundParent = document.querySelector (parent);
758
+ else
759
+ foundParent = parent;
760
+
761
+ if (foundParent == null)
762
+ throw new Error (`Unable to find parent ${parent}!`);
763
+
764
+ let result: HTMLElement = null;
765
+
766
+ if (typeof (html) === "string")
767
+ {
768
+ let newDOM: Document = new DOMParser ().parseFromString (html, "text/html");
769
+ let results: HTMLElement[] = [];
770
+
771
+ for (let iIdx = 0; iIdx < newDOM.body.children.length; iIdx++)
772
+ {
773
+ let child: HTMLElement = (<HTMLElement>newDOM.body.children[iIdx]);
774
+
775
+ results.push (foundParent.appendChild (child));
776
+ }
777
+
778
+ return (results);
779
+ }
780
+ else
781
+ result = foundParent.appendChild (html);
782
+
783
+ return (result);
784
+ }
785
+
786
+ /**
787
+ * Check if a HotSite's name is valid.
788
+ */
789
+ static checkHotSiteName (hotsiteName: string, throwException: boolean = false): boolean
790
+ {
791
+ let throwTheException = () =>
792
+ {
793
+ if (throwException === true)
794
+ throw new Error (`HotSite ${hotsiteName} has an invalid name! The name cannot be empty and must have a valid NPM module name.`);
795
+ };
796
+
797
+ let results = validateModuleName (hotsiteName);
798
+
799
+ if (results.errors != null)
800
+ {
801
+ if (results.errors.length > 0)
802
+ throwTheException ();
803
+ }
804
+
805
+ return (true);
806
+ }
807
+
808
+ /**
809
+ * Replace a key in a ${KEY} with a value.
810
+ */
811
+ static replaceKey (str: string, key: string, value: string): string
812
+ {
813
+ const finalStr: string = str.replace (new RegExp (`\\$\\{${key}\\}`, "g"), value);
814
+
815
+ return (finalStr);
816
+ }
817
+
818
+ /**
819
+ * Get a value from a HotSite object.
820
+ *
821
+ * @returns Returns the value from the hotsite object. Returns null if it doesn't exist.
822
+ */
823
+ static getValueFromHotSiteObj (hotsite: HotSite, params: string[]): any
824
+ {
825
+ let value: any = null;
826
+
827
+ if (hotsite != null)
828
+ {
829
+ let prevValue: any = hotsite;
830
+
831
+ // Go through each object in the list of parameters and
832
+ // get the value of the final parameter.
833
+ for (let iIdx = 0; iIdx < params.length; iIdx++)
834
+ {
835
+ let param: string = params[iIdx];
836
+
837
+ if (prevValue[param] == null)
838
+ {
839
+ prevValue = null;
840
+
841
+ break;
842
+ }
843
+
844
+ prevValue = prevValue[param];
845
+ }
846
+
847
+ if (prevValue != null)
848
+ value = prevValue;
849
+ }
850
+
851
+ return (value);
852
+ }
853
+
854
+ /**
855
+ * Load from a HotSite.json file. Be sure to load and attach any testers before
856
+ * loading a HotSite.
857
+ */
858
+ async loadHotSite (path: string): Promise<void>
859
+ {
860
+ let jsonStr: string = "";
861
+
862
+ if (HotStaq.isWeb === true)
863
+ {
864
+ this.logger.info (`Retrieving HotSite ${path}`);
865
+
866
+ let res: any = await fetch (path);
867
+
868
+ this.logger.info (`Retrieved site ${path}`);
869
+
870
+ jsonStr = res.text ();
871
+ }
872
+ else
873
+ {
874
+ path = ppath.normalize (path);
875
+
876
+ this.logger.info (`Retrieving HotSite ${path}`);
877
+
878
+ jsonStr = await new Promise (
879
+ (resolve: any, reject: any): void =>
880
+ {
881
+ fs.readFile (path, (err: NodeJS.ErrnoException, data: Buffer): void =>
882
+ {
883
+ if (err != null)
884
+ throw err;
885
+
886
+ let content: string = data.toString ();
887
+
888
+ this.logger.info (`Retrieved site ${path}`);
889
+
890
+ resolve (content);
891
+ });
892
+ });
893
+ }
894
+
895
+ this.hotSite = JSON.parse (jsonStr);
896
+
897
+ HotStaq.checkHotSiteName (this.hotSite.name, true);
898
+
899
+ this.hotSite.hotsitePath = path;
900
+ let routes = this.hotSite.routes;
901
+ let testerUrl: string = "http://127.0.0.1:8182";
902
+ let tester: HotTester = null;
903
+ let driver: HotTestDriver = null;
904
+
905
+ if (HotStaq.isWeb === false)
906
+ {
907
+ if (this.mode === DeveloperMode.Development)
908
+ {
909
+ if (this.hotSite.testing != null)
910
+ {
911
+ let setupTester = (parentObj: any) =>
912
+ {
913
+ let createNewTester: boolean = true;
914
+
915
+ if (parentObj.createNewTester != null)
916
+ createNewTester = parentObj.createNewTester;
917
+
918
+ let testerName: string = "Tester";
919
+
920
+ if (parentObj.tester != null)
921
+ testerName = parentObj.tester;
922
+
923
+ if (parentObj.testerName != null)
924
+ testerName = parentObj.testerName;
925
+
926
+ if (createNewTester === true)
927
+ {
928
+ /// @fixme Find a way to securely allow devs to use their own drivers and testers...
929
+ /// @fixme Hack for dealing with WebPack's bs.
930
+ HotTesterMocha = require ("./HotTesterMocha").HotTesterMocha;
931
+ HotTesterMochaSelenium = require ("./HotTesterMochaSelenium").HotTesterMochaSelenium;
932
+ HotTestSeleniumDriver = require ("./HotTestSeleniumDriver").HotTestSeleniumDriver;
933
+
934
+ if (parentObj.testerAPIUrl === "")
935
+ testerUrl = parentObj.testerAPIUrl;
936
+
937
+ if (parentObj.driver === "HotTestSeleniumDriver")
938
+ driver = new HotTestSeleniumDriver ();
939
+
940
+ if (parentObj.tester === "HotTesterMocha")
941
+ tester = new HotTesterMocha (this, testerName, testerUrl, driver);
942
+
943
+ if (parentObj.tester === "HotTesterMochaSelenium")
944
+ tester = new HotTesterMochaSelenium (this, testerName, testerUrl);
945
+ }
946
+ else
947
+ tester = this.testers[testerName];
948
+ };
949
+
950
+ if (this.hotSite.testing.web != null)
951
+ setupTester (this.hotSite.testing.web);
952
+
953
+ if (this.hotSite.testing.api != null)
954
+ setupTester (this.hotSite.testing.api);
955
+ }
956
+ }
957
+ }
958
+
959
+ if (routes != null)
960
+ {
961
+ for (let key in routes)
962
+ {
963
+ let route: HotSiteRoute = routes[key];
964
+ let file: HotFile = new HotFile (route);
965
+ let page: HotPage = new HotPage ({
966
+ processor: this,
967
+ name: route.name || "",
968
+ route: key,
969
+ files: [file]
970
+ });
971
+
972
+ if (tester != null)
973
+ {
974
+ if (this.mode === DeveloperMode.Development)
975
+ {
976
+ let mapName: string = route.name;
977
+ let testMap: HotTestMap = null;
978
+
979
+ if (route.map != null)
980
+ {
981
+ if (typeof (route.map) === "string")
982
+ {
983
+ if (tester.testMaps[route.map] == null)
984
+ throw new Error (`Test map ${route.map} does not exist!`);
985
+
986
+ tester.testMaps[mapName] = tester.testMaps[route.map];
987
+ }
988
+ else
989
+ {
990
+ testMap = new HotTestMap ();
991
+ let destinations: HotTestDestination[] | { [name: string]: HotTestDestination } = null;
992
+
993
+ if (route.map instanceof Array)
994
+ {
995
+ destinations = [];
996
+
997
+ for (let iIdx = 0; iIdx < route.map.length; iIdx++)
998
+ {
999
+ let dest = route.map[iIdx];
1000
+
1001
+ destinations.push (new HotTestDestination (dest));
1002
+ }
1003
+ }
1004
+ else
1005
+ {
1006
+ destinations = {};
1007
+
1008
+ for (let key2 in route.map)
1009
+ {
1010
+ let dest = route.map[key2];
1011
+
1012
+ destinations[key2] = new HotTestDestination (dest);
1013
+ }
1014
+ }
1015
+
1016
+ testMap.destinations = destinations;
1017
+ }
1018
+
1019
+ tester.testMaps[mapName] = testMap;
1020
+ }
1021
+
1022
+ if (route.destinationOrder != null)
1023
+ tester.testMaps[mapName].destinationOrder = route.destinationOrder;
1024
+ }
1025
+ }
1026
+
1027
+ this.addPage (page);
1028
+ }
1029
+ }
1030
+
1031
+ if (this.hotSite.apis != null)
1032
+ {
1033
+ for (let key in this.hotSite.apis)
1034
+ {
1035
+ let api = this.hotSite.apis[key];
1036
+
1037
+ if (api.map == null)
1038
+ continue;
1039
+
1040
+ if (HotStaq.isWeb === false)
1041
+ {
1042
+ if (this.mode === DeveloperMode.Development)
1043
+ {
1044
+ let mapName: string = key;
1045
+ let testMap: HotTestMap = new HotTestMap ();
1046
+
1047
+ testMap.destinations = [];
1048
+
1049
+ for (let iIdx = 0; iIdx < api.map.length; iIdx++)
1050
+ {
1051
+ let map: string = api.map[iIdx];
1052
+
1053
+ testMap.destinations.push (new HotTestDestination (map));
1054
+ }
1055
+
1056
+ if (tester == null)
1057
+ throw new Error (`A tester was not created first! You must specify one in the CLI or in HotSite.json.`);
1058
+
1059
+ tester.testMaps[mapName] = testMap;
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ /// @fixme Allow this to work for server-side as well...
1066
+ if (HotStaq.isWeb === true)
1067
+ {
1068
+ for (let key in this.hotSite.components)
1069
+ {
1070
+ let component = this.hotSite.components[key];
1071
+ let componentUrl: string = component.url;
1072
+
1073
+ /// @fixme Create unit test for fetching, loading, and registering.
1074
+ let res: any = await fetch (componentUrl);
1075
+ let newComponent: HotComponent = eval (res);
1076
+
1077
+ this.addComponent (newComponent);
1078
+ }
1079
+ }
1080
+
1081
+ if (this.hotSite.routes == null)
1082
+ this.hotSite.routes = {};
1083
+
1084
+ await this.loadHotFiles (this.hotSite.files);
1085
+
1086
+ if (tester != null)
1087
+ this.addTester (tester);
1088
+ }
1089
+
1090
+ /**
1091
+ * Load an array of files. If a file already has content, it will not be reloaded
1092
+ * unless forceContentLoading is set to true.
1093
+ */
1094
+ async loadHotFiles (files: { [name: string]: { url?: string; localFile?: string; content?: string; } },
1095
+ forceContentLoading: boolean = false): Promise<void>
1096
+ {
1097
+ for (let key in files)
1098
+ {
1099
+ let file = files[key];
1100
+ let newFile: HotFile = null;
1101
+
1102
+ if (HotStaq.isWeb === true)
1103
+ {
1104
+ newFile = new HotFile ({
1105
+ "name": key
1106
+ });
1107
+ }
1108
+ else
1109
+ {
1110
+ newFile = new HotFile ({
1111
+ "name": key
1112
+ });
1113
+ }
1114
+
1115
+ if (file.url != null)
1116
+ newFile.url = file.url;
1117
+
1118
+ if (HotStaq.isWeb === false)
1119
+ {
1120
+ if (file.localFile != null)
1121
+ newFile.localFile = file.localFile;
1122
+ }
1123
+
1124
+ let loadContent: boolean = true;
1125
+
1126
+ if (file.content != null)
1127
+ {
1128
+ newFile.content = file.content;
1129
+ loadContent = false;
1130
+ }
1131
+
1132
+ if (forceContentLoading === true)
1133
+ loadContent = true;
1134
+
1135
+ if (loadContent === true)
1136
+ await newFile.load ();
1137
+
1138
+ this.addFile (newFile);
1139
+ }
1140
+ }
1141
+
1142
+ /**
1143
+ * Generate the content to send to a client.
1144
+ */
1145
+ generateContent (routeKey: string, name: string = "", url: string = "./",
1146
+ jsSrcPath: string = "./js/HotStaq.js", passArgs: boolean = true,
1147
+ args: any = null): string
1148
+ {
1149
+ let apiScripts: string = "";
1150
+ let apiCode: string = "";
1151
+ let publicSecrets: string = "";
1152
+
1153
+ /// @todo Optimize this function as much as possible.
1154
+
1155
+ // Load the API string.
1156
+ if (this.hotSite != null)
1157
+ {
1158
+ if (this.hotSite.server.globalApi != null)
1159
+ {
1160
+ if (this.hotSite.server.globalApi !== "")
1161
+ {
1162
+ const globalApi = this.hotSite.apis[this.hotSite.server.globalApi];
1163
+
1164
+ if (globalApi == null)
1165
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't exist!`);
1166
+ else
1167
+ {
1168
+ let sendJSContent: boolean = true;
1169
+
1170
+ if (globalApi.jsapi == null)
1171
+ {
1172
+ sendJSContent = false;
1173
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a jsapi set. Will not send js content to client.`);
1174
+ }
1175
+
1176
+ if (globalApi.libraryName == null)
1177
+ {
1178
+ sendJSContent = false;
1179
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a libraryName set. Will not send js content to client.`);
1180
+ }
1181
+
1182
+ if (globalApi.apiName == null)
1183
+ {
1184
+ sendJSContent = false;
1185
+ this.logger.warning (`API with name ${this.hotSite.server.globalApi} doesn't have a apiName set. Will not send js content to client.`);
1186
+ }
1187
+
1188
+ if (sendJSContent === true)
1189
+ {
1190
+ apiScripts += `\t<script type = "text/javascript" src = "${globalApi.jsapi}"></script>\n`;
1191
+
1192
+ let baseUrl: string = "\"\"";
1193
+
1194
+ if (this.api != null)
1195
+ baseUrl = `\"${this.api.baseUrl}\"`;
1196
+
1197
+ let tempAPIContent: string = this.apiContent;
1198
+ tempAPIContent = tempAPIContent.replace (/\%api\_name\%/g, globalApi.apiName);
1199
+ tempAPIContent = tempAPIContent.replace (/\%api\_exported\_name\%/g, globalApi.libraryName);
1200
+ tempAPIContent = tempAPIContent.replace (/\%base\_url\%/g, baseUrl);
1201
+
1202
+ apiCode += tempAPIContent;
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ if (this.hotSite.apis != null)
1209
+ {
1210
+ let route = this.hotSite.routes[routeKey];
1211
+
1212
+ if (route != null)
1213
+ {
1214
+ if (route.api != null)
1215
+ {
1216
+ let api = this.hotSite.apis[route.api];
1217
+
1218
+ if (api == null)
1219
+ throw new Error (`Unable to find API ${route.api}`);
1220
+
1221
+ let sendJSContent: boolean = true;
1222
+
1223
+ if (api.jsapi == null)
1224
+ {
1225
+ sendJSContent = false;
1226
+ this.logger.warning (`API with name ${route.api} doesn't have a jsapi set. Will not send js content to client.`);
1227
+ }
1228
+
1229
+ if (api.libraryName == null)
1230
+ {
1231
+ sendJSContent = false;
1232
+ this.logger.warning (`API with name ${route.api} doesn't have a libraryName set. Will not send js content to client.`);
1233
+ }
1234
+
1235
+ if (api.apiName == null)
1236
+ {
1237
+ sendJSContent = false;
1238
+ this.logger.warning (`API with name ${route.api} doesn't have a apiName set. Will not send js content to client.`);
1239
+ }
1240
+
1241
+ if (sendJSContent === true)
1242
+ {
1243
+ let jsapipath = api.jsapi;
1244
+ apiScripts += `\t<script type = "text/javascript" src = "${jsapipath}"></script>\n`;
1245
+
1246
+ let baseUrl: string = "\"\"";
1247
+
1248
+ if (this.api != null)
1249
+ baseUrl = `\"${this.api.baseUrl}\"`;
1250
+
1251
+ let tempAPIContent: string = this.apiContent;
1252
+ tempAPIContent = tempAPIContent.replace (/\%api\_name\%/g, api.apiName);
1253
+ tempAPIContent = tempAPIContent.replace (/\%api\_exported\_name\%/g, api.libraryName);
1254
+ tempAPIContent = tempAPIContent.replace (/\%base\_url\%/g, baseUrl);
1255
+
1256
+ apiCode += tempAPIContent;
1257
+ }
1258
+ }
1259
+ }
1260
+ }
1261
+
1262
+ if (this.hotSite.server != null)
1263
+ {
1264
+ if (this.hotSite.server.jsSrcPath != null)
1265
+ jsSrcPath = this.hotSite.server.jsSrcPath;
1266
+ }
1267
+
1268
+ if (this.hotSite.publicSecrets != null)
1269
+ {
1270
+ for (let key in this.hotSite.publicSecrets)
1271
+ {
1272
+ let secret = this.hotSite.publicSecrets[key];
1273
+ let value: string = undefined;
1274
+
1275
+ if (typeof (secret) === "string")
1276
+ value = JSON.stringify (secret);
1277
+ else
1278
+ {
1279
+ if (HotStaq.isWeb === false)
1280
+ {
1281
+ if (this.api != null)
1282
+ {
1283
+ if (this.api.connection == null)
1284
+ throw new Error (`Cannot pass secrets from the API if there's no connection!`);
1285
+
1286
+ let serverConn: HotServer = (<HotServer>this.api.connection);
1287
+
1288
+ if (secret.passSecretFromAPI != null)
1289
+ value = JSON.stringify (serverConn.secrets[key]);
1290
+ }
1291
+
1292
+ if (secret.env != null)
1293
+ {
1294
+ /// @fixme @secvul Is this a security vulnerability? Need to verify that
1295
+ /// only the server has access to this. At this point, I think only the
1296
+ /// server has access.
1297
+ const envKey: string = secret.env;
1298
+
1299
+ value = JSON.stringify (process.env[envKey]);
1300
+ }
1301
+ }
1302
+ }
1303
+
1304
+ publicSecrets += `processor.publicSecrets["${key}"] = ${value};\n`;
1305
+ }
1306
+ }
1307
+ }
1308
+
1309
+ let content: string = this.pageContent;
1310
+ let fixContent = (tempContent: string) =>
1311
+ {
1312
+ let developerModeStr: string = "";
1313
+ let testerAPIStr: string = "";
1314
+
1315
+ if (this.mode === DeveloperMode.Development)
1316
+ {
1317
+ developerModeStr = `tempMode = HotStaqWeb.DeveloperMode.Development;`;
1318
+ testerAPIStr = this.testerApiContent;
1319
+
1320
+ if (this.hotSite != null)
1321
+ {
1322
+ if (this.hotSite.testing != null)
1323
+ {
1324
+ if (this.hotSite.testing.web.testerAPIUrl == null)
1325
+ this.hotSite.testing.web.testerAPIUrl = "http://127.0.0.1:8182";
1326
+
1327
+ testerAPIStr = testerAPIStr.replace (/\%base\_tester\_url\%/g, `\"${this.hotSite.testing.web.testerAPIUrl}\"`);
1328
+ }
1329
+ }
1330
+ }
1331
+
1332
+ let loadFiles: string = "";
1333
+
1334
+ if (Object.keys (this.files).length > 0)
1335
+ {
1336
+ loadFiles += `var files = {};\n\n`;
1337
+
1338
+ for (let key in this.files)
1339
+ {
1340
+ let file = this.files[key];
1341
+ let fileUrl: string = `"${file.url}"`;
1342
+ let fileContent: string = "";
1343
+
1344
+ if (file.content !== "")
1345
+ {
1346
+ let escapedContent: string = JSON.stringify (file.content);
1347
+
1348
+ // Find any script tags and interrupt them so the HTML parsers
1349
+ // don't get confused.
1350
+ escapedContent = escapedContent.replace (new RegExp ("\\<script", "gmi"), "<scr\" + \"ipt");
1351
+ escapedContent = escapedContent.replace (new RegExp ("\\<\\/script", "gmi"), "</scr\" + \"ipt");
1352
+
1353
+ fileContent = `, "content": ${escapedContent}`;
1354
+ }
1355
+
1356
+ loadFiles += `\t\t\tfiles["${key}"] = { "url": ${fileUrl}${fileContent} };\n`;
1357
+ }
1358
+
1359
+ loadFiles += `\t\t\tpromises.push (processor.loadHotFiles (files));\n`;
1360
+ }
1361
+
1362
+ tempContent = tempContent.replace (/\%title\%/g, name);
1363
+
1364
+ if (passArgs === true)
1365
+ tempContent = tempContent.replace (/\%args\%/g, "Hot.Arguments");
1366
+
1367
+ if (args != null)
1368
+ tempContent = tempContent.replace (/\%args\%/g, JSON.stringify (args));
1369
+
1370
+ let testerMap: string = routeKey;
1371
+ let testerUrl: string = "";
1372
+ let testerLaunchpadUrl: string = "";
1373
+ let testerName: string = "Tester";
1374
+
1375
+ if (this.hotSite != null)
1376
+ {
1377
+ if (this.hotSite.testing != null)
1378
+ {
1379
+ if (this.hotSite.testing.web.tester != null)
1380
+ testerName = this.hotSite.testing.web.tester;
1381
+
1382
+ if (this.hotSite.testing.web.testerName != null)
1383
+ testerName = this.hotSite.testing.web.testerName;
1384
+
1385
+ if (this.hotSite.testing.web.testerAPIUrl != null)
1386
+ testerUrl = this.hotSite.testing.web.testerAPIUrl;
1387
+
1388
+ if (this.hotSite.testing.web.launchpadUrl != null)
1389
+ testerLaunchpadUrl = this.hotSite.testing.web.launchpadUrl;
1390
+ }
1391
+
1392
+ if (this.hotSite.routes != null)
1393
+ {
1394
+ if (this.hotSite.routes[routeKey] != null)
1395
+ {
1396
+ let route = this.hotSite.routes[routeKey];
1397
+ testerMap = route.name;
1398
+ }
1399
+ }
1400
+ }
1401
+
1402
+ tempContent = tempContent.replace (/\%hotstaq\_js\_src\%/g, jsSrcPath);
1403
+ tempContent = tempContent.replace (/\%developer\_mode\%/g, developerModeStr);
1404
+ tempContent = tempContent.replace (/\%tester\_api\%/g, testerAPIStr);
1405
+ tempContent = tempContent.replace (/\%apis\_to\_load\%/g, apiScripts);
1406
+ tempContent = tempContent.replace (/\%load\_hot\_site\%/g, ""); /// @fixme Should this only be done server-side?
1407
+ tempContent = tempContent.replace (/\%load\_files\%/g, loadFiles);
1408
+ tempContent = tempContent.replace (/\%api\_code\%/g, apiCode);
1409
+ tempContent = tempContent.replace (/\%public\_secrets\%/g, publicSecrets);
1410
+ tempContent = tempContent.replace (/\%url\%/g, url);
1411
+ tempContent = tempContent.replace (/\%tester\_name\%/g, `"${testerName}"`);
1412
+ tempContent = tempContent.replace (/\%tester\_map\%/g, `"${testerMap}"`);
1413
+ tempContent = tempContent.replace (/\%tester\_api\_base\_url\%/g, `"${testerUrl}"`);
1414
+ tempContent = tempContent.replace (/\%tester\_launchpad\_url\%/g, `"${testerLaunchpadUrl}"`);
1415
+
1416
+ return (tempContent);
1417
+ };
1418
+ content = fixContent (content);
1419
+
1420
+ return (content);
1421
+ }
1422
+
1423
+ /**
1424
+ * Create the Express routes from the given pages. Be sure to load the
1425
+ * pages first before doing this. This method is meant to be used for
1426
+ * customized Express applications. If you wish to use the loaded routes
1427
+ * from this HotStaq object with HotHTTPServer, be sure to use
1428
+ * the loadHotSite method in HotHTTPServer.
1429
+ */
1430
+ createExpressRoutes (expressApp: any, jsSrcPath: string = "./js/HotStaq.js"): void
1431
+ {
1432
+ for (let key in this.pages)
1433
+ {
1434
+ let page: HotPage = this.pages[key];
1435
+ const content: string = this.generateContent (page.route, page.name, page.files[0].url, jsSrcPath);
1436
+
1437
+ expressApp.get (page.route, (req: any, res: any) =>
1438
+ {
1439
+ res.send (content);
1440
+ });
1441
+ }
1442
+ }
1443
+
1444
+ /**
1445
+ * Add a tester for use later.
1446
+ */
1447
+ addTester (tester: HotTester): void
1448
+ {
1449
+ this.testers[tester.name] = tester;
1450
+ }
1451
+
1452
+ /**
1453
+ * Get the list of maps for testing from the HotSite.
1454
+ */
1455
+ getWebTestingMaps (): string[]
1456
+ {
1457
+ if (this.hotSite == null)
1458
+ throw new Error ("No HotSite was loaded!");
1459
+
1460
+ if (this.hotSite.testing == null)
1461
+ throw new Error ("The HotSite does not have a testing object!");
1462
+
1463
+ if (this.hotSite.testing.web == null)
1464
+ throw new Error ("The HotSite does not have a testing web object!");
1465
+
1466
+ if (this.hotSite.testing.web.maps == null)
1467
+ throw new Error ("The HotSite testing object does not have any maps!");
1468
+
1469
+ return (this.hotSite.testing.web.maps);
1470
+ }
1471
+
1472
+ /**
1473
+ * Get the list of maps for testing from the HotSite.
1474
+ */
1475
+ getAPITestingMaps (): string[]
1476
+ {
1477
+ if (this.hotSite == null)
1478
+ throw new Error ("No HotSite was loaded!");
1479
+
1480
+ if (this.hotSite.testing == null)
1481
+ throw new Error ("The HotSite does not have a testing object!");
1482
+
1483
+ if (this.hotSite.testing.api == null)
1484
+ throw new Error ("The HotSite does not have a testing api object!");
1485
+
1486
+ if (this.hotSite.testing.api.maps == null)
1487
+ throw new Error ("The HotSite testing object does not have any maps!");
1488
+
1489
+ return (this.hotSite.testing.api.maps);
1490
+ }
1491
+
1492
+ /**
1493
+ * Get a route's key from a route's name.
1494
+ */
1495
+ getRouteKeyFromName (name: string): string
1496
+ {
1497
+ let foundKey: string = "";
1498
+
1499
+ if (this.hotSite != null)
1500
+ {
1501
+ if (this.hotSite.routes != null)
1502
+ {
1503
+ for (let key in this.hotSite.routes)
1504
+ {
1505
+ let route: HotSiteRoute = this.hotSite.routes[key];
1506
+
1507
+ if (route.name === name)
1508
+ {
1509
+ foundKey = key;
1510
+
1511
+ break;
1512
+ }
1513
+ }
1514
+ }
1515
+ }
1516
+
1517
+ return (foundKey);
1518
+ }
1519
+
1520
+ /**
1521
+ * Get a route from a route's name.
1522
+ */
1523
+ getRouteFromName (name: string): HotSiteRoute
1524
+ {
1525
+ let foundRoute: HotSiteRoute = null;
1526
+ let foundKey: string = this.getRouteKeyFromName (name);
1527
+
1528
+ if (foundKey !== "")
1529
+ foundRoute = this.hotSite.routes[foundKey];
1530
+
1531
+ return (foundRoute);
1532
+ }
1533
+
1534
+ /**
1535
+ * Execute tests.
1536
+ *
1537
+ * @param testerName The tester to use to execute tests.
1538
+ * @param mapName The map or maps to use to navigate through tests.
1539
+ */
1540
+ async executeTests (testerName: string, mapName: string): Promise<void>
1541
+ {
1542
+ let tester: HotTester = this.testers[testerName];
1543
+
1544
+ if (tester == null)
1545
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1546
+
1547
+ return (tester.execute (mapName));
1548
+ }
1549
+
1550
+ /**
1551
+ * Execute all web tests from the HotSite testing web object.
1552
+ *
1553
+ * @param testerName The tester to use to execute tests.
1554
+ */
1555
+ async executeAllWebTests (testerName: string): Promise<void>
1556
+ {
1557
+ let maps: string[] = this.getWebTestingMaps ();
1558
+ let tester: HotTester = this.testers[testerName];
1559
+
1560
+ if (tester == null)
1561
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1562
+
1563
+ for (let iIdx = 0; iIdx < maps.length; iIdx++)
1564
+ {
1565
+ let mapName: string = maps[iIdx];
1566
+
1567
+ await this.executeTests (testerName, mapName);
1568
+ }
1569
+ }
1570
+
1571
+ /**
1572
+ * Execute all api tests from the HotSite testing api object.
1573
+ *
1574
+ * @param testerName The tester to use to execute tests.
1575
+ */
1576
+ async executeAllAPITests (testerName: string): Promise<void>
1577
+ {
1578
+ let maps: string[] = this.getAPITestingMaps ();
1579
+ let tester: HotTester = this.testers[testerName];
1580
+
1581
+ if (tester == null)
1582
+ throw new Error (`Unable to execute tests. Tester ${testerName} does not exist!`);
1583
+
1584
+ for (let iIdx = 0; iIdx < maps.length; iIdx++)
1585
+ {
1586
+ let mapName: string = maps[iIdx];
1587
+
1588
+ await this.executeTests (testerName, mapName);
1589
+ }
1590
+ }
1591
+
1592
+ /**
1593
+ * Process a page and get the result.
1594
+ */
1595
+ async process (pageName: string, args: any = null): Promise<string>
1596
+ {
1597
+ let page: HotPage = this.getPage (pageName);
1598
+ let result: string = await page.process (args);
1599
+
1600
+ return (result);
1601
+ }
1602
+
1603
+ /**
1604
+ * Process a local file and get the result.
1605
+ */
1606
+ static async processLocalFile (localFilepath: string, name: string = localFilepath, args: any = null): Promise<string>
1607
+ {
1608
+ let processor: HotStaq = new HotStaq ();
1609
+ let file: HotFile = new HotFile ({
1610
+ "localFile": localFilepath
1611
+ });
1612
+ await file.load ();
1613
+ let page: HotPage = new HotPage ({
1614
+ "processor": processor,
1615
+ "name": name,
1616
+ "files": [file]
1617
+ });
1618
+ processor.addPage (page);
1619
+ let result: string = await processor.process (name, args);
1620
+
1621
+ return (result);
1622
+ }
1623
+
1624
+ /**
1625
+ * Process a url and get the result.
1626
+ */
1627
+ static async processUrl (options: HotStartOptions): Promise<string>
1628
+ {
1629
+ let file: HotFile = new HotFile ({
1630
+ "url": options.url
1631
+ });
1632
+
1633
+ await file.load ();
1634
+ let page: HotPage = new HotPage ({
1635
+ "processor": options.processor,
1636
+ "name": options.name,
1637
+ "files": [file],
1638
+ "testerName": options.testerName,
1639
+ "testerMap": options.testerMap
1640
+ });
1641
+ options.processor.addPage (page);
1642
+ let result: string = await options.processor.process (options.name, options.args);
1643
+
1644
+ return (result);
1645
+ }
1646
+
1647
+ /**
1648
+ * Process content and get the result.
1649
+ */
1650
+ static async processContent (processor: HotStaq,
1651
+ content: string, name: string, args: any = null): Promise<string>
1652
+ {
1653
+ let file: HotFile = new HotFile ({
1654
+ "content": content
1655
+ });
1656
+ await file.load ();
1657
+ let page: HotPage = new HotPage ({
1658
+ "processor": processor,
1659
+ "name": name,
1660
+ "files": [file]
1661
+ });
1662
+ processor.addPage (page);
1663
+ let result: string = await processor.process (name, args);
1664
+
1665
+ return (result);
1666
+ }
1667
+
1668
+ /**
1669
+ * When the window has finished loading, execute the function.
1670
+ * This is meant for web browser use only.
1671
+ */
1672
+ static onReady (readyFunc: () => void): void
1673
+ {
1674
+ if ((document.readyState === "complete") || (document.readyState === "interactive"))
1675
+ readyFunc ();
1676
+ else
1677
+ window.addEventListener ("load", readyFunc);
1678
+ }
1679
+
1680
+ /**
1681
+ * Replace the current HTML page with the output.
1682
+ * This is meant for web browser use only.
1683
+ */
1684
+ static useOutput (output: string): void
1685
+ {
1686
+ document.open ();
1687
+ document.write (output);
1688
+ document.close ();
1689
+ }
1690
+
1691
+ /**
1692
+ * Wait for testers to load.
1693
+ *
1694
+ * @fixme This does not wait for ALL testers to finish loading. Only
1695
+ * the first one.
1696
+ */
1697
+ static async waitForTesters (): Promise<void>
1698
+ {
1699
+ while (HotStaq.isReadyForTesting === false)
1700
+ await HotStaq.wait (10);
1701
+
1702
+ if (HotStaq.onReadyForTesting != null)
1703
+ await HotStaq.onReadyForTesting ();
1704
+ }
1705
+
1706
+ /**
1707
+ * Process and replace the current HTML page with the hott script from the given url.
1708
+ * This is meant for web browser use only.
1709
+ */
1710
+ static async displayUrl (url: string | HotStartOptions, name: string = null,
1711
+ processor: HotStaq = null, args: any = null): Promise<HotStaq>
1712
+ {
1713
+ return (new Promise<HotStaq> ((resolve, reject) =>
1714
+ {
1715
+ HotStaq.onReady (async () =>
1716
+ {
1717
+ let options: HotStartOptions = {
1718
+ "url": ""
1719
+ };
1720
+
1721
+ if (name == null)
1722
+ {
1723
+ if (typeof (url) === "string")
1724
+ options.name = url;
1725
+ else
1726
+ options.name = url.name;
1727
+ }
1728
+ else
1729
+ options.name = name;
1730
+
1731
+ if (options.name === "")
1732
+ {
1733
+ if (typeof (url) === "string")
1734
+ options.name = url;
1735
+ else
1736
+ options.name = url.name;
1737
+ }
1738
+
1739
+ if (typeof (url) === "string")
1740
+ options.url = url;
1741
+ else
1742
+ {
1743
+ options.url = url.url;
1744
+
1745
+ if (processor == null)
1746
+ {
1747
+ if (url.processor != null)
1748
+ processor = url.processor;
1749
+ }
1750
+
1751
+ if (args == null)
1752
+ {
1753
+ if (url.args != null)
1754
+ args = url.args;
1755
+ }
1756
+
1757
+ if (url.testerMap != null)
1758
+ options.testerMap = url.testerMap;
1759
+
1760
+ if (url.testerName != null)
1761
+ options.testerName = url.testerName;
1762
+
1763
+ if (url.testerAPIBaseUrl != null)
1764
+ options.testerAPIBaseUrl = url.testerAPIBaseUrl;
1765
+ }
1766
+
1767
+ if (processor == null)
1768
+ processor = new HotStaq ();
1769
+
1770
+ if (processor.mode === DeveloperMode.Development)
1771
+ {
1772
+ if (processor.testerAPI == null)
1773
+ {
1774
+ if (options.testerAPIBaseUrl == null)
1775
+ options.testerAPIBaseUrl = "";
1776
+
1777
+ if (options.testerAPIBaseUrl === "")
1778
+ options.testerAPIBaseUrl = "http://127.0.0.1:8182";
1779
+
1780
+ let client: HotClient = new HotClient (processor);
1781
+ let testerAPI: HotTesterAPI = new HotTesterAPI (options.testerAPIBaseUrl, client);
1782
+ testerAPI.connection.api = testerAPI;
1783
+ processor.testerAPI = testerAPI;
1784
+ }
1785
+ }
1786
+
1787
+ options.processor = processor;
1788
+ options.args = args;
1789
+
1790
+ let output: string = await HotStaq.processUrl (options);
1791
+
1792
+ if (processor.mode === DeveloperMode.Development)
1793
+ {
1794
+ output +=
1795
+ `<script type = "text/javascript">
1796
+ function hotstaq_isDocumentReady ()
1797
+ {
1798
+ if (window["Hot"] != null)
1799
+ {
1800
+ if (Hot.Mode === HotStaqWeb.DeveloperMode.Development)
1801
+ {
1802
+ let func = function ()
1803
+ {
1804
+ if (Hot.TesterAPI != null)
1805
+ {
1806
+ let testPaths = {};
1807
+ let testElements = JSON.stringify (Hot.CurrentPage.testElements);
1808
+ let testMaps = JSON.stringify (Hot.CurrentPage.testMaps);
1809
+
1810
+ for (let key in Hot.CurrentPage.testPaths)
1811
+ {
1812
+ let testPath = Hot.CurrentPage.testPaths[key];
1813
+
1814
+ testPaths[key] = testPath.toString ();
1815
+ }
1816
+
1817
+ let testPathsStr = JSON.stringify (testPaths);
1818
+
1819
+ Hot.TesterAPI.tester.pageLoaded ({
1820
+ testerName: Hot.CurrentPage.testerName,
1821
+ testerMap: Hot.CurrentPage.testerMap,
1822
+ pageName: Hot.CurrentPage.name,
1823
+ testElements: testElements,
1824
+ testPaths: testPathsStr
1825
+ }).then (function (resp)
1826
+ {
1827
+ if (resp.error != null)
1828
+ {
1829
+ if (resp.error !== "")
1830
+ throw new Error (resp.error);
1831
+ }
1832
+
1833
+ HotStaqWeb.HotStaq.isReadyForTesting = true;
1834
+ });
1835
+ }
1836
+ };
1837
+
1838
+ if ((document.readyState === "complete") || (document.readyState === "interactive"))
1839
+ func ();
1840
+ else
1841
+ document.addEventListener ("DOMContentLoaded", func);
1842
+ }
1843
+ }
1844
+ }
1845
+
1846
+ hotstaq_isDocumentReady ();
1847
+ </script>`;
1848
+ }
1849
+
1850
+ HotStaq.useOutput (output);
1851
+ resolve (processor);
1852
+ });
1853
+ }));
1854
+ }
1855
+
1856
+ /**
1857
+ * Process and replace the current HTML page with the hott script.
1858
+ * This is meant for web browser use only.
1859
+ */
1860
+ static async displayContent (content: string, name: string, processor: HotStaq = null): Promise<HotStaq>
1861
+ {
1862
+ return (new Promise<HotStaq> ((resolve, reject) =>
1863
+ {
1864
+ HotStaq.onReady (async () =>
1865
+ {
1866
+ if (processor == null)
1867
+ processor = new HotStaq ();
1868
+
1869
+ let output: string = await HotStaq.processContent (processor, content, name);
1870
+
1871
+ HotStaq.useOutput (output);
1872
+ resolve (processor);
1873
+ });
1874
+ }));
1875
+ }
1876
+ }
1877
+