@jseeio/jsee 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/utils.js ADDED
@@ -0,0 +1,52 @@
1
+ function getModelFuncJS (model, target, log=console.log) {
2
+ switch (model.type) {
3
+ case 'class':
4
+ log('Init class')
5
+ const modelClass = new target()
6
+ return (...a) => {
7
+ return modelClass[model.method || 'predict'](...a)
8
+ }
9
+ case 'async-init':
10
+ // TODO: Test this
11
+ log('Function with async init')
12
+ return target().then(m => {
13
+ log('> Async init resolved: ', m)
14
+ return m
15
+ })
16
+ default:
17
+ log('Init function')
18
+ return target
19
+ }
20
+ }
21
+
22
+ function getModelFuncAPI (model, log=console.log) {
23
+ switch (model.type) {
24
+ case 'get':
25
+ return (data) => {
26
+ const query = new URLSearchParams(data).toString()
27
+ const finalURL = model.url +'?' + query
28
+ log('Sending GET request to:', finalURL)
29
+ const resPromise = fetch(finalURL)
30
+ .then(response => response.json())
31
+ return resPromise
32
+ }
33
+ case 'post':
34
+ return (data) => {
35
+ log('Sending POST request to', this.schema.model.url)
36
+ const resPromise = fetch(this.schema.model.url, {
37
+ method: 'POST',
38
+ headers: {
39
+ 'Accept': 'application/json',
40
+ 'Content-Type': 'application/json'
41
+ },
42
+ body: JSON.stringify(data)
43
+ }).then(response => response.json())
44
+ return resPromise
45
+ }
46
+ }
47
+ }
48
+
49
+ module.exports = {
50
+ getModelFuncJS,
51
+ getModelFuncAPI
52
+ }
package/src/worker.js CHANGED
@@ -1,11 +1,55 @@
1
+ const utils = require('./utils')
2
+
1
3
  function log () {
2
4
  const args = Array.prototype.slice.call(arguments);
3
5
  args.unshift('[Worker]')
4
- console.log(args)
5
6
  postMessage({_status: 'log', _log: args})
6
7
  }
7
8
 
9
+ function initTF (model) {
10
+ throw new Error('Tensorflow in worker (not implemented)')
11
+ }
12
+
13
+ function initPython (model) {
14
+ throw new Error('Python in worker (not implemented)')
15
+ }
16
+
17
+ function initJS (model) {
18
+ log('Init JS')
19
+ this.container = model.container
20
+
21
+ if (model.code) {
22
+ log('Load code as a string')
23
+ // https://github.com/altbdoor/blob-worker/blob/master/blobWorker.js
24
+ importScripts(URL.createObjectURL(new Blob([model.code], { type: 'text/javascript' })))
25
+ } else if (model.url) {
26
+ log('Load script from URL:', model.url)
27
+ importScripts(model.url)
28
+ } else {
29
+ log('No script provided')
30
+ }
31
+
32
+
33
+ // Related:
34
+ // https://stackoverflow.com/questions/37711603/javascript-es6-class-definition-not-accessible-in-window-global
35
+ const target = model.type === 'class'
36
+ ? eval(model.name)
37
+ : this[model.name]
38
+ // Need promise here in case of async init
39
+ Promise.resolve(utils.getModelFuncJS(model, target, log))
40
+ .then(m => {
41
+ postMessage({_status: 'loaded'})
42
+ this.modelFunc = m
43
+ })
44
+ }
45
+
46
+ function initAPI (model) {
47
+ log('Init API')
48
+ this.modelFunc = utils.getModelFuncAPI(model, log)
49
+ }
50
+
8
51
  onmessage = function (e) {
52
+
9
53
  var data = e.data
10
54
  log('Received message of type:', typeof data)
11
55
 
@@ -14,67 +58,30 @@ onmessage = function (e) {
14
58
  INIT MESSAGE
15
59
  */
16
60
  let model = data
61
+ log('Init...')
17
62
 
18
- if (model.type === 'py') {
19
- // Python with Pyodide
20
- importScripts('https://pyodide.cdn.iodide.io/pyodide.js')
21
- // Check when all's loaded
22
- /*
23
- TODO: Implement same loaded as port
24
- let pyCheck = setInterval(() => {
25
- if (self.pyodide && self.pyodide.runPythonAsync && this.model && this.model.length) {
26
- console.log('[Worker] Pyodide lib and model loaded. Try running to preload all imports')
27
- self.pyodide.runPythonAsync(this.model, () => {})
28
- .then((res) => {
29
- postMessage({_status: 'loaded'})
30
- })
31
- .catch((err) => {
32
- postMessage({_status: 'loaded'})
33
- })
34
- clearInterval(pyCheck)
35
- }
36
- }, 500)
37
- */
38
- } else {
39
- // Javascript
40
- this.container = model.container
41
-
42
- if (model.code) {
43
- log('Load code from schema')
44
- // https://github.com/altbdoor/blob-worker/blob/master/blobWorker.js
45
- importScripts(URL.createObjectURL(new Blob([model.code], { type: 'text/javascript' })))
46
- } else if (model.url) {
47
- log('Load script from URL:', model.url)
48
- importScripts(model.url)
49
- } else {
50
- log('No script provided')
51
- }
52
-
53
- if (model.type === 'class') {
54
- log('[Worker] Init class')
55
- // this.modelFunc = (new this[model.name]())[model.method || 'predict']
56
-
57
- const modelClass = new this[model.name]()
58
- this.modelFunc = (...a) => {
59
- return modelClass[model.method || 'predict'](...a)
60
- }
61
- } else if (model.type === 'async-init') {
62
- log('Init function with promise')
63
- log(this[model.name])
64
- this[model.name]().then((m) => {
65
- log('Async init resolved: ', m)
66
- this.modelFunc = m
67
- })
68
- } else {
69
- log('Init function')
70
- this.modelFunc = this[model.name]
71
- }
72
-
73
- postMessage({_status: 'loaded'})
63
+ switch (model.type) {
64
+ case 'tf':
65
+ initTF(model)
66
+ break
67
+ case 'py':
68
+ initPython(model)
69
+ break
70
+ case 'function':
71
+ case 'class':
72
+ case 'async-init':
73
+ case 'async-function':
74
+ initJS(model)
75
+ break
76
+ case 'get':
77
+ case 'post':
78
+ initAPI(model)
79
+ break
74
80
  }
75
81
  } else {
76
82
  /*
77
- CALL MESSAGE
83
+ :w
84
+ CALL MESSAGE
78
85
  */
79
86
  var res
80
87
  if (typeof this.modelFunc === 'string') {
package/test/code.html ADDED
@@ -0,0 +1,25 @@
1
+ <html>
2
+ <div id="jsee-container">
3
+ <script src="/dist/jsee.js"></script>
4
+ <script>
5
+ function sum (a, b) {
6
+ return a + b
7
+ }
8
+ new JSEE({
9
+ schema: {
10
+ "model": {
11
+ "name": "sum",
12
+ "type": "function",
13
+ "container": "args",
14
+ "code": sum,
15
+ "autorun": true,
16
+ "worker": false
17
+ },
18
+ "inputs": [
19
+ { "name": "a", "type": "int", "default": 100 },
20
+ { "name": "b", "type": "int", "default": 42 }
21
+ ]
22
+ }
23
+ })
24
+ </script>
25
+ </html>
@@ -0,0 +1,25 @@
1
+ <html>
2
+ <div id="jsee-container">
3
+ <script src="/dist/jsee.js"></script>
4
+ <script>
5
+ function sum (a, b) {
6
+ return a + b
7
+ }
8
+ new JSEE({
9
+ schema: {
10
+ "model": {
11
+ "name": "sum",
12
+ "type": "function",
13
+ "container": "args",
14
+ "code": sum,
15
+ "autorun": true,
16
+ "worker": true
17
+ },
18
+ "inputs": [
19
+ { "name": "a", "type": "int", "default": 100 },
20
+ { "name": "b", "type": "int", "default": 42 }
21
+ ]
22
+ }
23
+ })
24
+ </script>
25
+ </html>
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ class Doubler {
2
+ constructor() {
3
+ this.a = 2
4
+ }
5
+ double(b) {
6
+ return this.a * b
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ function sum (a, b) {
2
+ return a + b
3
+ }
@@ -0,0 +1,14 @@
1
+ <html>
2
+ <div id="jsee-container">
3
+ <script src="/dist/jsee.js"></script>
4
+ <script>
5
+ new JSEE({
6
+ schema: {
7
+ 'model': function (a, b) {
8
+ return a * b
9
+ }
10
+ },
11
+ container: 'jsee-container'
12
+ })
13
+ </script>
14
+ </html>
@@ -0,0 +1,26 @@
1
+ <html>
2
+ <div id="jsee-container">
3
+ <script src="/dist/jsee.js"></script>
4
+ <script>
5
+ new JSEE({
6
+ schema: {
7
+ "model": {
8
+ "name": "sum",
9
+ "type": "function",
10
+ "container": "args",
11
+ "code": `
12
+ function sum (a, b) {
13
+ return a + b
14
+ }
15
+ `,
16
+ "autorun": true,
17
+ "worker": false
18
+ },
19
+ "inputs": [
20
+ { "name": "a", "type": "int", "default": 100 },
21
+ { "name": "b", "type": "int", "default": 42 }
22
+ ]
23
+ }
24
+ })
25
+ </script>
26
+ </html>
@@ -0,0 +1,29 @@
1
+ <html>
2
+ <div id="jsee-container">
3
+ <script src="/dist/jsee.js"></script>
4
+ <script>
5
+
6
+ new JSEE({
7
+ schema: {
8
+ "model": {
9
+ "name": "sum",
10
+ "type": "function",
11
+ "container": "args",
12
+ "code": `
13
+ function sum (a, b) {
14
+ return a + b
15
+ }
16
+ `,
17
+ "autorun": true,
18
+ "worker": true
19
+ },
20
+ "inputs": [
21
+ { "name": "a", "type": "int", "default": 100 },
22
+ { "name": "b", "type": "int", "default": 42 }
23
+ ]
24
+ },
25
+ container: '#jsee-container',
26
+ verbose: false
27
+ })
28
+ </script>
29
+ </html>
@@ -0,0 +1,17 @@
1
+ {
2
+ "model": {
3
+ "name": "sum",
4
+ "type": "function",
5
+ "title": "title",
6
+ "description": "description",
7
+ "container": "args",
8
+ "url": "example-sum.js",
9
+ "autorun": true,
10
+ "worker": false
11
+ },
12
+ "inputs": [
13
+ { "name": "a", "type": "int", "default": 100 },
14
+ { "name": "b", "type": "int", "default": 42 }
15
+ ]
16
+ }
17
+
@@ -0,0 +1,17 @@
1
+ {
2
+ "model": {
3
+ "name": "sum",
4
+ "type": "function",
5
+ "title": "title",
6
+ "description": "description",
7
+ "container": "args",
8
+ "url": "example-sum.js",
9
+ "autorun": true,
10
+ "worker": true
11
+ },
12
+ "inputs": [
13
+ { "name": "a", "type": "int", "default": 100 },
14
+ { "name": "b", "type": "int", "default": 42 }
15
+ ]
16
+ }
17
+
package/test.js ADDED
@@ -0,0 +1,121 @@
1
+ require('expect-puppeteer')
2
+
3
+ page.setDefaultTimeout(1000)
4
+
5
+ const port = 8080
6
+ const urlSchema = (name) => `http://localhost:${port}/load.html?s=/test/${name}.schema.json`
7
+ const urlHTML = (name) => `http://localhost:${port}/test/${name}.html`
8
+ const urlQuery = (schema) => `http://localhost:${port}/load.html?s=${JSON.stringify(schema)}`
9
+
10
+ describe('Initial test', () => {
11
+ beforeAll(async () => {
12
+ await page.goto(urlSchema('sum'))
13
+ })
14
+ test('Title', async () => {
15
+ await expect(page).toMatch('title')
16
+ })
17
+ test('Description', async () => {
18
+ await expect(page).toMatch('description')
19
+ })
20
+ test('Run button is active', async () => {
21
+ await expect(page).toClick('button', { text: 'Run' })
22
+ })
23
+ test('Default result is right', async () => {
24
+ await expect(page).toMatch('142')
25
+ })
26
+ test('Changing inputs', async () => {
27
+ await expect(page).toFill('#a', '200')
28
+ await expect(page).toClick('button', { text: 'Run' })
29
+ await expect(page).toMatch('242')
30
+ // await jestPuppeteer.debug()
31
+ })
32
+ })
33
+
34
+
35
+ describe('Initial test (worker)', () => {
36
+ beforeAll(async () => {
37
+ await page.goto(urlSchema('sumw'))
38
+ })
39
+ test('Result is right', async () => {
40
+ await expect(page).toFill('#a', '8')
41
+ await expect(page).toFill('#b', '7')
42
+ await expect(page).toClick('button', { text: 'Run' })
43
+ await expect(page).toMatch('15')
44
+ })
45
+ })
46
+
47
+ describe('Some edge cases', () => {
48
+ const schema = {
49
+ 'model': {
50
+ 'code': 'function (a, b) { return a / b }', // TODO: check '+'
51
+ }
52
+ }
53
+ test('Minimal', async () => {
54
+ schema.model.worker = false
55
+ await page.goto(urlQuery(schema))
56
+ await expect(page).toFill('#a', '100')
57
+ await expect(page).toFill('#b', '4')
58
+ await expect(page).toClick('button', { text: 'Run' })
59
+ await expect(page).toMatch('25')
60
+ })
61
+ })
62
+
63
+ describe('Load code directly', () => {
64
+ test('Window', async () => {
65
+ await page.goto(urlHTML('code'))
66
+ await expect(page).toFill('#a', '8')
67
+ await expect(page).toFill('#b', '7')
68
+ await expect(page).toClick('button', { text: 'Run' })
69
+ await expect(page).toMatch('15')
70
+ })
71
+ test('Window (string with eval)', async () => {
72
+ await page.goto(urlHTML('string'))
73
+ await expect(page).toFill('#a', '8')
74
+ await expect(page).toFill('#b', '7')
75
+ await expect(page).toClick('button', { text: 'Run' })
76
+ await expect(page).toMatch('15')
77
+ })
78
+ test('Worker', async () => {
79
+ await page.goto(urlHTML('codew'))
80
+ await expect(page).toFill('#a', '8')
81
+ await expect(page).toFill('#b', '7')
82
+ await expect(page).toClick('button', { text: 'Run' })
83
+ await expect(page).toMatch('15')
84
+ })
85
+ test('Worker (string)', async () => {
86
+ await page.goto(urlHTML('stringw'))
87
+ await expect(page).toFill('#a', '8')
88
+ await expect(page).toFill('#b', '7')
89
+ await expect(page).toClick('button', { text: 'Run' })
90
+ await expect(page).toMatch('15')
91
+ })
92
+ })
93
+
94
+ describe('Classes', () => {
95
+ const schema = {
96
+ 'model': {
97
+ 'name': 'Doubler',
98
+ 'method': 'double',
99
+ 'type': 'class',
100
+ 'container': 'args',
101
+ 'url': '/test/example-class.js',
102
+ },
103
+ 'inputs': [
104
+ { 'name': 'b', 'type': 'int', 'default': 100 }
105
+ ]
106
+ }
107
+
108
+ test('Window', async () => {
109
+ schema.model.worker = false
110
+ await page.goto(urlQuery(schema))
111
+ await expect(page).toClick('button', { text: 'Run' })
112
+ await expect(page).toMatch('200')
113
+ })
114
+
115
+ test('Worker', async () => {
116
+ schema.model.worker = true
117
+ await page.goto(urlQuery(schema))
118
+ await expect(page).toClick('button', { text: 'Run' })
119
+ await expect(page).toMatch('200')
120
+ })
121
+ })
package/webpack.config.js CHANGED
@@ -87,7 +87,9 @@ module.exports = (env) => {
87
87
  },
88
88
  }
89
89
  })],
90
- }
90
+ },
91
+ // Source map
92
+ devtool: 'eval'
91
93
  }
92
94
 
93
95
  return config