@muze-nl/simplystore 0.3.2 → 0.3.3
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/.npmignore~ +8 -0
- package/package.json +7 -4
- package/package.json~ +32 -0
- package/src/commands.mjs +7 -0
- package/src/produce.mjs +354 -0
- package/src/server.mjs +152 -147
- package/src/share.mjs +159 -0
- package/src/util.mjs +46 -0
- package/src/worker-command.js +49 -0
- package/src/worker-query.js +148 -0
- package/test/produce.mjs +79 -0
- package/test/share.mjs +18 -0
- package/test/test.jsontag +20 -0
- package/www/codemirror/keymap/vim.js +3 -3
- package/design/access-management.md +0 -25
- package/design/acid.md +0 -31
- package/design/commands.md +0 -25
- package/design/identity.md +0 -71
- package/design/immutability.md +0 -26
- package/design/jsontag-selector.md +0 -365
- package/design/multitasking.md +0 -13
- package/design/thoughts.md +0 -32
- package/docs/docker.md +0 -129
- package/www/assets/css/page.css +0 -34
- package/www/help/index.html +0 -94
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
# JSONTag-selector or query
|
|
2
|
-
|
|
3
|
-
This document is now here for historical reference only. SimplyStore now uses the [npm package array-where-select](https://www.npmjs.com/package/array-where-select) as its default query or selector algorithm. In addition work is ongoing on a triplestore implementation with a datalog query engine, but this needs a lot of work to improve performance.
|
|
4
|
-
|
|
5
|
-
Historical design document follows:
|
|
6
|
-
|
|
7
|
-
<hr>
|
|
8
|
-
|
|
9
|
-
The aim is to define a way to represent a selection of the JSONTag dataspace in a string. For JSON there are JSON Path and JSON Pointer. For HTML there is the CSS selector. JSONTag has properties of both HTML and JSON, so a mix of these is a logical solution.
|
|
10
|
-
|
|
11
|
-
What must the JSONTag selector be capable of?
|
|
12
|
-
|
|
13
|
-
- point to a single entity inside a JSONTag dataset
|
|
14
|
-
- select multiple entities with something similar
|
|
15
|
-
- combine two or more selectors
|
|
16
|
-
|
|
17
|
-
In addition the selector must be usable within a graphql-like query syntax.
|
|
18
|
-
|
|
19
|
-
## CSS Selectors and JSONTag data
|
|
20
|
-
|
|
21
|
-
We have some experience with this, using the [JSON-CSS](https://github.com/simplyedit/json-css) library. This works reasonably well, but mostly because browsers have built-in CSS selector support that is highly optimized. Still let's see what JSONTag-selector would look like if it was just a CSS Selector.
|
|
22
|
-
|
|
23
|
-
The JSONTag data would need to be converted to an XML structure that contains all data. The problem here is that JSONTag already contains tags, that would need to be converted somehow. This may actually be simpler than just JSON data, because we have tags for all types already.
|
|
24
|
-
|
|
25
|
-
Given:
|
|
26
|
-
```jsontag
|
|
27
|
-
[
|
|
28
|
-
<object class="Task">{
|
|
29
|
-
"title": "This is a task",
|
|
30
|
-
"start": <date>"2023-01-18"
|
|
31
|
-
}
|
|
32
|
-
]
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
This becomes:
|
|
36
|
-
```xml
|
|
37
|
-
<array>
|
|
38
|
-
<object class="Task" key="0">
|
|
39
|
-
<string key="title" value="This is a task" />
|
|
40
|
-
<date key="start" value="2023-01-18" />
|
|
41
|
-
</object>
|
|
42
|
-
</array>
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
The only issue here is that there may be a conflict between attributes on the JSONTag tags, and attributes used to encode JSONTag values into XML attributes. This is solvable by introducing a namespace:
|
|
46
|
-
|
|
47
|
-
```xml
|
|
48
|
-
<array>
|
|
49
|
-
<object class="Task" js-key="0">
|
|
50
|
-
<string js-key="title" js-value="This is a task" />
|
|
51
|
-
<date js-key="start" js-value="2023-01-18" />
|
|
52
|
-
</object>
|
|
53
|
-
</array>
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
The `-` is illegal in JSONTag, so this will never conflict.
|
|
57
|
-
|
|
58
|
-
To query all tasks with a date after 2023-01-01, you could write this selector:
|
|
59
|
-
|
|
60
|
-
```css
|
|
61
|
-
.Task:has(> date[js-key="start"][js-value^="2023-01-"])
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The problems with this approach are, among others:
|
|
65
|
-
- you can only query tags and attributes, so all values must be encoded in attributes
|
|
66
|
-
- you can only use text-like comparisons, there is no `attribute value is greater than` comparison, for example. There are no ranges.
|
|
67
|
-
- the syntax is a bit cumbersome, although it could be improved by a simple compilation. The above syntax could be simplyfied to:
|
|
68
|
-
|
|
69
|
-
```css
|
|
70
|
-
.Task:has(> date[start^="2023-01-"])
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
The advantages are that there is a wealth of optimized code to apply CSS selectors to XML data, and CSS selectors are relatively well understood and documented.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## JSONPath and JSONTag data
|
|
77
|
-
|
|
78
|
-
JSONPath is a complete query and filter language for JSON data. However it is also fairly complex and cumbersome. The same query as above would be something like:
|
|
79
|
-
|
|
80
|
-
```jsonpath
|
|
81
|
-
$[?(@.start>'2023-01-01')]
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
There is no support for tag names or attributes, because JSON doesn't support those. So this needs to be added. One way is to encode these inside the JSON data, similar to what JSON-LD does. The JSON version of the JSONTag data then becomes:
|
|
85
|
-
|
|
86
|
-
```json
|
|
87
|
-
[
|
|
88
|
-
{
|
|
89
|
-
"_class": "Task",
|
|
90
|
-
"_tag": "object",
|
|
91
|
-
"title": "This is a task",
|
|
92
|
-
"start": {
|
|
93
|
-
"_tag": "date",
|
|
94
|
-
"_value": "2023-01-18"
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
This is not ideal or consistent. I haven't converted the `title` value to an object, but have done so for the `start` value. There is probably a better encoding, but this shows the gist of it. The JSONPath selector now becomes:
|
|
101
|
-
|
|
102
|
-
```jsonpath
|
|
103
|
-
$[?(@._class='Task' && @.start._value>'2023-01-01')]
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
But the ideal solution would be to just extend the JSONPath language with tag and attribute query support and use the JSONTag data directly.
|
|
107
|
-
|
|
108
|
-
See [JSONPath-plus](https://github.com/JSONPath-Plus/JSONPath) for a promising code base that has the potential to be extended for JSONTag.
|
|
109
|
-
|
|
110
|
-
## GraphQL
|
|
111
|
-
|
|
112
|
-
GraphQL not only allows for querying, but also mutations. Here we just consider the query syntax. Given the same data and selection, here's what this would look like in a GraphQL query:
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
Task(filter:{start:{greaterThen:"2023-01-01"}) {
|
|
116
|
-
title
|
|
117
|
-
start
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
This is not entirely accurate, but will do for this purpose. The advantage of Graphql is that it allows you to specify the properties you are interested in, including properties of linked objects. This solves the problem of having to query a server many times to complete a dataset that contains links.
|
|
122
|
-
|
|
123
|
-
For example, if we add assigned persons to a task, the dataset might look like this:
|
|
124
|
-
|
|
125
|
-
```jsontag
|
|
126
|
-
[
|
|
127
|
-
<object class="Person" id="john">{
|
|
128
|
-
"name": "John",
|
|
129
|
-
"dob": <date>"1972-09-20"
|
|
130
|
-
},
|
|
131
|
-
<object class="Task">{
|
|
132
|
-
"title": "This is a task",
|
|
133
|
-
"start": <date>"2023-01-18",
|
|
134
|
-
"assigned": [
|
|
135
|
-
<link>"#john"
|
|
136
|
-
]
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Then you could write this query:
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
Task(filter:{start:{greaterThen:"2023-01-01"}) {
|
|
145
|
-
title
|
|
146
|
-
start
|
|
147
|
-
assigned {
|
|
148
|
-
name
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
And the result should be:
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
[
|
|
157
|
-
<object class="Task">{
|
|
158
|
-
"title": "This is a task",
|
|
159
|
-
"start": <date>"2023-01-18",
|
|
160
|
-
"assigned": [
|
|
161
|
-
<object class="Person" id="john">{
|
|
162
|
-
"name": "John"
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
]
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
The interesting part here is, whether the graphql query/filter syntax is the best fit, or perhaps can we replace that with the CSS selector or JSON Path? GraphQL is not quite designed for the purpose here, but I like the way you can select which data to return. The filter options are limited and non-standardized. Each graphql server has its own filter syntax and options. I also don't like the way the filter syntax is articifially limited to something that looks like JSON. I do like the focus on words instead of symbols to denote filter options and functions.
|
|
170
|
-
|
|
171
|
-
GraphQL also allows you to specify aliases for resulting data, so instead of assigned you could write:
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
Task(filter:{start:{greaterThen:"2023-01-01"}) {
|
|
175
|
-
title
|
|
176
|
-
start
|
|
177
|
-
owner:assigned {
|
|
178
|
-
name
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
[
|
|
185
|
-
<object class="Task">{
|
|
186
|
-
"title": "This is a task",
|
|
187
|
-
"start": <date>"2023-01-18",
|
|
188
|
-
"owner": [
|
|
189
|
-
<object class="Person">{
|
|
190
|
-
"name": "John"
|
|
191
|
-
}
|
|
192
|
-
]
|
|
193
|
-
}
|
|
194
|
-
]
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
There is more you can do with aliases, but I'm not sure that this is an approach to copy. The main use for aliases is to prevent conflicts in result names.
|
|
198
|
-
|
|
199
|
-
The main problem I see with the query syntax is that if I just want all the assigned persons, I'd query this way:
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
Task(filter:{start:{greaterThen:"2023-01-01"}) {
|
|
203
|
-
title
|
|
204
|
-
start
|
|
205
|
-
assigned
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
But as it stands this would result in:
|
|
210
|
-
|
|
211
|
-
```
|
|
212
|
-
[
|
|
213
|
-
<object class="Task">{
|
|
214
|
-
"title": "This is a task",
|
|
215
|
-
"start": <date>"2023-01-18",
|
|
216
|
-
"assigned": [
|
|
217
|
-
<link>"/0/"
|
|
218
|
-
]
|
|
219
|
-
}
|
|
220
|
-
]
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
This requires extra queries again, which is precisely what we want to avoid.
|
|
224
|
-
For this reason, the JSONTag Rest server always resolves arrays and literal values (strings, numbers and booleans). So the result should be:
|
|
225
|
-
|
|
226
|
-
```
|
|
227
|
-
[
|
|
228
|
-
<object class="Task">{
|
|
229
|
-
"title": "This is a task",
|
|
230
|
-
"start": <date>"2023-01-18",
|
|
231
|
-
"assigned": [
|
|
232
|
-
<object class="Person">{
|
|
233
|
-
"name": "John",
|
|
234
|
-
"dob": <date>"1972-09-20"
|
|
235
|
-
}
|
|
236
|
-
]
|
|
237
|
-
}
|
|
238
|
-
]
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
This does break the GrapQL paradigm where you will only ever get the exact properties that you request.
|
|
242
|
-
|
|
243
|
-
## -Current approach- past approach
|
|
244
|
-
|
|
245
|
-
For now I'm using [jsonpath-plus](https://github.com/JSONPath-Plus/JSONPath) which extends the default [JSONPath]() with among others a parent selector. This codebase looks relatively simple to add tagname and attribute selection support. This implementation allows for javascript methods to be called in filters, applied only to available data. The expressions are evaluated in a seperate 'vm', but security needs to be checked carefully.
|
|
246
|
-
|
|
247
|
-
You can query the dataset by POSTing to a path, with the JSON Path query in the body. e.g.:
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
POST /persons/
|
|
251
|
-
|
|
252
|
-
$[?(@.name==='Jane')]
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
This will result in:
|
|
256
|
-
|
|
257
|
-
```jsontag
|
|
258
|
-
[
|
|
259
|
-
<object class="Person">{
|
|
260
|
-
"name":"Jane",
|
|
261
|
-
"dob":<date>"1986-01-01"
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
The next step is to add a bit of GraphQL syntax to describe which properties you want returned. e.g.
|
|
267
|
-
|
|
268
|
-
```
|
|
269
|
-
$.[?(@.name==='Jane')] {
|
|
270
|
-
name
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
Add alias support, so you can rename properties:
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
$.[?(@.name==='Jane')] {
|
|
278
|
-
foo:name
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
And finally add subqueries, where you can assign JSON Path search results to a property:
|
|
283
|
-
|
|
284
|
-
```
|
|
285
|
-
$.[?(@.name==='Jane')] {
|
|
286
|
-
name
|
|
287
|
-
tasks:$$..tasks[?(@.assigned==='Jane')]
|
|
288
|
-
}
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
A problem here is whether the subquery should start at the global root, or the current object. The global root allows for much more features, but also adds complexity. It would be nice to be able to specify in the query, e.g. make the global root something like $$.
|
|
292
|
-
|
|
293
|
-
JSON Path has no knowledge of JSONTag, so it has no support for tag names (types) or attributes. JSON Path uses these special characters already: `$.@?&=!<>*`. Lets use `#` for attributes, like `@` for properties. You could then do this:
|
|
294
|
-
|
|
295
|
-
```
|
|
296
|
-
$.[?(#.class==='Person')] {
|
|
297
|
-
name
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
And we can add a tag name, like this:
|
|
302
|
-
|
|
303
|
-
```
|
|
304
|
-
$.[?(#.class==='Person' && #.tag-name==='object')] {
|
|
305
|
-
name
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
By using 'tag-name' we are sure it will never conflict with an attribute name, since they can't contain `-`.
|
|
310
|
-
|
|
311
|
-
## New Current Approach
|
|
312
|
-
|
|
313
|
-
After reading [Datalog in Javascript](https://www.instantdb.com/essays/datalogjs) I've switched the query engine to Datalog. In fact the query engine is a javascript interpreter running in [VM2](https://github.com/patriksimek/vm2), with access to the query function of the triple store compiled from the jsontag input data.
|
|
314
|
-
|
|
315
|
-
So you can now POST to the /query endpoint with just a simple query:
|
|
316
|
-
|
|
317
|
-
```javascript
|
|
318
|
-
query({
|
|
319
|
-
find: ["?name"],
|
|
320
|
-
where: [
|
|
321
|
-
["?person", "dob", "1972-09-20"],
|
|
322
|
-
["?person", "name", "?name"]
|
|
323
|
-
]
|
|
324
|
-
})
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
And the result will be:
|
|
328
|
-
```
|
|
329
|
-
[
|
|
330
|
-
["John"]
|
|
331
|
-
]
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
Or you can add your own function to match values:
|
|
335
|
-
|
|
336
|
-
```javascript
|
|
337
|
-
function lessThan(pattern) {
|
|
338
|
-
return function(match) {
|
|
339
|
-
if (match<pattern) {
|
|
340
|
-
return match
|
|
341
|
-
}
|
|
342
|
-
return null
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
query({
|
|
347
|
-
find: ["?name"],
|
|
348
|
-
where: [
|
|
349
|
-
["?person", "dob", lessThan("1980")],
|
|
350
|
-
["?person", "name", "?name"]
|
|
351
|
-
]
|
|
352
|
-
})
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
You can also return complete objects:
|
|
356
|
-
```javascript
|
|
357
|
-
query({
|
|
358
|
-
find: ["?person"],
|
|
359
|
-
where: [
|
|
360
|
-
["?person", "dob", lessThan("1980")]
|
|
361
|
-
]
|
|
362
|
-
})
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
There is as yet no way to transform the result, other than adding your own bespoke javascript to do that.
|
package/design/multitasking.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# multitasking
|
|
2
|
-
|
|
3
|
-
Node itself is (almost) a single process. The server must be able to handle multiple requests simultaneously, maximizing cpu core usage.
|
|
4
|
-
|
|
5
|
-
The query endpoint is the only part that must be multitasking. Instead of using an external system like PM2, the server should use worker threads. Threads use the same memory, but since the query endpoints are all using the immutable dataset only, that is no worry here.
|
|
6
|
-
|
|
7
|
-
The update endpoint needs a single seperate worker. This allows for the main process to be limited to managing the workers only. This way there is less chance of a problem crashing the whole server.
|
|
8
|
-
|
|
9
|
-
Each worker starts a single VM, which runs all the code. The isolated-vm package allows for more control than VM2, but also has no easy way to share the immutable dataset. So for now we stick to VM2.
|
|
10
|
-
Update: isolated-vm may be able to do this using the [ivm.Reference](https://github.com/laverdet/isolated-vm#class-reference-transferable) function.
|
|
11
|
-
|
|
12
|
-
https://www.digitalocean.com/community/tutorials/how-to-use-multithreading-in-node-js
|
|
13
|
-
https://www.npmjs.com/package/piscina
|
package/design/thoughts.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
JSONTag
|
|
2
|
-
|
|
3
|
-
- streaming parser (handrolled for performance)
|
|
4
|
-
- option: documentURL
|
|
5
|
-
automatically enhance id's as urls with baseURL the documentURL
|
|
6
|
-
do this for <link> as well as id attributes
|
|
7
|
-
- option: parent index
|
|
8
|
-
when parsing, keep track of the parent for each value, and add it to a WeakMap index.parents, this contains all objects with a reference to the object used as key.
|
|
9
|
-
- object id and link value should always be URI's, and should be identical. If the link value starts with a '#', then the id should also start with a '#'.
|
|
10
|
-
- fast-parse doesn't enforce parsing rules for specific tags yet
|
|
11
|
-
|
|
12
|
-
JSONTag REST Server
|
|
13
|
-
|
|
14
|
-
- think of a better name (iets met live?) simplyStore?
|
|
15
|
-
- add a set of default functions / standard library to walk/map/reduce over the data. Like the arc/tree methods, e.g. dive. Note: JSONTag data is a graph, so it can contain circular references, make sure you don't walk into an infinite loop. Keep track of which objects have already been walked over.
|
|
16
|
-
- create extra indexes to speed up 'common' searches, e.g. give me all objects with class X that are contained/child/descendant of Y. (like the JSONPath `$..book` expression)
|
|
17
|
-
|
|
18
|
-
JSONTag REST Server Commands
|
|
19
|
-
- create endpoint POST /update/ for example
|
|
20
|
-
- syntax of commands should be simple and easily parsible
|
|
21
|
-
- create a log of all commands, with a unique id per command
|
|
22
|
-
- commands are javascript methods defined serverside in a commands.js file / commands object
|
|
23
|
-
- each command is a transaction, it succeeds wholly or fails wholly
|
|
24
|
-
- commands may have preconditions, if not met, they fail
|
|
25
|
-
- when sending a command, you get back the generated command id immediately
|
|
26
|
-
- you can (long) poll for the status of a command (pending, success, failed)
|
|
27
|
-
GET /update/{uuid}
|
|
28
|
-
- there should be an SSE event bus endpoint that allows you to listen wihtout polling
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
package/docs/docker.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# Docker Image
|
|
2
|
-
|
|
3
|
-
A minimal Docker image is available for the `jsontag-rest-server`.
|
|
4
|
-
|
|
5
|
-
<!-- toc -->
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Building the image](#building-the-image)
|
|
9
|
-
- [Pulling the image](#pulling-the-image)
|
|
10
|
-
- [Usage](#usage)
|
|
11
|
-
- [Docker User](#docker-user)
|
|
12
|
-
- [Environment Variables](#environment-variables)
|
|
13
|
-
- [Extending the image](#extending-the-image)
|
|
14
|
-
|
|
15
|
-
<!-- tocstop -->
|
|
16
|
-
|
|
17
|
-
The docker image is created from the [`Dockerfile`](../Dockerfile) in the root of the repository.
|
|
18
|
-
|
|
19
|
-
The image is based on [alpine][1] with [NodeJS installed][2], rather than using the official NodeJS image. This is done to keep the image size down (from more than 200MB to less than 100MB).
|
|
20
|
-
|
|
21
|
-
If more functionality is needed, the image can be extended by creating a new `Dockerfile` that uses the `jsontag-rest-server` image as a base.
|
|
22
|
-
|
|
23
|
-
## Installation
|
|
24
|
-
|
|
25
|
-
The image can be build from the Dockerfile provide by this project or pulled from the GitHub Container Registry.
|
|
26
|
-
|
|
27
|
-
### Building the image
|
|
28
|
-
|
|
29
|
-
The image can be built using the following command:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
docker build --tag jsontag-rest-server .
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Pulling the image
|
|
36
|
-
|
|
37
|
-
The image can be pulled from the GitHub Container Registry using the following command:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
docker pull ghcr.io/poef/jsontag-rest-server
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Usage
|
|
44
|
-
|
|
45
|
-
To use the `jsontag-rest-server` container, two things are needed:
|
|
46
|
-
|
|
47
|
-
1. **The port the server is listening on needs to be exposed.**<br>
|
|
48
|
-
This can be done by using the `--expose` flag when running the container. By default, this is port 3000. The port can be changed by setting the `NODE_PORT` environment variable (see the "Environment Variables" section).
|
|
49
|
-
|
|
50
|
-
2. **A data file needs to be mounted into the container.**<br>
|
|
51
|
-
The image comes with [a default dataset][3], loaded from `/app/data.jsontag` file that can be used to test the server. To use a different dataset, mount it into the container using the `--volume` flag when running the container.
|
|
52
|
-
|
|
53
|
-
To change the default for either the port or the data file, see the "Environment Variables" section.
|
|
54
|
-
|
|
55
|
-
An example of running the server (with the default data file and port):
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
docker run \
|
|
59
|
-
--expose 3000 \
|
|
60
|
-
--interactive \
|
|
61
|
-
--name=jsontag-rest-server \
|
|
62
|
-
--rm \
|
|
63
|
-
--tty \
|
|
64
|
-
--volume "$PWD/my-data.json:/app/data.jsontag"
|
|
65
|
-
jsontag-rest-server
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Docker User
|
|
69
|
-
|
|
70
|
-
Similar to the official NodeJS image, the image is set up to run as a non-root user. The user and group are both `node`. The user is created with a UID of 1000 and a GID of 1000.
|
|
71
|
-
|
|
72
|
-
### Environment Variables
|
|
73
|
-
|
|
74
|
-
The runtime environment variables that can be set are:
|
|
75
|
-
|
|
76
|
-
- `DATAFILE`: The file that is loaded into the REST server.<br>
|
|
77
|
-
Defaults to `data.jsontag`.
|
|
78
|
-
- `NODE_ENV`: The environment the server is running in.<br>
|
|
79
|
-
Defaults to `production`.
|
|
80
|
-
- `NODE_PORT`: The port the server will listen on.<br>
|
|
81
|
-
Defaults to `3000`.
|
|
82
|
-
|
|
83
|
-
These variables can be set when _building_ the Docker image:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
docker build --tag jsontag-rest-server\
|
|
87
|
-
--build-arg DATAFILE=my-data.jsontag \
|
|
88
|
-
--build-arg NODE_ENV=development \
|
|
89
|
-
--build-arg NODE_PORT=8080 \
|
|
90
|
-
.
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
or when _running_ the Docker container
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
docker run \
|
|
97
|
-
--env DATAFILE=my-data.jsontag \
|
|
98
|
-
--env NODE_ENV=development \
|
|
99
|
-
--env NODE_PORT=8080 \
|
|
100
|
-
--expose 8080 \
|
|
101
|
-
--volume "$PWD/my-data.json:/app/my-data.jsontag"
|
|
102
|
-
jsontag-rest-server
|
|
103
|
-
```
|
|
104
|
-
### Extending the image
|
|
105
|
-
|
|
106
|
-
The image can be extended by creating a new `Dockerfile` that uses the `jsontag-rest-server` image as a base.
|
|
107
|
-
|
|
108
|
-
For example, to add a custom data file to the image:
|
|
109
|
-
|
|
110
|
-
```dockerfile
|
|
111
|
-
FROM node:lts-buster-slim
|
|
112
|
-
|
|
113
|
-
# ... (other instructions)
|
|
114
|
-
|
|
115
|
-
COPY --from=ghcr.io/poef/jsontag-rest-server /app/ /usr/src/app/jsontag-rest-server/
|
|
116
|
-
COPY my-data.jsontag /usr/src/app/jsontag-rest-server/data.jsontag
|
|
117
|
-
|
|
118
|
-
# ... (other instructions)
|
|
119
|
-
|
|
120
|
-
# The jsontag-rest-server is assumed to be started from entrypoint.sh
|
|
121
|
-
CMD ["entrypoint.sh"]
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
[1]: https://hub.docker.com/_/alpine
|
|
128
|
-
[2]: https://pkgs.alpinelinux.org/package/edge/main/x86/nodejs
|
|
129
|
-
[3]: ../data.jsontag
|
package/www/assets/css/page.css
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
:root {
|
|
2
|
-
--ds-primary: var(--ds-simplystore);
|
|
3
|
-
--ds-primary-contrast: var(--ds-simplystore-contrast);
|
|
4
|
-
--ds-primary-gradient: var(--ds-simplystore-gradient);
|
|
5
|
-
--ds-primary-gradient-bump: var(--ds-simplystore-gradient-bump);
|
|
6
|
-
}
|
|
7
|
-
html, body {
|
|
8
|
-
height: 100%;
|
|
9
|
-
margin: 0;
|
|
10
|
-
background: var(--ds-black);
|
|
11
|
-
color: var(--ds-white);
|
|
12
|
-
overflow: hidden;
|
|
13
|
-
}
|
|
14
|
-
body {
|
|
15
|
-
display: flow-root;
|
|
16
|
-
overflow: auto;
|
|
17
|
-
}
|
|
18
|
-
a:hover, a:active {
|
|
19
|
-
text-decoration: underline;
|
|
20
|
-
color: var(--ds-simplystore-light);
|
|
21
|
-
}
|
|
22
|
-
a:link {
|
|
23
|
-
text-decoration: none;
|
|
24
|
-
color: var(--ds-simplystore);
|
|
25
|
-
}
|
|
26
|
-
a:visited {
|
|
27
|
-
text-decoration: none;
|
|
28
|
-
color: var(--ds-simplystore-dark);
|
|
29
|
-
}
|
|
30
|
-
pre {
|
|
31
|
-
background-color: var(--ds-grey-80);
|
|
32
|
-
border-radius: 8px;
|
|
33
|
-
padding: 8px;
|
|
34
|
-
}
|
package/www/help/index.html
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<link rel="stylesheet" href="/assets/css/style.css">
|
|
3
|
-
<link rel="stylesheet" href="/assets/css/page.css">
|
|
4
|
-
<section class="ds-space">
|
|
5
|
-
<h1>Help</h1>
|
|
6
|
-
<nav>
|
|
7
|
-
<ul>
|
|
8
|
-
<li><a href="#intro">Introduction</a></li>
|
|
9
|
-
<li><a href="#queries">Queries</a></li>
|
|
10
|
-
<li><a href="#jsontag">JSONTag</a></li>
|
|
11
|
-
<li><a href="#fixtures">Fixtures</a></li>
|
|
12
|
-
<li><a href="#about">About</a></li>
|
|
13
|
-
</ul>
|
|
14
|
-
</nav>
|
|
15
|
-
<h2 id="introduction">Introduction</h2>
|
|
16
|
-
<p>
|
|
17
|
-
SimplyStore is a datastore that allows you to query data with javascript. The data is stored and retrieved as JSONTag, an enhanced version of JSON that has support for metadata (type, attributes) and links.
|
|
18
|
-
</p><p>
|
|
19
|
-
SimplyStore has a single dataspace, named <code>data</code>. You can enter a query on the left and press <Control+Enter> to execute it. If you haven't written a query, the right data pane shows an overview of the available data in the dataspace.
|
|
20
|
-
</p>
|
|
21
|
-
<h2 id="queries">Queries</h2>
|
|
22
|
-
<p>SimplyStore queries are just javascript, so this works:</p>
|
|
23
|
-
<pre class="javascript">data.filter(p => p.name=='John')</pre>
|
|
24
|
-
<p>SimplyStore uses the <a href="//npmjs.com/package/array-where-select" target="_blank">array-where-select library</a> to allow for shorter, more expressive queries, e.g:</p>
|
|
25
|
-
<pre class="javascript">from(data)
|
|
26
|
-
.where({
|
|
27
|
-
name: 'John'
|
|
28
|
-
})
|
|
29
|
-
.select({
|
|
30
|
-
name: _,
|
|
31
|
-
dob: _
|
|
32
|
-
})</pre>
|
|
33
|
-
<p>The <code>_</code> is a placeholder that selects the property value corresponding with the property name on the left.</p>
|
|
34
|
-
<p>This query results in output like this:</p>
|
|
35
|
-
<pre class="JSONTag">[
|
|
36
|
-
{
|
|
37
|
-
"name":"John",
|
|
38
|
-
"dob":<date>"1972-09-20"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"name":"Jane",
|
|
42
|
-
"dob":<date>"1986-01-01"
|
|
43
|
-
}
|
|
44
|
-
]</pre>
|
|
45
|
-
<p>See the array-where-select library to learn more about this.</p>
|
|
46
|
-
<h2 id="jsontag">JSONTag</h2>
|
|
47
|
-
<p>JSONtag is backwards compatible with JSON. It adds two important things: links and tags.</p>
|
|
48
|
-
<p>JSON cannot represent data structures with internal references. JSONTag adds a <code><link></code> tag to represent these, like this:</p>
|
|
49
|
-
<pre class="JSONTag">{
|
|
50
|
-
"name": "John",
|
|
51
|
-
"friends": [
|
|
52
|
-
<link>"/persons/1/"
|
|
53
|
-
]
|
|
54
|
-
}</pre>
|
|
55
|
-
<p>Here you also see the first tag pop up. Tags in JSONTag are similar to HTML opening tags. There are no closing tags. Each tag starts with a tagName, which denotes the basic type of the JSON value to the right.</p>
|
|
56
|
-
<p>Available types (tag names) are:</p>
|
|
57
|
-
<h3>JSON</h3>
|
|
58
|
-
<ul>
|
|
59
|
-
<li>boolean</li>
|
|
60
|
-
<li>number</li>
|
|
61
|
-
<li>string</li>
|
|
62
|
-
<li>array</li>
|
|
63
|
-
<li>object</li>
|
|
64
|
-
</ul>
|
|
65
|
-
<h3>Scalars</h3>
|
|
66
|
-
<ul>
|
|
67
|
-
<li>text</li>
|
|
68
|
-
<li>blob</li>
|
|
69
|
-
<li>int (uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64)</li>
|
|
70
|
-
<li>float (float32, float64)</li>
|
|
71
|
-
</ul>
|
|
72
|
-
<h3>Semantic types</h3>
|
|
73
|
-
<ul>
|
|
74
|
-
<li>color</li>
|
|
75
|
-
<li>date</li>
|
|
76
|
-
<li>decimal</li>
|
|
77
|
-
<li>email</li>
|
|
78
|
-
<li>hash</li>
|
|
79
|
-
<li>interval</li>
|
|
80
|
-
<li>link</li>
|
|
81
|
-
<li>money</li>
|
|
82
|
-
<li>phone</li>
|
|
83
|
-
<li>range</li>
|
|
84
|
-
<li>time</li>
|
|
85
|
-
<li>timestamp</li>
|
|
86
|
-
<li>url</li>
|
|
87
|
-
<li>uuid</li>
|
|
88
|
-
</ul>
|
|
89
|
-
<p>Read <a href="//npmjs.com/package/@muze-nl/jsontag" target="_blank">more about JSONTag here</a>.
|
|
90
|
-
<h3 id="fixtures">Fixtures</h3>
|
|
91
|
-
<p>The SimplyStore UI allows you to save and restore queries. These are saved in your browser on your local machine. Because queries are just javascript, you can add your own helper functions. To prevent having to add these to all your seperate queries, the UI has a special 'Fixtures' query. The contents of this query are always prepended to whatever query you send.</p>
|
|
92
|
-
<h3 id="about">About</h3>
|
|
93
|
-
<p>SimplyStore is made by <a href="//muze.nl/" target="_blank">muze.nl</a> and is part of the <a href="//simplyedit.io/" target="_blank">SimplyEdit</a> product range. SimplyStore's source code is open source (MIT) and can be found on <a href="//github.com/SimplyEdit/SimplyStore" target="_blank">//github.com/SimplyEdit/SimplyStore</a></p>
|
|
94
|
-
</section>
|