@muze-nl/simplystore 0.3.3 → 0.3.5
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/.eslintrc.cjs +15 -0
- package/package.json +3 -2
- package/src/server.mjs +24 -23
- package/src/worker-command-init.mjs +11 -0
- package/src/worker-command.mjs +51 -0
- package/src/worker-query-init.mjs +14 -0
- package/src/worker-query.mjs +146 -0
- package/.npmignore~ +0 -8
- package/package.json~ +0 -32
- package/src/worker-command.js +0 -49
- package/src/worker-query.js +0 -148
package/.eslintrc.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muze-nl/simplystore",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"main": "src/server.mjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
"express": "^4.18.1",
|
|
23
23
|
"json-pointer": "^0.6.2",
|
|
24
24
|
"jsonpath-plus": "^7.2.0",
|
|
25
|
-
"
|
|
25
|
+
"piscina": "^4.1.0",
|
|
26
26
|
"vm2": "^3.9.13"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"eslint": "^8.48.0",
|
|
29
30
|
"process": "^0.11.10",
|
|
30
31
|
"tap": "^16.3.8"
|
|
31
32
|
}
|
package/src/server.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import fs from 'fs'
|
|
|
3
3
|
import JSONTag from '@muze-nl/jsontag'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import path from 'path'
|
|
6
|
-
import { spawn, Pool, Worker } from 'threads'
|
|
7
6
|
import commands from './commands.mjs'
|
|
8
7
|
import {appendFile} from './util.mjs'
|
|
8
|
+
import {Piscina} from 'piscina'
|
|
9
9
|
|
|
10
10
|
const __dirname = path.dirname(path.dirname(fileURLToPath(import.meta.url)))
|
|
11
11
|
|
|
@@ -15,29 +15,32 @@ async function main(options) {
|
|
|
15
15
|
if (!options) {
|
|
16
16
|
options = {}
|
|
17
17
|
}
|
|
18
|
-
const port
|
|
19
|
-
const datafile
|
|
20
|
-
const wwwroot
|
|
21
|
-
const commandLog
|
|
22
|
-
|
|
18
|
+
const port = options.port || 3000
|
|
19
|
+
const datafile = options.datafile || './data.jsontag'
|
|
20
|
+
const wwwroot = options.wwwroot || __dirname+'/www'
|
|
21
|
+
const commandLog = options.commandlog || './commandlog.jsontag'
|
|
22
|
+
const queryWorker = options.queryWorker || __dirname+'/src/worker-query-init.mjs'
|
|
23
|
+
const commandWorker = options.commandWorker || __dirname+'/src/worker-command-init.mjs'
|
|
23
24
|
|
|
24
|
-
let jsontag
|
|
25
|
+
let jsontag = fs.readFileSync(datafile, 'utf-8')
|
|
25
26
|
|
|
26
27
|
function initWorkerPool(workerName, size=null) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
let options = {
|
|
29
|
+
filename: workerName,
|
|
30
|
+
workerData: jsontag
|
|
31
|
+
}
|
|
32
|
+
if (size) {
|
|
33
|
+
options.maxThreads = size
|
|
34
|
+
}
|
|
35
|
+
return new Piscina(options)
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
let queryWorkerpool = initWorkerPool(
|
|
38
|
+
let queryWorkerpool = initWorkerPool(queryWorker)
|
|
36
39
|
|
|
37
|
-
const commandWorkerpool = initWorkerPool(
|
|
40
|
+
// const commandWorkerpool = initWorkerPool(commandWorker,1) // only one update worker so no changes can get lost
|
|
38
41
|
|
|
39
42
|
server.get('/', (req,res) => {
|
|
40
|
-
res.send('<h1>SimplyStore</h1>')
|
|
43
|
+
res.send('<h1>SimplyStore</h1>') //@TODO: implement something nice
|
|
41
44
|
})
|
|
42
45
|
|
|
43
46
|
server.use(express.static(wwwroot))
|
|
@@ -114,9 +117,8 @@ async function main(options) {
|
|
|
114
117
|
query: req.query,
|
|
115
118
|
jsontag: req.accepts('application/jsontag')
|
|
116
119
|
}
|
|
117
|
-
queryWorkerpool.
|
|
118
|
-
|
|
119
|
-
}).then(response => {
|
|
120
|
+
queryWorkerpool.run({pointer:path, request})
|
|
121
|
+
.then(response => {
|
|
120
122
|
sendResponse(response, res)
|
|
121
123
|
let end = Date.now()
|
|
122
124
|
console.log(path, (end-start), process.memoryUsage())
|
|
@@ -141,9 +143,8 @@ async function main(options) {
|
|
|
141
143
|
query: req.query,
|
|
142
144
|
jsontag: req.accepts('application/jsontag')
|
|
143
145
|
}
|
|
144
|
-
queryWorkerpool.
|
|
145
|
-
|
|
146
|
-
}).then(response => {
|
|
146
|
+
queryWorkerpool.run({pointer:path, request, query})
|
|
147
|
+
.then(response => {
|
|
147
148
|
sendResponse(response, res)
|
|
148
149
|
let end = Date.now()
|
|
149
150
|
console.log(path, (end-start), process.memoryUsage())
|
|
@@ -215,7 +216,7 @@ async function main(options) {
|
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
commandWorkerpool
|
|
218
|
-
.
|
|
219
|
+
.run({request, commandStr})
|
|
219
220
|
.then(response => {
|
|
220
221
|
//@TODO store response status, if response.code => error
|
|
221
222
|
if (!response.code) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as commandWorker from './worker-command.mjs'
|
|
2
|
+
import worker_threads from 'node:worker_threads'
|
|
3
|
+
|
|
4
|
+
async function initialize() {
|
|
5
|
+
let meta = {}
|
|
6
|
+
let dataspace = JSONTag.parse(worker_threads.workerData, null, meta)
|
|
7
|
+
await commandWorker.initialize(worker_threads.workerData)
|
|
8
|
+
return commandWorker.runCommand
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default initialize()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import JSONTag from '@muze-nl/jsontag'
|
|
2
|
+
import commands from './commands.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Command Worker for threads.js library
|
|
6
|
+
* returns JSONTag strings, since otherwise JSON.stringify is used
|
|
7
|
+
* and type+attribute data gets lost
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
let dataspace
|
|
11
|
+
|
|
12
|
+
export function setDataspace(d) {
|
|
13
|
+
dataspace = d
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @TODO: check for valid command id
|
|
18
|
+
* @TODO: write valid commands to command log, emit 'ok', check in server.mjs for that
|
|
19
|
+
* @TODO: setTimeout or do above checks in server.mjs before queueing
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export function async initialize(jsontag) {
|
|
23
|
+
if (!jsontag) { throw new Error('missing jsontag parameter')}
|
|
24
|
+
dataspace = jsontag
|
|
25
|
+
console.log('initialized command worker thread')
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function runCommand({request, commandStr}) {
|
|
30
|
+
if (!commandStr) { throw new Error('missing command parameter')}
|
|
31
|
+
if (!request) { throw new Error('missing request parameter')}
|
|
32
|
+
let response = {
|
|
33
|
+
jsontag: true
|
|
34
|
+
}
|
|
35
|
+
let command = JSONTag.parse(commandStr) // raw body through express.raw()
|
|
36
|
+
if (command && command.name && commands[command.name]) {
|
|
37
|
+
try {
|
|
38
|
+
commands[command.name](dataspace, command, request)
|
|
39
|
+
response.body = JSONTag.stringify(dataspace) //@TODO: this is inefficient, patch would be better
|
|
40
|
+
} catch(err) {
|
|
41
|
+
console.log(err)
|
|
42
|
+
response.code = 422;
|
|
43
|
+
response.body = '<object class="Error">{"message":'+JSON.stringify(''+err)+',"code":422}'
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
response.code = 404
|
|
47
|
+
response.body = '<object class="Error">{"message":"Command '+command.name+' not found","code":404}'
|
|
48
|
+
}
|
|
49
|
+
return response
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import JSONTag from '@muze-nl/jsontag';
|
|
2
|
+
import worker_threads from 'node:worker_threads';
|
|
3
|
+
import * as queryWorker from './worker-query.mjs';
|
|
4
|
+
|
|
5
|
+
async function initialize() {
|
|
6
|
+
let meta = {}
|
|
7
|
+
let dataspace = JSONTag.parse(worker_threads.workerData, null, meta)
|
|
8
|
+
//console.log('starting')
|
|
9
|
+
await queryWorker.initialize(dataspace,meta)
|
|
10
|
+
//console.log('initialized')
|
|
11
|
+
return queryWorker.runQuery
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default initialize()
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import JSONTag from "@muze-nl/jsontag"
|
|
2
|
+
import pointer from 'json-pointer'
|
|
3
|
+
import {_,from,not,anyOf,allOf} from 'array-where-select'
|
|
4
|
+
import {deepFreeze} from './util.mjs'
|
|
5
|
+
import {VM} from 'vm2'
|
|
6
|
+
|
|
7
|
+
let dataspace, meta = {};
|
|
8
|
+
|
|
9
|
+
export function setDataspace(d, m) {
|
|
10
|
+
dataspace = d
|
|
11
|
+
if(m) {
|
|
12
|
+
meta = m
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getDataSpace(path, dataspace) {
|
|
17
|
+
if (path.substring(path.length-1)==='/') {
|
|
18
|
+
//jsonpointer doesn't allow a trailing '/'
|
|
19
|
+
path = path.substring(0, path.length-1)
|
|
20
|
+
}
|
|
21
|
+
let result
|
|
22
|
+
if (path) {
|
|
23
|
+
//jsonpointer doesn't allow an empty pointer
|
|
24
|
+
try {
|
|
25
|
+
if (pointer.has(dataspace, path)) {
|
|
26
|
+
result = pointer.get(dataspace, path)
|
|
27
|
+
} else {
|
|
28
|
+
result = JSONTag.parse('<object class="Error">{"message":"Not found", "code":404}')
|
|
29
|
+
}
|
|
30
|
+
} catch(err) {
|
|
31
|
+
result = JSONTag.parse('<object class="Error">{"message":'+JSON.stringify(err.message)+', "code":500}')
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
result = dataspace
|
|
35
|
+
}
|
|
36
|
+
return [result,path]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function linkReplacer(data, baseURL) {
|
|
40
|
+
let type = JSONTag.getType(data)
|
|
41
|
+
let attributes = JSONTag.getAttributes(data)
|
|
42
|
+
if (Array.isArray(data)) {
|
|
43
|
+
data = data.map((entry,index) => {
|
|
44
|
+
return linkReplacer(data[index], baseURL+index+'/')
|
|
45
|
+
})
|
|
46
|
+
} else if (type === 'link') {
|
|
47
|
+
// do nothing
|
|
48
|
+
} else if (data && typeof data === 'object') {
|
|
49
|
+
data = JSONTag.clone(data)
|
|
50
|
+
Object.keys(data).forEach(key => {
|
|
51
|
+
if (Array.isArray(data[key])) {
|
|
52
|
+
data[key] = new JSONTag.Link(baseURL+key+'/')
|
|
53
|
+
} else if (data[key] && typeof data[key] === 'object') {
|
|
54
|
+
if (JSONTag.getType(data[key])!=='link') {
|
|
55
|
+
let id=JSONTag.getAttribute(data[key], 'id')
|
|
56
|
+
if (!id) {
|
|
57
|
+
id = baseURL+key+'/'
|
|
58
|
+
}
|
|
59
|
+
data[key] = new JSONTag.Link(id)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
return data
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//@TODO: emit console events that server.mjs picks up
|
|
68
|
+
function connectConsole(res) {
|
|
69
|
+
return {
|
|
70
|
+
log: function(...args) {
|
|
71
|
+
// res.append('X-Console-Log', joinArgs(args))
|
|
72
|
+
},
|
|
73
|
+
warning: function(...args) {
|
|
74
|
+
// res.append('X-Console-Warning', joinArgs(args))
|
|
75
|
+
},
|
|
76
|
+
error: function(...args) {
|
|
77
|
+
// res.append('X-Console-Error', joinArgs(args))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function initialize(jsontag, m) {
|
|
83
|
+
if (!jsontag) { throw new Error('missing jsontag parameter')}
|
|
84
|
+
dataspace = jsontag
|
|
85
|
+
meta = m
|
|
86
|
+
deepFreeze(dataspace)
|
|
87
|
+
return true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function runQuery({pointer, request, query}) {
|
|
91
|
+
if (!pointer) { throw new Error('missing pointer parameter')}
|
|
92
|
+
if (!request) { throw new Error('missing request parameter')}
|
|
93
|
+
console.log('query',pointer)
|
|
94
|
+
let response = {
|
|
95
|
+
jsontag: request.jsontag
|
|
96
|
+
}
|
|
97
|
+
let [result,path] = getDataSpace(pointer, dataspace)
|
|
98
|
+
|
|
99
|
+
if (query) {
|
|
100
|
+
// @todo add text search: https://github.com/nextapps-de/flexsearch
|
|
101
|
+
// @todo add tree walk map/reduce/find/filter style functions
|
|
102
|
+
// @todo add arc tree dive function?
|
|
103
|
+
// @todo replace VM with V8 isolate
|
|
104
|
+
const vm = new VM({
|
|
105
|
+
timeout: 1000,
|
|
106
|
+
allowAsync: false,
|
|
107
|
+
sandbox: {
|
|
108
|
+
root: dataspace, //@TODO: if we don't pass the root, we can later shard
|
|
109
|
+
data: result,
|
|
110
|
+
meta,
|
|
111
|
+
_,
|
|
112
|
+
from,
|
|
113
|
+
not,
|
|
114
|
+
anyOf,
|
|
115
|
+
allOf,
|
|
116
|
+
// console: connectConsole(res),
|
|
117
|
+
JSONTag,
|
|
118
|
+
request
|
|
119
|
+
},
|
|
120
|
+
wasm: false
|
|
121
|
+
})
|
|
122
|
+
try {
|
|
123
|
+
result = vm.run(query)
|
|
124
|
+
let used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
125
|
+
console.log(`(${used} MB)`);
|
|
126
|
+
} catch(err) {
|
|
127
|
+
console.log(err)
|
|
128
|
+
response.code = 422;
|
|
129
|
+
if (request.jsontag) {
|
|
130
|
+
response.body = '<object class="Error">{"message":'+JSON.stringify(''+err)+',"code":422}'
|
|
131
|
+
} else {
|
|
132
|
+
response.body = JSON.stringify({message:err, code: 422})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
result = linkReplacer(result, path+'/')
|
|
137
|
+
}
|
|
138
|
+
if (!response.code) {
|
|
139
|
+
if (response.jsontag) {
|
|
140
|
+
response.body = JSONTag.stringify(result)
|
|
141
|
+
} else {
|
|
142
|
+
response.body = JSON.stringify(result)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return response
|
|
146
|
+
}
|
package/.npmignore~
DELETED
package/package.json~
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@muze-nl/simplystore",
|
|
3
|
-
"version": "0.3.2",
|
|
4
|
-
"main": "src/server.mjs",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node src/run.mjs",
|
|
8
|
-
"test": "tap test/*.mjs"
|
|
9
|
-
},
|
|
10
|
-
"author": "auke@muze.nl",
|
|
11
|
-
"license": "MIT",
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/simplyedit/simplystore.git"
|
|
15
|
-
},
|
|
16
|
-
"bugs": "https://github.com/simplyedit/simplystore/issues",
|
|
17
|
-
"homepage": "https://github.com/simplyedit/simplystore#readme",
|
|
18
|
-
"dependencies": {
|
|
19
|
-
"@muze-nl/jsontag": "^0.8.5",
|
|
20
|
-
"array-where-select": "^0.3.1",
|
|
21
|
-
"codemirror": "^6.0.1",
|
|
22
|
-
"express": "^4.18.1",
|
|
23
|
-
"json-pointer": "^0.6.2",
|
|
24
|
-
"jsonpath-plus": "^7.2.0",
|
|
25
|
-
"threads": "^1.7.0",
|
|
26
|
-
"vm2": "^3.9.13"
|
|
27
|
-
},
|
|
28
|
-
"devDependencies": {
|
|
29
|
-
"process": "^0.11.10",
|
|
30
|
-
"tap": "^16.3.8"
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/worker-command.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {expose} from 'threads/worker'
|
|
2
|
-
import JSONTag from '@muze-nl/jsontag'
|
|
3
|
-
import commands from './commands.mjs'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Command Worker for threads.js library
|
|
7
|
-
* returns JSONTag strings, since otherwise JSON.stringify is used
|
|
8
|
-
* and type+attribute data gets lost
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
let dataspace
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @TODO: check for valid command id
|
|
15
|
-
* @TODO: write valid commands to command log, emit 'ok', check in server.mjs for that
|
|
16
|
-
* @TODO: setTimeout or do above checks in server.mjs before queueing
|
|
17
|
-
*/
|
|
18
|
-
const command = {
|
|
19
|
-
initialize(jsontag) {
|
|
20
|
-
if (!jsontag) { throw new Error('missing jsontag parameter')}
|
|
21
|
-
dataspace = JSONTag.parse(jsontag)
|
|
22
|
-
console.log('initialized command worker thread')
|
|
23
|
-
return true
|
|
24
|
-
},
|
|
25
|
-
runCommand(request, commandStr) {
|
|
26
|
-
if (!commandStr) { throw new Error('missing command parameter')}
|
|
27
|
-
if (!request) { throw new Error('missing request parameter')}
|
|
28
|
-
let response = {
|
|
29
|
-
jsontag: true
|
|
30
|
-
}
|
|
31
|
-
let command = JSONTag.parse(commandStr) // raw body through express.raw()
|
|
32
|
-
if (command && command.name && commands[command.name]) {
|
|
33
|
-
try {
|
|
34
|
-
commands[command.name](dataspace, command, request)
|
|
35
|
-
response.body = JSONTag.stringify(dataspace) //@TODO: this is inefficient, patch would be better
|
|
36
|
-
} catch(err) {
|
|
37
|
-
console.log(err)
|
|
38
|
-
response.code = 422;
|
|
39
|
-
response.body = '<object class="Error">{"message":'+JSON.stringify(''+err)+',"code":422}'
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
response.code = 404
|
|
43
|
-
response.body = '<object class="Error">{"message":"Command '+command.name+' not found","code":404}'
|
|
44
|
-
}
|
|
45
|
-
return response
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
expose(command)
|
package/src/worker-query.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import {expose} from "threads/worker"
|
|
2
|
-
import JSONTag from "@muze-nl/jsontag"
|
|
3
|
-
import pointer from 'json-pointer'
|
|
4
|
-
import {_,from,not,anyOf,allOf} from 'array-where-select'
|
|
5
|
-
import {deepFreeze} from './util.mjs'
|
|
6
|
-
import {VM} from 'vm2'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Query Worker for threads.js library
|
|
10
|
-
* returns JSONTag strings, since otherwise JSON.stringify is used
|
|
11
|
-
* and type+attribute data gets lost
|
|
12
|
-
* perhaps return [result,err] instead, so errors can be detected?
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
let dataspace, meta = {};
|
|
16
|
-
|
|
17
|
-
function getDataSpace(path, dataspace) {
|
|
18
|
-
if (path.substring(path.length-1)==='/') {
|
|
19
|
-
//jsonpointer doesn't allow a trailing '/'
|
|
20
|
-
path = path.substring(0, path.length-1)
|
|
21
|
-
}
|
|
22
|
-
let result
|
|
23
|
-
if (path) {
|
|
24
|
-
//jsonpointer doesn't allow an empty pointer
|
|
25
|
-
try {
|
|
26
|
-
if (pointer.has(dataspace, path)) {
|
|
27
|
-
result = pointer.get(dataspace, path)
|
|
28
|
-
} else {
|
|
29
|
-
result = JSONTag.parse('<object class="Error">{"message":"Not found", "code":404}')
|
|
30
|
-
}
|
|
31
|
-
} catch(err) {
|
|
32
|
-
result = JSONTag.parse('<object class="Error">{"message":'+originalJSON.stringify(err.message)+', "code":500}')
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
result = dataspace
|
|
36
|
-
}
|
|
37
|
-
return [result,path]
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function linkReplacer(data, baseURL) {
|
|
41
|
-
let type = JSONTag.getType(data)
|
|
42
|
-
let attributes = JSONTag.getAttributes(data)
|
|
43
|
-
if (Array.isArray(data)) {
|
|
44
|
-
data = data.map((entry,index) => {
|
|
45
|
-
return linkReplacer(data[index], baseURL+index+'/')
|
|
46
|
-
})
|
|
47
|
-
} else if (type === 'link') {
|
|
48
|
-
// do nothing
|
|
49
|
-
} else if (data && typeof data === 'object') {
|
|
50
|
-
data = JSONTag.clone(data)
|
|
51
|
-
Object.keys(data).forEach(key => {
|
|
52
|
-
if (Array.isArray(data[key])) {
|
|
53
|
-
data[key] = new JSONTag.Link(baseURL+key+'/')
|
|
54
|
-
} else if (typeof data[key] === 'object') {
|
|
55
|
-
if (JSONTag.getType(data[key])!=='link') {
|
|
56
|
-
let id=JSONTag.getAttribute(data[key], 'id')
|
|
57
|
-
if (!id) {
|
|
58
|
-
id = baseURL+key+'/'
|
|
59
|
-
}
|
|
60
|
-
data[key] = new JSONTag.Link(id)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
return data
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
//@TODO: emit console events that server.mjs picks up
|
|
69
|
-
function connectConsole(res) {
|
|
70
|
-
return {
|
|
71
|
-
log: function(...args) {
|
|
72
|
-
// res.append('X-Console-Log', joinArgs(args))
|
|
73
|
-
},
|
|
74
|
-
warning: function(...args) {
|
|
75
|
-
// res.append('X-Console-Warning', joinArgs(args))
|
|
76
|
-
},
|
|
77
|
-
error: function(...args) {
|
|
78
|
-
// res.append('X-Console-Error', joinArgs(args))
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const query = {
|
|
84
|
-
initialize(jsontag) {
|
|
85
|
-
if (!jsontag) { throw new Error('missing jsontag parameter')}
|
|
86
|
-
dataspace = JSONTag.parse(jsontag, null, meta)
|
|
87
|
-
deepFreeze(dataspace)
|
|
88
|
-
console.log('initialized query worker thread')
|
|
89
|
-
return true
|
|
90
|
-
},
|
|
91
|
-
runQuery(pointer, request, query=null) {
|
|
92
|
-
if (!pointer) { throw new Error('missing pointer parameter')}
|
|
93
|
-
if (!request) { throw new Error('missing request parameter')}
|
|
94
|
-
console.log('query',pointer)
|
|
95
|
-
let response = {
|
|
96
|
-
jsontag: request.jsontag
|
|
97
|
-
}
|
|
98
|
-
let [result,path] = getDataSpace(pointer, dataspace)
|
|
99
|
-
|
|
100
|
-
if (query) {
|
|
101
|
-
// @todo add text search: https://github.com/nextapps-de/flexsearch
|
|
102
|
-
// @todo add tree walk map/reduce/find/filter style functions
|
|
103
|
-
// @todo add arc tree dive function?
|
|
104
|
-
// @todo replace VM with V8 isolate
|
|
105
|
-
const vm = new VM({
|
|
106
|
-
timeout: 1000,
|
|
107
|
-
allowAsync: false,
|
|
108
|
-
sandbox: {
|
|
109
|
-
root: dataspace, //@TODO: if we don't pass the root, we can later shard
|
|
110
|
-
data: result,
|
|
111
|
-
meta: meta,
|
|
112
|
-
_: _,
|
|
113
|
-
from: from,
|
|
114
|
-
not: not,
|
|
115
|
-
anyOf: anyOf,
|
|
116
|
-
allOf: allOf,
|
|
117
|
-
// console: connectConsole(res),
|
|
118
|
-
JSONTag: JSONTag,
|
|
119
|
-
request: request
|
|
120
|
-
},
|
|
121
|
-
wasm: false
|
|
122
|
-
})
|
|
123
|
-
try {
|
|
124
|
-
result = vm.run(query)
|
|
125
|
-
let used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
126
|
-
console.log(`(${used} MB)`);
|
|
127
|
-
} catch(err) {
|
|
128
|
-
console.log(err)
|
|
129
|
-
response.code = 422;
|
|
130
|
-
if (request.jsontag) {
|
|
131
|
-
response.body = '<object class="Error">{"message":'+originalJSON.stringify(''+err)+',"code":422}'
|
|
132
|
-
} else {
|
|
133
|
-
response.body = JSON.stringify({message:err, code: 422})
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (!response.code) {
|
|
138
|
-
if (response.jsontag) {
|
|
139
|
-
response.body = JSONTag.stringify(linkReplacer(result, path+'/'))
|
|
140
|
-
} else {
|
|
141
|
-
response.body = JSON.stringify(result)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return response
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
expose(query)
|