@jseeio/jsee 0.2.3 → 0.2.7

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.
@@ -0,0 +1 @@
1
+ (()=>{var __webpack_modules__={555:()=>{eval("function getModelFuncJS(schema, target, log=console.log) {\n switch (schema.model.type) {\n case 'class':\n log('Init class')\n const modelClass = new target()\n return (...a) => {\n return modelClass[this.schema.model.method || 'predict'](...a)\n }\n case 'async-init':\n log('Function with async init')\n return target().then((m) => {\n log('Async init resolved: ', m)\n this.modelFunc = m\n })\n default:\n log('Init function')\n return target\n }\n}\n\n\n//# sourceURL=webpack://@jseeio/jsee/./src/utils.js?")},736:(__unused_webpack_module,__unused_webpack___webpack_exports__,__webpack_require__)=>{"use strict";eval("/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(555);\n/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_utils_js__WEBPACK_IMPORTED_MODULE_0__);\n\n\nconsole.log('!!!', _utils_js__WEBPACK_IMPORTED_MODULE_0__.getModelFuncJS)\n\nfunction log () {\n const args = Array.prototype.slice.call(arguments);\n args.unshift('[Worker]')\n console.log(args)\n postMessage({_status: 'log', _log: args})\n}\n\nfunction initTF (model) {\n throw new Error('Tensorflow in worker (not implemented)')\n}\n\nfunction initPython (model) {\n throw new Error('Python in worker (not implemented)')\n}\n\nfunction initJS (model) {\n this.container = model.container\n\n if (model.code) {\n log('Load code as a string')\n // https://github.com/altbdoor/blob-worker/blob/master/blobWorker.js\n importScripts(URL.createObjectURL(new Blob([model.code], { type: 'text/javascript' })))\n } else if (model.url) {\n log('Load script from URL:', model.url)\n importScripts(model.url)\n } else {\n log('No script provided')\n }\n\n this.modelFunc = (0,_utils_js__WEBPACK_IMPORTED_MODULE_0__.getModelFuncJS)(model, this[model.name], log)\n console.log('>>>', this.modelFunc)\n postMessage({_status: 'loaded'})\n}\n\nonmessage = function (e) {\n\n var data = e.data\n log('Received message of type:', typeof data)\n\n if ((typeof data === 'object') && ((data.url) || (data.code))) {\n /*\n INIT MESSAGE\n */\n let model = data\n log('Init...')\n\n switch (model.type) {\n case 'tf':\n initTF(model)\n break\n case 'py':\n initPython(model)\n break\n case 'function':\n case 'class':\n case 'async-init':\n case 'async-function':\n initJS.call(this, model)\n break\n }\n } else {\n /*\n :w\n CALL MESSAGE\n */\n var res\n if (typeof this.modelFunc === 'string') {\n // Python model:\n log('Calling Python model')\n /*\n const keys = Object.keys(data)\n for (let key of keys) {\n self[key] = data[key];\n }\n self.pyodide.runPythonAsync(this.model, () => {})\n .then((res) => {\n console.log('[Worker] Py results: ', typeof res, res)\n\t postMessage(res)\n })\n .catch((err) => {\n // self.postMessage({error : err.message});\n })\n */\n } else {\n // JavaScript model\n log('Calling JavaScript model')\n if (this.container === 'args') {\n log('Applying inputs as arguments')\n res = this.modelFunc.apply(null, data)\n } else {\n // JS object or array\n log('Applying inputs as object/array')\n res = this.modelFunc(data, log)\n }\n // Return promise value or just regular value\n // Promise.resolve handles both cases\n Promise.resolve(res).then(r => { postMessage(r) })\n }\n }\n}\n\n\n//# sourceURL=webpack://@jseeio/jsee/./src/worker.js?")}},__webpack_module_cache__={};function __webpack_require__(e){var n=__webpack_module_cache__[e];if(void 0!==n)return n.exports;var o=__webpack_module_cache__[e]={exports:{}};return __webpack_modules__[e](o,o.exports,__webpack_require__),o.exports}__webpack_require__.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(n,{a:n}),n},__webpack_require__.d=(e,n)=>{for(var o in n)__webpack_require__.o(n,o)&&!__webpack_require__.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:n[o]})},__webpack_require__.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n);var __webpack_exports__=__webpack_require__(736)})();
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ launch: {
3
+ dumpio: false, // dump browser errors to jest
4
+ headless: process.env.HEADLESS !== 'false',
5
+ product: 'chrome',
6
+ },
7
+ // server: {
8
+ // command: 'http-server',
9
+ // port: 8081,
10
+ // },
11
+ browserContext: 'default',
12
+ }
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ preset: 'jest-puppeteer',
3
+ verbose: true,
4
+ }
5
+
6
+ module.exports = config
7
+
@@ -0,0 +1,40 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>JSEE Schema Loader</title>
7
+ <style>
8
+ body {
9
+ font-family: sans-serif;
10
+ }
11
+ #jsee-container {
12
+ max-width: 1100px;
13
+ margin: auto;
14
+ }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div id="jsee-container"></div>
19
+ <script src="/dist/jsee.js" type="text/javascript"></script>
20
+ <script>window.JSEE || document.write('<script src="https://cdn.jsdelivr.net/npm/@jseeio/jsee@latest/dist/jsee.runtime.js">\x3C/script>')</script>
21
+ <script>window.JSEE || document.write('<script src="https://unpkg.com/@jseeio/jsee@latest/dist/jsee.runtime.js">\x3C/script>')</script>
22
+ <script>
23
+ var params = new URLSearchParams(window.location.search)
24
+ var schemaStr = params.get('s')
25
+ if (schemaStr) {
26
+ let schema
27
+ try {
28
+ schema = JSON.parse(schemaStr);
29
+ } catch (e) {
30
+ schema = schemaStr.includes('/') ? schemaStr : '/apps/' + schemaStr + '/schema.json'
31
+ }
32
+ console.log('[Loader] Schema:', schemaStr, schema)
33
+ const env = new JSEE({
34
+ container: document.getElementById('jsee-container'),
35
+ schema: schema
36
+ })
37
+ }
38
+ </script>
39
+ </body>
40
+ </html>
package/main.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { createVueApp } from './src/app'
2
2
  import Worker from './src/worker.js'
3
3
 
4
+ const utils = require('./src/utils')
5
+
4
6
  const { Notyf } = require('notyf')
5
7
  const notyf = new Notyf({
6
8
  types: [
@@ -25,8 +27,11 @@ require('notyf/notyf.min.css')
25
27
  const fetch = window['fetch']
26
28
  const Blob = window['Blob']
27
29
 
30
+ let verbose = true
28
31
  function log () {
29
- console.log(`[JSEE v${VERSION}]`, ...arguments)
32
+ if (verbose) {
33
+ console.log(`[JSEE v${VERSION}]`, ...arguments)
34
+ }
30
35
  }
31
36
 
32
37
  // const Worker = window['Worker']
@@ -42,6 +47,20 @@ function isObject (item) {
42
47
  return (typeof item === 'object' && !Array.isArray(item) && item !== null)
43
48
  }
44
49
 
50
+ function getName (code) {
51
+ switch (typeof code) {
52
+ case 'function':
53
+ return code.name
54
+ case 'string':
55
+ const words = code.split(' ')
56
+ const functionIndex = words.findIndex((word) => word == 'function')
57
+ const name = words[functionIndex + 1]
58
+ return name.includes('(') ? undefined : name
59
+ default:
60
+ return undefined
61
+ }
62
+ }
63
+
45
64
  // Return input value
46
65
  function getValue (input) {
47
66
  if (input.type === 'group') {
@@ -55,19 +74,85 @@ function getValue (input) {
55
74
  }
56
75
  }
57
76
 
77
+ function getModelType (model) {
78
+ if (model.code && typeof model.code === 'string' && model.code.split(' ').map(v => v.trim()).includes('def')) {
79
+ return 'py'
80
+ } else if (model.url) {
81
+ return 'post'
82
+ }
83
+ return 'function'
84
+ }
85
+
86
+ // Nice trick to get a function parameters by Jack Allan
87
+ // From: https://stackoverflow.com/a/9924463/2998960
88
+ const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg
89
+ const ARGUMENT_NAMES = /([^\s,]+)/g
90
+ function getParamNames (func) {
91
+ const fnStr = func.toString().replace(STRIP_COMMENTS, '')
92
+ let result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES)
93
+ if (result === null)
94
+ result = []
95
+ return result
96
+ }
97
+
98
+ function getInputs (model) {
99
+ if (model.code) {
100
+ const params = getParamNames(model.code)
101
+ return params.map(p => ({
102
+ 'name': p,
103
+ 'type': 'string'
104
+ }))
105
+ }
106
+ return []
107
+ }
108
+
109
+ function getFunctionContainer (target) {
110
+ // Check if the number of parameters is > 1, then 'args'
111
+ }
112
+
58
113
  export default class JSEE {
59
- constructor (params) {
60
- log('Initializing JSEE with parameters: ', params)
114
+ constructor (params, alt1, alt2) {
115
+
116
+ // Check if JSEE was initialized with args rather than with a params object
117
+ if (('model' in params) || (typeof params === 'string') || (typeof params === 'function') || !(typeof alt === 'undefined')) {
118
+ params = {
119
+ 'schema': params,
120
+ 'container': alt1,
121
+ 'verbose': alt2
122
+ }
123
+ }
124
+
125
+ // Set global verbose flag
126
+ verbose = !(params.verbose === false)
127
+
128
+ // Previous naming
61
129
  params.schema = params.schema || params.config
130
+
131
+ log('Initializing JSEE with parameters: ', params)
62
132
  this.params = params
63
133
  this.__version__ = VERSION
64
134
 
65
- // Get schema then initialize a model
66
- if (params.schema) {
67
- if (typeof params.schema === 'object') {
135
+ // Get schema then initialize a new environment
136
+ switch (typeof params.schema) {
137
+ case 'object':
68
138
  log('Received schema as object')
139
+ if (typeof params.schema.model === 'function') {
140
+ params.schema.model = {
141
+ code: params.schema.model
142
+ }
143
+ }
69
144
  this.init(params.schema)
70
- } else if (typeof params.schema === 'string') {
145
+ break
146
+ case 'function':
147
+ log('Received schema as function')
148
+ params.schema = {
149
+ model: {
150
+ code: params.schema,
151
+ }
152
+ }
153
+ this.init(params.schema)
154
+ break
155
+ case 'string':
71
156
  log('Received schema as string')
72
157
  this.schemaUrl = params.schema.indexOf('json') ? params.schema : params.schema + '.json'
73
158
  fetch(this.schemaUrl)
@@ -79,7 +164,10 @@ export default class JSEE {
79
164
  .catch((err) => {
80
165
  console.error(err)
81
166
  })
82
- }
167
+ break
168
+ default:
169
+ log('No schema provided')
170
+ notyf.error('No schema provided')
83
171
  }
84
172
  }
85
173
 
@@ -87,206 +175,141 @@ export default class JSEE {
87
175
  notyf.success(txt)
88
176
  }
89
177
 
90
- // Initialize model from schema
91
178
  init (schema) {
92
- log('Initializing schema', schema)
179
+ this.loadCode(schema).then((code) => { // -> code
180
+ this.initSchema(schema, code) // -> this.schema
181
+ this.initVue() // -> this.app, this.data
182
+ this.initWorker() // -> this.worker
183
+ this.initRender() // -> this.renderFunc
184
+ this.initModel() // -> this.modelFunc (depends on this.worker)
185
+ })
186
+ }
93
187
 
94
- // Convert JS code to string
95
- if (schema.model.code && (typeof schema.model.code !== 'string')) {
96
- log('Convert code in schema to string')
97
- schema.model.code = schema.model.code.toString()
98
- }
188
+ loadCode (schema) {
189
+ const initPromise = new Promise((resolve, reject) => {
190
+ // Unwind this ball of possible cases
191
+ let url = schema.model.url
192
+ if (url && (url.includes('.js') || url.includes('.py'))) {
193
+ // Update model URL if needed
194
+ if (!url.includes('/') && this.schemaUrl && this.schemaUrl.includes('/')) {
195
+ url = window.location.protocol + '//' + window.location.host + this.schemaUrl.split('/').slice(0, -1).join('/') + '/' + url
196
+ log(`Changed the old model URL to ${url} (based on the schema URL)`)
197
+ }
198
+ fetch(url)
199
+ .then(res => res.text())
200
+ .then(res => {
201
+ log('Loaded code from:', url)
202
+ resolve(res)
203
+ })
204
+ } else if (!(typeof schema.model.code === 'undefined')) {
205
+ log('Code is: schema.model.code')
206
+ resolve(schema.model.code)
207
+ } else {
208
+ log('No code. Probably API...')
209
+ resolve(undefined)
210
+ }
211
+ })
212
+
213
+ return initPromise
214
+ }
99
215
 
100
- // Update model URL if needed
101
- if (schema.model.url && !schema.model.url.includes('/') && this.schemaUrl && this.schemaUrl.includes('/')) {
102
- let oldModelUrl = schema.model.url
103
- log('Schema URL:', this.schemaUrl)
104
- schema.model.url = window.location.protocol + '//' + window.location.host + this.schemaUrl.split('/').slice(0, -1).join('/') + '/' + oldModelUrl
105
- log('Changed the old model URL to absolute one:', oldModelUrl, schema.model.url)
216
+ initSchema (schema, code) {
217
+ log('Initializing schema')
218
+
219
+ // Check for empty model block
220
+ if (typeof schema.model === 'undefined') {
221
+ schema.model = {}
106
222
  }
107
223
 
224
+ schema.model.code = code
225
+
226
+ // Check for super minimal config
108
227
  // Check for worker flag
109
228
  if (typeof schema.model.worker === 'undefined') {
110
229
  schema.model.worker = true
111
230
  }
112
231
 
113
232
  // Check inputs
233
+ // Relies on model.code
234
+ // So run after possible fetching
114
235
  if (typeof schema.inputs === 'undefined') {
115
- schema.inputs = []
236
+ schema.model.container = 'args'
237
+ schema.inputs = getInputs(schema.model)
238
+ }
239
+
240
+ // Relies on input check
241
+ // Set default input type
242
+ schema.inputs.forEach(input => {
243
+ if (typeof input.type === 'undefined') {
244
+ input.type = 'string'
245
+ }
246
+ })
247
+
248
+ // Infer model type
249
+ if (typeof schema.model.type === 'undefined') {
250
+ schema.model.type = getModelType(schema.model)
116
251
  }
117
252
 
118
- // Check if name is present, if not - get name from the file
119
- if (typeof schema.model.name === 'undefined') {
120
- // Two options here
121
- if (schema.model.url) {
122
- // 1. Get the name from the file name
253
+ // Update model name if absent
254
+ if (typeof schema.model.name === 'undefined'){
255
+ if ((schema.model.url) && (schema.model.url.includes('.js'))) {
123
256
  schema.model.name = schema.model.url.split('/').pop().split('.')[0]
124
- log('Use name from url: ', schema.model.name)
257
+ log('Use model name from url: ', schema.model.name)
125
258
  } else if (schema.model.code) {
126
- // 2. Get the name from the url
127
- schema.model.name = schema.model.code.name
128
- log('Use name from code: ', schema.model.name)
259
+ schema.model.name = getName(schema.model.code)
129
260
  }
130
261
  }
131
262
 
263
+ // At this point we have all code in model.code or api
132
264
  this.schema = clone(schema)
265
+ }
133
266
 
134
- // Init Vue app
135
- this.app = createVueApp(this, this.schema, (container) => {
267
+ initVue () {
268
+ log('Initializing VUE')
269
+ this.app = createVueApp(this, (container) => {
136
270
  // Called when the app is mounted
137
271
  // FYI "this" here refers to port object
138
272
  this.outputsContainer = container.querySelector('#outputs')
139
273
  this.inputsContainer = container.querySelector('#inputs')
140
274
  this.modelContainer = container.querySelector('#model')
141
-
142
275
  // Init overlay
143
276
  this.overlay = new Overlay(this.inputsContainer ? this.inputsContainer : this.outputsContainer)
144
- })
277
+ }, log)
145
278
  this.data = this.app.$data
279
+ }
146
280
 
147
- // Init Model
148
- // ----------
149
- if (this.schema.model.type === 'py') {
150
- // Add loading indicator
151
- this.overlay.show()
152
- let script = document.createElement('script')
153
- script.src = 'https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js'
154
- script.onload = async () => {
155
- this.pyodide = await loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/" });
156
- notyf.success('Loaded: Python')
157
- const resRaw = await fetch(this.schema.model.url)
158
- const res = await resRaw.text()
159
- this.pymodel = res
160
- log('Loaded python code:', res)
161
- // Check if micropip is used
162
- if (res.includes('micropip')) {
163
- await this.pyodide.loadPackage('micropip')
164
- log('Loaded micropip')
165
- }
166
- // Import packages if defined
167
- if ('packages' in this.schema.model) {
168
- await this.pyodide.loadPackage(this.schema.model.packages)
169
- log('Loaded packages from schema')
170
- } else {
171
- await this.pyodide.loadPackagesFromImports(res)
172
- log('Loaded packages from Python code')
173
- }
281
+ initWorker () {
282
+ if (this.schema.model.worker) {
283
+ log('Initializing Worker')
284
+ this.worker = new Worker()
285
+ this.worker.onmessage = (e) => {
174
286
  this.overlay.hide()
175
- }
176
- document.head.appendChild(script)
177
- } else if (['function', 'class', 'async-init', 'async-function'].includes(this.schema.model.type)) {
178
- // Initialize worker with the model
179
- if (this.schema.model.worker) {
180
- // this.worker = new Worker(new URL('./src/worker.js', import.meta.url))
181
- this.worker = new Worker()
182
- if (this.schema.model.url) {
183
- fetch(this.schema.model.url)
184
- .then(res => res.text())
185
- .then(res => {
186
- log('Loaded js code for worker')
187
- this.schema.model.code = res
188
- this.worker.postMessage(this.schema.model)
189
- })
190
- } else if (typeof this.schema.model.code !== 'undefined') {
191
- this.worker.postMessage(this.schema.model)
192
- } else {
193
- notyf.error('No code provided')
194
- }
195
-
196
- this.worker.onmessage = (e) => {
197
- this.overlay.hide()
198
- const res = e.data
199
- if ((typeof res === 'object') && (res._status)) {
200
- switch (res._status) {
201
- case 'loaded':
202
- notyf.success('Loaded: JS model (in worker)')
203
- break
204
- case 'log':
205
- log(...res._log)
206
- break
207
- }
208
- } else {
209
- log('Response from worker:', res)
210
- this.output(res)
211
- }
212
- }
213
- this.worker.onerror = (e) => {
214
- this.overlay.hide()
215
- notyf.error(e.message)
216
- log('Error from worker:', e)
217
- }
218
- } else {
219
- // Initialize model in main window
220
- log('Init model in window')
221
- let script = document.createElement('script')
222
- script.src = this.schema.model.url
223
- script.onload = () => {
224
- notyf.success('Loaded: JS model')
225
- this.overlay.hide()
226
- log('Loaded JS model in main window')
227
-
228
- // Initializing the model (same in worker)
229
- if (this.schema.model.type === 'class') {
230
- log('Init class')
231
- const modelClass = new window[this.schema.model.name]()
232
- this.modelFunc = (...a) => {
233
- return modelClass[this.schema.model.method || 'predict'](...a)
234
- }
235
- } else if (this.schema.model.type === 'async-init') {
236
- log('Init function with promise')
237
- window[this.schema.model.name]().then((m) => {
238
- log('Async init resolved: ', m)
239
- this.modelFunc = m
240
- })
241
- } else {
242
- log('Init function')
243
- this.modelFunc = window[this.schema.model.name]
287
+ const res = e.data
288
+ if ((typeof res === 'object') && (res._status)) {
289
+ switch (res._status) {
290
+ case 'loaded':
291
+ notyf.success('Loaded: JS model (in worker)')
292
+ break
293
+ case 'log':
294
+ log(...res._log)
295
+ break
244
296
  }
297
+ } else {
298
+ log('Response from worker:', res)
299
+ this.output(res)
245
300
  }
246
- document.head.appendChild(script)
247
301
  }
248
- } else if (this.schema.model.type === 'tf') {
249
- // Initialize TF
250
- let script = document.createElement('script')
251
- script.src = 'dist/tf.min.js'
252
- script.onload = () => {
253
- log('Loaded TF.js')
302
+ this.worker.onerror = (e) => {
254
303
  this.overlay.hide()
255
- window['tf'].loadLayersModel(this.schema.model.url).then(res => {
256
- log('Loaded Tensorflow model')
257
- })
258
- }
259
- document.head.appendChild(script)
260
- } else if (this.schema.model.type === 'get') {
261
- this.overlay.hide()
262
- this.modelFunc = (data) => {
263
- log('Sending GET request to', this.schema.model.url)
264
- const query = new window['URLSearchParams'](data).toString()
265
- log('Generated query string:', query)
266
- const resPromise = fetch(this.schema.model.url +'?' + query)
267
- .then(response => response.json())
268
- return resPromise
269
- }
270
- } else if (this.schema.model.type === 'post') {
271
- this.overlay.hide()
272
- this.modelFunc = (data) => {
273
- log('Sending POST request to', this.schema.model.url)
274
- const resPromise = fetch(this.schema.model.url, {
275
- method: 'POST',
276
- headers: {
277
- 'Accept': 'application/json',
278
- 'Content-Type': 'application/json'
279
- },
280
- body: JSON.stringify(data)
281
- }).then(response => response.json())
282
- return resPromise
304
+ notyf.error(e.message)
305
+ log('Error from worker:', e)
283
306
  }
284
307
  }
308
+ }
285
309
 
286
- // Init render
287
- // -----------
310
+ initRender () {
288
311
  if (this.schema.render && this.schema.render.url) {
289
- log('Init render in window')
312
+ log('Initializing a render function')
290
313
  let script = document.createElement('script')
291
314
  script.src = this.schema.render.url
292
315
  script.onload = () => {
@@ -315,6 +338,135 @@ export default class JSEE {
315
338
  }
316
339
  }
317
340
 
341
+ initModel () {
342
+ log('Initializing a model function')
343
+ switch (this.schema.model.type) {
344
+ case 'py':
345
+ this.initPython()
346
+ break
347
+ case 'tf':
348
+ this.initTF()
349
+ break
350
+ case 'function':
351
+ case 'class':
352
+ case 'async-init':
353
+ case 'async-function':
354
+ this.initJS()
355
+ break
356
+ case 'get':
357
+ case 'post':
358
+ this.initAPI()
359
+ break
360
+ default:
361
+ notyf.error('No type information')
362
+ break
363
+ }
364
+ }
365
+
366
+ initPython () {
367
+ // Add loading indicator
368
+ this.overlay.show()
369
+ let script = document.createElement('script')
370
+ script.src = 'https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js'
371
+ script.onload = async () => {
372
+ this.pyodide = await loadPyodide({ indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/" });
373
+ notyf.success('Loaded: Python')
374
+ log('Loaded python code:', res)
375
+ // Check if micropip is used
376
+ if (res.includes('micropip')) {
377
+ await this.pyodide.loadPackage('micropip')
378
+ log('Loaded micropip')
379
+ }
380
+ // Import packages if defined
381
+ if ('packages' in this.schema.model) {
382
+ await this.pyodide.loadPackage(this.schema.model.packages)
383
+ log('Loaded packages from schema')
384
+ } else {
385
+ await this.pyodide.loadPackagesFromImports(res)
386
+ log('Loaded packages from Python code')
387
+ }
388
+ this.overlay.hide()
389
+ }
390
+ document.head.appendChild(script)
391
+ }
392
+
393
+ initJS () {
394
+ // 1. String input <- loaded from url or code(string)
395
+ // 2. Target object (can be function, class or a function with async init <- code(object)
396
+ // 3. Model function
397
+
398
+ // We always start from 1 or 2
399
+ // For window execution we go: [1 ->] 2 -> 3
400
+ // For worker: [2 ->] 1 -> Worker
401
+
402
+ if (this.schema.model.worker) {
403
+ // Worker: Initialize worker with the model
404
+ // 2 -> 1
405
+ if (typeof this.schema.model.code === 'function') {
406
+ log('Convert code in schema to string for WebWorker')
407
+ this.schema.model.code = this.schema.model.code.toString()
408
+ }
409
+ // Wrap anonymous functions
410
+ if (!this.schema.model.name) {
411
+ this.schema.model.code = `function anon () { return (${this.schema.model.code})(...arguments) }`
412
+ this.schema.model.name = 'anon'
413
+ }
414
+ this.worker.postMessage(this.schema.model)
415
+ } else {
416
+ // Main: Initialize model in main window
417
+
418
+ // Target here represents raw JS object (e.g. class), not the final callable function
419
+ let target
420
+ if (typeof this.schema.model.code === 'string') {
421
+ // 1 -> 2
422
+ // Danger zone
423
+ if (this.schema.model.name) {
424
+ log('Evaluating code from string (has name)')
425
+ target = Function(
426
+ `${this.schema.model.code} ;return ${this.schema.model.name}`
427
+ )()
428
+ } else {
429
+ log('Evaluating code from string (no name)')
430
+ target = eval(`(${this.schema.model.code})`) // ( ͡° ͜ʖ ͡°) YEAHVAL
431
+ }
432
+ } else {
433
+ target = this.schema.model.code
434
+ }
435
+
436
+ // Need promise here in case of async init
437
+ Promise.resolve(utils.getModelFuncJS(this.schema.model, target, log))
438
+ .then(m => {
439
+ this.overlay.hide()
440
+ notyf.success('Loaded: JS model')
441
+ this.modelFunc = m
442
+ })
443
+ }
444
+ }
445
+
446
+ initAPI () {
447
+ this.overlay.hide()
448
+ if (this.schema.model.worker) {
449
+ // Worker:
450
+ this.worker.postMessage(this.schema.model)
451
+ } else {
452
+ // Main:
453
+ this.modelFunc = utils.getModelFuncAPI(model, log)
454
+ }
455
+ }
456
+
457
+ initTF () {
458
+ let script = document.createElement('script')
459
+ script.src = 'dist/tf.min.js'
460
+ script.onload = () => {
461
+ log('Loaded TF.js')
462
+ this.overlay.hide()
463
+ window['tf'].loadLayersModel(this.schema.model.url).then(res => {
464
+ log('Loaded Tensorflow model')
465
+ })
466
+ }
467
+ document.head.appendChild(script)
468
+ }
469
+
318
470
  run () {
319
471
  const schema = this.schema
320
472
  const data = this.data
@@ -326,7 +478,7 @@ export default class JSEE {
326
478
  log('Pass inputs as function arguments')
327
479
  inputValues = data.inputs.map(input => getValue(input))
328
480
  } else {
329
- log('Pass inputs in an object')
481
+ log('Pass inputs as object')
330
482
  inputValues = {}
331
483
  data.inputs.forEach(input => {
332
484
  if (input.name) {
@@ -346,7 +498,7 @@ export default class JSEE {
346
498
  data.inputs.forEach(input => {
347
499
  this.pyodide.globals.set(input.name, input.value);
348
500
  })
349
- this.pyodide.runPythonAsync(this.pymodel, () => {})
501
+ this.pyodide.runPythonAsync(this.schema.model.code, () => {})
350
502
  .then((res) => {
351
503
  if (schema.outputs && schema.outputs.length) {
352
504
  const resultObj = {}
@@ -385,8 +537,6 @@ export default class JSEE {
385
537
  Promise.resolve(res).then(r => { this.output(r) })
386
538
  }
387
539
  break
388
- case 'api':
389
- break
390
540
  }
391
541
  }
392
542