@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/CHANGELOG.md +48 -0
- package/dist/jsee.js +1 -1
- package/dist/jsee.runtime.js +1 -1
- package/dist/worker_bundle.js +1 -0
- package/jest-puppeteer.config.js +12 -0
- package/jest.config.js +7 -0
- package/load.html +8 -4
- package/main.js +286 -170
- package/package.json +11 -6
- package/src/app.js +8 -6
- package/src/utils.js +52 -0
- package/src/worker.js +65 -58
- package/test/code.html +25 -0
- package/test/codew.html +25 -0
- package/test/example-async-function.js +0 -0
- package/test/example-async-init.js +0 -0
- package/test/example-class.js +8 -0
- package/test/example-sum.js +3 -0
- package/test/minimal.html +14 -0
- package/test/string.html +26 -0
- package/test/stringw.html +29 -0
- package/test/sum.schema.json +17 -0
- package/test/sumw.schema.json +17 -0
- package/test.js +121 -0
- package/webpack.config.js +3 -1
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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>
|
package/test/codew.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": 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
|
package/test/string.html
ADDED
|
@@ -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
|
+
})
|