worker.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. import * as base from '../source/base.js';
  2. import * as fs from 'fs/promises';
  3. import * as path from 'path';
  4. import * as process from 'process';
  5. import * as python from '../source/python.js';
  6. import * as tar from '../source/tar.js';
  7. import * as url from 'url';
  8. import * as view from '../source/view.js';
  9. import * as worker_threads from 'worker_threads';
  10. import * as zip from '../source/zip.js';
  11. const access = async (path) => {
  12. try {
  13. await fs.access(path);
  14. return true;
  15. } catch {
  16. return false;
  17. }
  18. };
  19. const dirname = (...args) => {
  20. const file = url.fileURLToPath(import.meta.url);
  21. const dir = path.dirname(file);
  22. return path.join(dir, ...args);
  23. };
  24. const decompress = (buffer) => {
  25. let archive = zip.Archive.open(buffer, 'gzip');
  26. if (archive && archive.entries.size === 1) {
  27. const stream = archive.entries.values().next().value;
  28. buffer = stream.peek();
  29. }
  30. const formats = [zip, tar];
  31. for (const module of formats) {
  32. archive = module.Archive.open(buffer);
  33. if (archive) {
  34. break;
  35. }
  36. }
  37. return archive;
  38. };
  39. const host = {};
  40. host.TestHost = class {
  41. constructor(window, environment) {
  42. this._window = window;
  43. this._environment = environment;
  44. this._errors = [];
  45. host.TestHost.source = host.TestHost.source || dirname('..', 'source');
  46. }
  47. async view(/* view */) {
  48. }
  49. async start() {
  50. }
  51. get window() {
  52. return this._window;
  53. }
  54. get document() {
  55. return this._window.document;
  56. }
  57. get errors() {
  58. return this._errors;
  59. }
  60. environment(name) {
  61. return this._environment[name];
  62. }
  63. screen(/* name */) {
  64. }
  65. async require(id) {
  66. const file = path.join(host.TestHost.source, `${id}.js`);
  67. return await import(`file://${file}`);
  68. }
  69. worker(id) {
  70. const file = path.join(host.TestHost.source, `${id}.js`);
  71. const worker = new worker_threads.Worker(file);
  72. worker.addEventListener = (type, listener) => {
  73. worker.on(type, (message) => listener({ data: message }));
  74. };
  75. return worker;
  76. }
  77. async request(file, encoding, basename) {
  78. const pathname = path.join(basename || host.TestHost.source, file);
  79. const exists = await access(pathname);
  80. if (!exists) {
  81. throw new Error(`The file '${file}' does not exist.`);
  82. }
  83. if (encoding) {
  84. return await fs.readFile(pathname, encoding);
  85. }
  86. const buffer = await fs.readFile(pathname, null);
  87. return new base.BinaryStream(buffer);
  88. }
  89. event(/* name, params */) {
  90. }
  91. exception(error /*, fatal */) {
  92. this._errors.push(error);
  93. }
  94. message() {
  95. }
  96. };
  97. host.TestHost.Context = class {
  98. constructor(host, folder, identifier, stream, entries) {
  99. this._host = host;
  100. this._folder = folder;
  101. this._identifier = identifier;
  102. this._stream = stream;
  103. this._entries = entries;
  104. }
  105. get identifier() {
  106. return this._identifier;
  107. }
  108. get stream() {
  109. return this._stream;
  110. }
  111. get entries() {
  112. return this._entries;
  113. }
  114. async request(file, encoding, base) {
  115. return this._host.request(file, encoding, base === undefined ? this._folder : base);
  116. }
  117. async require(id) {
  118. return this._host.require(id);
  119. }
  120. error(error, fatal) {
  121. this._host.exception(error, fatal);
  122. }
  123. };
  124. class CSSStyleDeclaration {
  125. constructor() {
  126. this._properties = new Map();
  127. }
  128. setProperty(name, value) {
  129. this._properties.set(name, value);
  130. }
  131. removeProperty(name) {
  132. this._properties.delete(name);
  133. }
  134. }
  135. class DOMTokenList {
  136. constructor(element) {
  137. this._element = element;
  138. }
  139. add(...tokens) {
  140. const value = this._element.getAttribute('class') || '';
  141. tokens = new Set(value.split(' ').concat(tokens));
  142. this._element.setAttribute('class', Array.from(tokens).join(' '));
  143. }
  144. contains(token) {
  145. const value = this._element.getAttribute('class');
  146. if (value === null || value.indexOf(token) === -1) {
  147. return false;
  148. }
  149. return value.split(' ').some((s) => s === token);
  150. }
  151. }
  152. class HTMLElement {
  153. constructor() {
  154. this._childNodes = [];
  155. this._attributes = new Map();
  156. this._style = new CSSStyleDeclaration();
  157. }
  158. get style() {
  159. return this._style;
  160. }
  161. get childNodes() {
  162. return this._childNodes;
  163. }
  164. get firstChild() {
  165. return this._childNodes.length > 0 ? this._childNodes[0] : null;
  166. }
  167. get lastChild() {
  168. const index = this._childNodes.length - 1;
  169. if (index >= 0) {
  170. return this._childNodes[index];
  171. }
  172. return null;
  173. }
  174. appendChild(node) {
  175. this._childNodes.push(node);
  176. }
  177. insertBefore(newNode, referenceNode) {
  178. const index = this._childNodes.indexOf(referenceNode);
  179. if (index !== -1) {
  180. this._childNodes.splice(index, 0, newNode);
  181. }
  182. }
  183. removeChild(node) {
  184. const index = this._childNodes.lastIndexOf(node);
  185. if (index !== -1) {
  186. this._childNodes.splice(index, 1);
  187. }
  188. }
  189. setAttribute(name, value) {
  190. this._attributes.set(name, value);
  191. }
  192. hasAttribute(name) {
  193. return this._attributes.has(name);
  194. }
  195. getAttribute(name) {
  196. return this._attributes.has(name) ? this._attributes.get(name) : null;
  197. }
  198. getElementsByClassName(name) {
  199. const elements = [];
  200. for (const node of this._childNodes) {
  201. if (node instanceof HTMLElement) {
  202. elements.push(...node.getElementsByClassName(name));
  203. if (node.classList.contains(name)) {
  204. elements.push(node);
  205. }
  206. }
  207. }
  208. return elements;
  209. }
  210. addEventListener(/* event, callback */) {
  211. }
  212. removeEventListener(/* event, callback */) {
  213. }
  214. get classList() {
  215. this._classList = this._classList || new DOMTokenList(this);
  216. return this._classList;
  217. }
  218. getBBox() {
  219. return { x: 0, y: 0, width: 10, height: 10 };
  220. }
  221. getBoundingClientRect() {
  222. return { left: 0, top: 0, wigth: 0, height: 0 };
  223. }
  224. scrollTo() {
  225. }
  226. focus() {
  227. }
  228. }
  229. class Document {
  230. constructor() {
  231. this._elements = {};
  232. this._documentElement = new HTMLElement();
  233. this._body = new HTMLElement();
  234. }
  235. get documentElement() {
  236. return this._documentElement;
  237. }
  238. get body() {
  239. return this._body;
  240. }
  241. createElement(/* name */) {
  242. return new HTMLElement();
  243. }
  244. createElementNS(/* namespace, name */) {
  245. return new HTMLElement();
  246. }
  247. createTextNode(/* text */) {
  248. return new HTMLElement();
  249. }
  250. getElementById(id) {
  251. let element = this._elements[id];
  252. if (!element) {
  253. element = new HTMLElement();
  254. this._elements[id] = element;
  255. }
  256. return element;
  257. }
  258. addEventListener(/* event, callback */) {
  259. }
  260. removeEventListener(/* event, callback */) {
  261. }
  262. }
  263. class Window {
  264. constructor() {
  265. this._document = new Document();
  266. }
  267. get document() {
  268. return this._document;
  269. }
  270. addEventListener(/* event, callback */) {
  271. }
  272. removeEventListener(/* event, callback */) {
  273. }
  274. }
  275. export class Target {
  276. constructor(item) {
  277. Object.assign(this, item);
  278. this.events = {};
  279. this.tags = new Set(this.tags);
  280. this.folder = item.type ? path.normalize(dirname('..', 'third_party' , 'test', item.type)) : process.cwd();
  281. this.assert = !this.assert || Array.isArray(this.assert) ? this.assert : [this.assert];
  282. }
  283. on(event, callback) {
  284. this.events[event] = this.events[event] || [];
  285. this.events[event].push(callback);
  286. }
  287. emit(event, data) {
  288. if (this.events && this.events[event]) {
  289. for (const callback of this.events[event]) {
  290. callback(this, data);
  291. }
  292. }
  293. }
  294. status(message) {
  295. this.emit('status', message);
  296. }
  297. async execute() {
  298. if (this.measures) {
  299. this.measures.set('name', this.name);
  300. }
  301. await zip.Archive.import();
  302. this.window = this.window || new Window();
  303. const environment = {
  304. zoom: 'none',
  305. measure: this.measures ? true : false
  306. };
  307. this.host = await new host.TestHost(this.window, environment);
  308. this.view = new view.View(this.host);
  309. this.view.options.attributes = true;
  310. this.view.options.initializers = true;
  311. const time = async (method) => {
  312. const start = process.hrtime.bigint();
  313. let err = null;
  314. try {
  315. await method.call(this);
  316. } catch (error) {
  317. err = error;
  318. }
  319. const duration = Number(process.hrtime.bigint() - start) / 1e9;
  320. if (this.measures) {
  321. this.measures.set(method.name, duration);
  322. }
  323. if (err) {
  324. throw err;
  325. }
  326. };
  327. this.status({ name: 'name', target: this.name });
  328. const errors = [];
  329. try {
  330. await time(this.download);
  331. await time(this.load);
  332. await time(this.validate);
  333. if (!this.tags.has('skip-render')) {
  334. await time(this.render);
  335. }
  336. } catch (error) {
  337. errors.push(error);
  338. }
  339. errors.push(...this.host.errors);
  340. if (errors.length === 0 && this.error) {
  341. throw new Error('Expected error.');
  342. }
  343. if (errors.length > 0 && (!this.error || errors.map((error) => error.message).join('\n') !== this.error)) {
  344. throw errors[0];
  345. }
  346. }
  347. async request(url, init) {
  348. const response = await fetch(url, init);
  349. if (!response.ok) {
  350. throw new Error(response.status.toString());
  351. }
  352. if (response.body) {
  353. const reader = response.body.getReader();
  354. const length = response.headers.has('Content-Length') ? parseInt(response.headers.get('Content-Length'), 10) : -1;
  355. let position = 0;
  356. const target = this;
  357. const stream = new ReadableStream({
  358. async start(controller) {
  359. const read = async () => {
  360. try {
  361. const result = await reader.read();
  362. if (result.done) {
  363. target.status({ name: 'download' });
  364. controller.close();
  365. } else {
  366. position += result.value.length;
  367. if (length >= 0) {
  368. const percent = position / length;
  369. target.status({ name: 'download', target: url, percent });
  370. } else {
  371. target.status({ name: 'download', target: url, position });
  372. }
  373. controller.enqueue(result.value);
  374. return await read();
  375. }
  376. } catch (error) {
  377. controller.error(error);
  378. throw error;
  379. }
  380. return null;
  381. };
  382. return read();
  383. }
  384. });
  385. return new Response(stream, {
  386. status: response.status,
  387. statusText: response.statusText,
  388. headers: response.headers
  389. });
  390. }
  391. return response;
  392. }
  393. async download(targets, sources) {
  394. targets = targets || Array.from(this.targets);
  395. sources = sources || this.source;
  396. const files = targets.map((file) => path.resolve(this.folder, file));
  397. const exists = await Promise.all(files.map((file) => access(file)));
  398. if (exists.every((value) => value)) {
  399. return;
  400. }
  401. if (!sources) {
  402. throw new Error('Download source not specified.');
  403. }
  404. let source = '';
  405. let sourceFiles = [];
  406. const match = sources.match(/^(.*?)\[(.*?)\](.*)$/);
  407. if (match) {
  408. [, source, sourceFiles, sources] = match;
  409. sourceFiles = sourceFiles.split(',').map((file) => file.trim());
  410. sources = sources && sources.startsWith(',') ? sources.substring(1).trim() : '';
  411. } else {
  412. const commaIndex = sources.indexOf(',');
  413. if (commaIndex === -1) {
  414. source = sources;
  415. sources = '';
  416. } else {
  417. source = sources.substring(0, commaIndex);
  418. sources = sources.substring(commaIndex + 1);
  419. }
  420. }
  421. await Promise.all(targets.map((target) => {
  422. const dir = path.dirname(`${this.folder}/${target}`);
  423. return fs.mkdir(dir, { recursive: true });
  424. }));
  425. const response = await this.request(source);
  426. const buffer = await response.arrayBuffer();
  427. const data = new Uint8Array(buffer);
  428. if (sourceFiles.length > 0) {
  429. this.status({ name: 'decompress' });
  430. const archive = decompress(data);
  431. for (const name of sourceFiles) {
  432. this.status({ name: 'write', target: name });
  433. if (name === '.') {
  434. const target = targets.shift();
  435. const dir = path.join(this.folder, target);
  436. /* eslint-disable no-await-in-loop */
  437. await fs.mkdir(dir, { recursive: true });
  438. /* eslint-enable no-await-in-loop */
  439. } else {
  440. const stream = archive.entries.get(name);
  441. if (!stream) {
  442. throw new Error(`Entry not found '${name}. Archive contains entries: ${JSON.stringify(Array.from(archive.entries.keys()))} .`);
  443. }
  444. const target = targets.shift();
  445. const buffer = stream.peek();
  446. const file = path.join(this.folder, target);
  447. /* eslint-disable no-await-in-loop */
  448. await fs.writeFile(file, buffer, null);
  449. /* eslint-enable no-await-in-loop */
  450. }
  451. }
  452. } else {
  453. const target = targets.shift();
  454. this.status({ name: 'write', target });
  455. await fs.writeFile(`${this.folder}/${target}`, data, null);
  456. }
  457. if (targets.length > 0 && sources.length > 0) {
  458. await this.download(targets, sources);
  459. }
  460. }
  461. async load() {
  462. const target = path.resolve(this.folder, this.targets[0]);
  463. const identifier = path.basename(target);
  464. const stat = await fs.stat(target);
  465. let context = null;
  466. if (stat.isFile()) {
  467. const buffer = await fs.readFile(target, null);
  468. const reader = new base.BinaryStream(buffer);
  469. const dirname = path.dirname(target);
  470. context = new host.TestHost.Context(this.host, dirname, identifier, reader, new Map());
  471. } else if (stat.isDirectory()) {
  472. const entries = new Map();
  473. const file = async (pathname) => {
  474. const buffer = await fs.readFile(pathname, null);
  475. const stream = new base.BinaryStream(buffer);
  476. const name = pathname.split(path.sep).join(path.posix.sep);
  477. entries.set(name, stream);
  478. };
  479. const walk = async (dir) => {
  480. const stats = await fs.readdir(dir, { withFileTypes: true });
  481. const promises = [];
  482. for (const stat of stats) {
  483. const pathname = path.join(dir, stat.name);
  484. if (stat.isDirectory()) {
  485. promises.push(walk(pathname));
  486. } else if (stat.isFile()) {
  487. promises.push(file(pathname));
  488. }
  489. }
  490. await Promise.all(promises);
  491. };
  492. await walk(target);
  493. context = new host.TestHost.Context(this.host, target, identifier, null, entries);
  494. }
  495. const modelFactoryService = new view.ModelFactoryService(this.host);
  496. this.model = await modelFactoryService.open(context);
  497. }
  498. async validate() {
  499. if (!this.model.format || (this.format && this.format !== this.model.format)) {
  500. throw new Error(`Invalid model format '${this.model.format}'.`);
  501. }
  502. if (this.producer && this.model.producer !== this.producer) {
  503. throw new Error(`Invalid producer '${this.model.producer}'.`);
  504. }
  505. if (this.runtime && this.model.runtime !== this.runtime) {
  506. throw new Error(`Invalid runtime '${this.model.runtime}'.`);
  507. }
  508. if (this.model.metadata && !Array.isArray(this.model.metadata) && this.model.metadata.every((argument) => argument.name && argument.value)) {
  509. throw new Error("Invalid model metadata.'");
  510. }
  511. if (this.assert) {
  512. for (const assert of this.assert) {
  513. const parts = assert.split('==').map((item) => item.trim());
  514. const properties = parts[0].split('.');
  515. const value = JSON.parse(parts[1].replace(/\s*'|'\s*/g, '"'));
  516. let context = { model: this.model };
  517. while (properties.length) {
  518. const property = properties.shift();
  519. if (context[property] !== undefined) {
  520. context = context[property];
  521. continue;
  522. }
  523. const match = /(.*)\[(.*)\]/.exec(property);
  524. if (match.length === 3 && context[match[1]] !== undefined) {
  525. const array = context[match[1]];
  526. const index = parseInt(match[2], 10);
  527. if (array[index] !== undefined) {
  528. context = array[index];
  529. continue;
  530. }
  531. }
  532. throw new Error(`Invalid property path: '${parts[0]}`);
  533. }
  534. if (context !== value) {
  535. throw new Error(`Invalid '${context}' != '${assert}'.`);
  536. }
  537. }
  538. }
  539. if (this.model.version || this.model.description || this.model.author || this.model.license) {
  540. // continue
  541. }
  542. /* eslint-disable no-unused-expressions */
  543. const validateGraph = (graph) => {
  544. const values = new Map();
  545. const validateValue = (value) => {
  546. value.name.toString();
  547. value.name.length;
  548. value.description;
  549. if (value.quantization) {
  550. if (!this.tags.has('quantization')) {
  551. throw new Error("Invalid 'quantization' tag.");
  552. }
  553. const quantization = new view.Quantization(value.quantization);
  554. quantization.toString();
  555. }
  556. if (value.type) {
  557. value.type.toString();
  558. }
  559. if (value.initializer) {
  560. value.initializer.type.toString();
  561. const tensor = new base.Tensor(value.initializer);
  562. if (!this.tags.has('skip-tensor-value')) {
  563. if (tensor.encoding !== '<' && tensor.encoding !== '>' && tensor.encoding !== '|') {
  564. throw new Error(`Tensor encoding '${tensor.encoding}' is not implemented.`);
  565. }
  566. if (tensor.layout && (tensor.layout !== 'sparse' && tensor.layout !== 'sparse.coo')) {
  567. throw new Error(`Tensor layout '${tensor.layout}' is not implemented.`);
  568. }
  569. if (!tensor.empty) {
  570. if (tensor.type && tensor.type.dataType === '?') {
  571. throw new Error('Tensor data type is not defined.');
  572. } else if (tensor.type && !tensor.type.shape) {
  573. throw new Error('Tensor shape is not defined.');
  574. } else {
  575. tensor.toString();
  576. if (this.tags.has('validation')) {
  577. const size = tensor.type.shape.dimensions.reduce((a, b) => a * b, 1);
  578. if (tensor.type && tensor.type.dataType !== '?' && size < 8192) {
  579. let data_type = '?';
  580. switch (tensor.type.dataType) {
  581. case 'boolean': data_type = 'bool'; break;
  582. case 'bfloat16': data_type = 'float32'; break;
  583. case 'float8e5m2': data_type = 'float16'; break;
  584. case 'float8e5m2fnuz': data_type = 'float16'; break;
  585. case 'float8e4m3fn': data_type = 'float16'; break;
  586. case 'float8e4m3fnuz': data_type = 'float16'; break;
  587. case 'int4': data_type = 'int8'; break;
  588. default: data_type = tensor.type.dataType; break;
  589. }
  590. Target.execution = Target.execution || new python.Execution();
  591. const execution = Target.execution;
  592. const bytes = execution.invoke('io.BytesIO', []);
  593. const dtype = execution.invoke('numpy.dtype', [data_type]);
  594. const array = execution.invoke('numpy.asarray', [tensor.value, dtype]);
  595. execution.invoke('numpy.save', [bytes, array]);
  596. }
  597. }
  598. }
  599. }
  600. }
  601. } else if (value.name.length === 0) {
  602. throw new Error('Empty value name.');
  603. }
  604. if (value.name.length > 0 && value.initializer === null) {
  605. if (!values.has(value.name)) {
  606. values.set(value.name, value);
  607. } else if (value !== values.get(value.name)) {
  608. throw new Error(`Duplicate value '${value.name}'.`);
  609. }
  610. }
  611. };
  612. const signatures = Array.isArray(graph.signatures) ? graph.signatures : [graph];
  613. for (const signature of signatures) {
  614. for (const input of signature.inputs) {
  615. input.name.toString();
  616. input.name.length;
  617. for (const value of input.value) {
  618. validateValue(value);
  619. }
  620. }
  621. for (const output of signature.outputs) {
  622. output.name.toString();
  623. output.name.length;
  624. for (const value of output.value) {
  625. validateValue(value);
  626. }
  627. }
  628. }
  629. if (graph.metadata && !Array.isArray(graph.metadata) && graph.metadata.every((argument) => argument.name && argument.value)) {
  630. throw new Error("Invalid graph metadata.'");
  631. }
  632. for (const node of graph.nodes) {
  633. const type = node.type;
  634. if (!type || typeof type.name !== 'string') {
  635. throw new Error(`Invalid node type '${JSON.stringify(node.type)}'.`);
  636. }
  637. if (Array.isArray(type.nodes)) {
  638. validateGraph(type);
  639. }
  640. view.Documentation.open(type);
  641. node.name.toString();
  642. node.description;
  643. if (node.metadata && !Array.isArray(node.metadata) && node.metadata.every((argument) => argument.name && argument.value)) {
  644. throw new Error("Invalid graph metadata.'");
  645. }
  646. const attributes = node.attributes;
  647. if (attributes) {
  648. for (const attribute of attributes) {
  649. attribute.name.toString();
  650. attribute.name.length;
  651. const type = attribute.type;
  652. const value = attribute.value;
  653. if ((type === 'graph' || type === 'function') && value && Array.isArray(value.nodes)) {
  654. validateGraph(value);
  655. } else {
  656. let text = new view.Formatter(attribute.value, attribute.type).toString();
  657. if (text && text.length > 1000) {
  658. text = `${text.substring(0, 1000)}...`;
  659. }
  660. /* value = */ text.split('<');
  661. }
  662. }
  663. }
  664. for (const input of node.inputs) {
  665. input.name.toString();
  666. input.name.length;
  667. if (!input.type || input.type.endsWith('*')) {
  668. for (const value of input.value) {
  669. validateValue(value);
  670. }
  671. if (this.tags.has('validation')) {
  672. if (input.value.length === 1 && input.value[0].initializer) {
  673. const sidebar = new view.TensorSidebar(this.view, input);
  674. sidebar.render();
  675. }
  676. }
  677. }
  678. }
  679. for (const output of node.outputs) {
  680. output.name.toString();
  681. output.name.length;
  682. if (!output.type || output.type.endsWith('*')) {
  683. for (const value of output.value) {
  684. validateValue(value);
  685. }
  686. }
  687. }
  688. if (node.chain) {
  689. for (const chain of node.chain) {
  690. chain.name.toString();
  691. chain.name.length;
  692. }
  693. }
  694. const sidebar = new view.NodeSidebar(this.view, node);
  695. sidebar.render();
  696. }
  697. const sidebar = new view.ModelSidebar(this.view, this.model, graph);
  698. sidebar.render();
  699. };
  700. /* eslint-enable no-unused-expressions */
  701. for (const graph of this.model.graphs) {
  702. /* eslint-disable no-await-in-loop */
  703. await validateGraph(graph);
  704. /* eslint-enable no-await-in-loop */
  705. }
  706. }
  707. async render() {
  708. for (const graph of this.model.graphs) {
  709. const signatures = Array.isArray(graph.signatures) && graph.signatures.length > 0 ? graph.signatures : [graph];
  710. for (const signature of signatures) {
  711. /* eslint-disable no-await-in-loop */
  712. await this.view.renderGraph(this.model, graph, signature, this.view.options);
  713. /* eslint-enable no-await-in-loop */
  714. }
  715. }
  716. }
  717. }
  718. if (!worker_threads.isMainThread) {
  719. worker_threads.parentPort.addEventListener('message', async (e) => {
  720. const message = e.data;
  721. const response = {};
  722. try {
  723. const target = new Target(message);
  724. response.type = 'complete';
  725. response.target = target.name;
  726. target.on('status', (_, message) => {
  727. message = { type: 'status', ...message };
  728. worker_threads.parentPort.postMessage(message);
  729. });
  730. if (message.measures) {
  731. target.measures = new Map();
  732. }
  733. await target.execute();
  734. response.measures = target.measures;
  735. } catch (error) {
  736. response.type = 'error';
  737. response.error = {
  738. name: error.name,
  739. message: error.message
  740. };
  741. const cause = error.cause;
  742. if (cause) {
  743. response.error.cause = {
  744. name: cause.name,
  745. message: cause.message
  746. };
  747. }
  748. }
  749. worker_threads.parentPort.postMessage(response);
  750. });
  751. }