@muze-nl/simplystore 0.2.4 → 0.3.1
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/README.md +2 -2
- package/package.json +2 -2
- package/src/server.mjs +4 -1
- package/www/assets/css/page.css +34 -0
- package/www/help/index.html +94 -0
- package/www/index.html +67 -20
- package/.dockerignore +0 -8
- package/.github/workflows/docker-build.yml +0 -45
- package/Dockerfile +0 -60
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SimplyStore
|
|
2
2
|
|
|
3
|
-
SimplyStore is
|
|
3
|
+
SimplyStore is a radically simpler backend storage server. It does not have a database, certainly no SQL or GraphQL, it is not REST. In return it has a well defined API that is automatically derived from your dataset. It supports JSONTag to allow for semantically meaningful data, without having to do the full switch to Linked Data and triple stores. The query format is javascript, you can post javascript queries that will run on the server. All data is read into memory and is available to these javascript queries without needing (or allowing) disk access or indexes.
|
|
4
4
|
|
|
5
5
|
[JSONTag](https://github.com/poef/jsontag) is an enhancement over JSON that allows you to tag JSON data with metadata using HTML-like tags.
|
|
6
6
|
Javascript queries are run in a [VM2](https://www.npmjs.com/package/vm2) sandbox.
|
|
@@ -112,7 +112,7 @@ Most important: queries cannot change the dataset, it is immutable.
|
|
|
112
112
|
<a name="goals"></a>
|
|
113
113
|
## Goals of this project
|
|
114
114
|
|
|
115
|
-
SimplyStore is
|
|
115
|
+
SimplyStore is a more defined and usable REST like service, out of the box. One where all you need to do is change the data and add some access rights and get a self-describing, browseable, working API.
|
|
116
116
|
|
|
117
117
|
The SimplyStore design is predicated on the following realisations:
|
|
118
118
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muze-nl/simplystore",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"main": "src/server.mjs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"homepage": "https://github.com/simplyedit/simplystore#readme",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@muze-nl/jsontag": "^0.8.4",
|
|
19
|
-
"array-where-select": "^0.
|
|
19
|
+
"array-where-select": "^0.3.1",
|
|
20
20
|
"codemirror": "^6.0.1",
|
|
21
21
|
"express": "^4.18.1",
|
|
22
22
|
"json-pointer": "^0.6.2",
|
package/src/server.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import pointer from 'json-pointer'
|
|
|
4
4
|
import JSONTag from '@muze-nl/jsontag'
|
|
5
5
|
import {JSONPath} from 'jsonpath-plus'
|
|
6
6
|
import {VM} from 'vm2'
|
|
7
|
-
import {_,from} from 'array-where-select'
|
|
7
|
+
import {_,from,not,anyOf,allOf} from 'array-where-select'
|
|
8
8
|
import { fileURLToPath } from 'url'
|
|
9
9
|
import path from 'path'
|
|
10
10
|
|
|
@@ -195,6 +195,9 @@ async function main(options) {
|
|
|
195
195
|
meta: meta,
|
|
196
196
|
_: _,
|
|
197
197
|
from: from,
|
|
198
|
+
not: not,
|
|
199
|
+
anyOf: anyOf,
|
|
200
|
+
allOf: allOf,
|
|
198
201
|
console: connectConsole(res),
|
|
199
202
|
JSONTag: JSONTag,
|
|
200
203
|
request: {
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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>
|
package/www/index.html
CHANGED
|
@@ -8,33 +8,58 @@
|
|
|
8
8
|
--ds-primary-gradient: var(--ds-simplystore-gradient);
|
|
9
9
|
--ds-primary-gradient-bump: var(--ds-simplystore-gradient-bump);
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
body {
|
|
12
|
+
position: fixed;
|
|
13
|
+
width: 100%;
|
|
14
|
+
display: grid;
|
|
15
|
+
grid-template:
|
|
16
|
+
"header header" 65px
|
|
17
|
+
"code data" auto / 1fr 1fr;
|
|
18
|
+
height: 100%;
|
|
19
|
+
column-gap: 5px;
|
|
20
|
+
}
|
|
21
|
+
header {
|
|
22
|
+
grid-area: header;
|
|
17
23
|
}
|
|
18
24
|
form.Query {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
grid-area: code;
|
|
26
|
+
}
|
|
27
|
+
.dataBrowser {
|
|
28
|
+
position: relative;
|
|
29
|
+
grid-area: data;
|
|
24
30
|
}
|
|
25
31
|
.CodeMirror {
|
|
26
32
|
height: 100% !important;
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
|
|
35
|
+
.ds-visible {
|
|
36
|
+
display: block;
|
|
31
37
|
}
|
|
32
|
-
|
|
33
|
-
position:
|
|
38
|
+
.help {
|
|
39
|
+
position: absolute;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
34
42
|
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
background: var(--ds-black);
|
|
45
|
+
color: var(--ds-white);
|
|
46
|
+
margin:0;
|
|
47
|
+
padding:0;
|
|
35
48
|
}
|
|
36
|
-
.
|
|
37
|
-
|
|
49
|
+
iframe.full {
|
|
50
|
+
border: 0;
|
|
51
|
+
height: calc(100% + 1px);
|
|
52
|
+
width: calc(100% + 1px);
|
|
53
|
+
border: 0;
|
|
54
|
+
}
|
|
55
|
+
.ds-close {
|
|
56
|
+
position: absolute;
|
|
57
|
+
z-index: 1000;
|
|
58
|
+
top: 0;
|
|
59
|
+
right: 0;
|
|
60
|
+
}
|
|
61
|
+
dialog {
|
|
62
|
+
border: 0;
|
|
38
63
|
}
|
|
39
64
|
</style>
|
|
40
65
|
<body class="ds-nightmode">
|
|
@@ -81,13 +106,13 @@
|
|
|
81
106
|
Fixtures
|
|
82
107
|
</button>
|
|
83
108
|
<span class="ds-push-right"></span>
|
|
84
|
-
<button class="ds-button ds-button-icon">
|
|
109
|
+
<button data-simply-command="download" class="ds-button ds-button-icon">
|
|
85
110
|
<svg class="ds-icon ds-icon-feather">
|
|
86
111
|
<use xlink:href="/assets/feather-sprite.svg#download">
|
|
87
112
|
</use></svg>
|
|
88
113
|
Download
|
|
89
114
|
</button>
|
|
90
|
-
<button class="ds-button ds-button-icon">
|
|
115
|
+
<button data-simply-command="help" class="ds-button ds-button-icon">
|
|
91
116
|
<svg class="ds-icon ds-icon-feather">
|
|
92
117
|
<use xlink:href="/assets/feather-sprite.svg#help-circle">
|
|
93
118
|
</use></svg>
|
|
@@ -102,6 +127,14 @@
|
|
|
102
127
|
|
|
103
128
|
<div class="dataBrowser">
|
|
104
129
|
<textarea id="databrowser" class="CodeMirror" data-simply-field="data"></textarea>
|
|
130
|
+
<dialog class="help">
|
|
131
|
+
<button data-simply-command="close" class="ds-button ds-button-naked ds-close" data-simply-value="button[data-simply-command=help]">
|
|
132
|
+
<svg class="ds-icon ds-icon-feather">
|
|
133
|
+
<use xlink:href="/assets/feather-sprite.svg#x">
|
|
134
|
+
</use></svg>
|
|
135
|
+
</button>
|
|
136
|
+
<iframe class="full" src="/help/"></iframe>
|
|
137
|
+
<dialog>
|
|
105
138
|
</div>
|
|
106
139
|
|
|
107
140
|
<script src="/codemirror/lib/codemirror.js"></script>
|
|
@@ -175,6 +208,20 @@
|
|
|
175
208
|
let fixtures = await this.app.actions.loadFixtures()
|
|
176
209
|
codeEditor.setValue(fixtures)
|
|
177
210
|
codeEditor.focus()
|
|
211
|
+
},
|
|
212
|
+
'help': async function(el) {
|
|
213
|
+
let help = document.querySelector('.help');
|
|
214
|
+
if (help.open) {
|
|
215
|
+
help.removeAttribute('open')
|
|
216
|
+
el.classList.remove('ds-selected')
|
|
217
|
+
} else {
|
|
218
|
+
help.setAttribute('open','open')
|
|
219
|
+
el.classList.add('ds-selected')
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
'close': async function(el, value) {
|
|
223
|
+
el.closest('dialog').removeAttribute('open')
|
|
224
|
+
document.querySelector(value).classList.remove('ds-selected')
|
|
178
225
|
}
|
|
179
226
|
},
|
|
180
227
|
actions: {
|
package/.dockerignore
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Build Docker Image
|
|
3
|
-
|
|
4
|
-
on:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- master
|
|
8
|
-
- main
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [ main, master ]
|
|
11
|
-
# Allow manually triggering the workflow.
|
|
12
|
-
workflow_dispatch:
|
|
13
|
-
|
|
14
|
-
# Cancels all previous workflow runs for the same branch that have not yet completed.
|
|
15
|
-
concurrency:
|
|
16
|
-
# The concurrency group contains the workflow name and the branch name.
|
|
17
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
-
cancel-in-progress: true
|
|
19
|
-
|
|
20
|
-
jobs:
|
|
21
|
-
build-docker:
|
|
22
|
-
runs-on: ubuntu-latest
|
|
23
|
-
steps:
|
|
24
|
-
- name: Create docker tag (from git reference)
|
|
25
|
-
# A tag name may only contain lower- and uppercase letters, digits, underscores, periods and dashes.
|
|
26
|
-
run: |
|
|
27
|
-
echo "TAG=$(echo -n "${{ github.ref_name }}" \
|
|
28
|
-
| tr --complement --squeeze-repeats '[:alnum:]._-' '_')" \
|
|
29
|
-
>> "${GITHUB_ENV}"
|
|
30
|
-
|
|
31
|
-
- uses: actions/checkout@v3
|
|
32
|
-
|
|
33
|
-
- name: Login to GitHub Container Registry
|
|
34
|
-
uses: docker/login-action@v2
|
|
35
|
-
with:
|
|
36
|
-
registry: ghcr.io
|
|
37
|
-
username: ${{ github.actor }}
|
|
38
|
-
password: ${{ secrets.GITHUB_TOKEN }}
|
|
39
|
-
|
|
40
|
-
- name: Build Docker Image
|
|
41
|
-
run: |
|
|
42
|
-
docker build \
|
|
43
|
-
--tag "ghcr.io/poef/jsontag-rest-server:${{ env.TAG }}" \
|
|
44
|
-
.
|
|
45
|
-
docker push "ghcr.io/poef/jsontag-rest-server:${{ env.TAG }}"
|
package/Dockerfile
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# ==============================================================================
|
|
2
|
-
# Dependencies
|
|
3
|
-
# ------------------------------------------------------------------------------
|
|
4
|
-
FROM node:18-alpine3.17 as builder
|
|
5
|
-
|
|
6
|
-
WORKDIR /usr/src/app
|
|
7
|
-
COPY package.json ./
|
|
8
|
-
|
|
9
|
-
# @TODO: Once development is stable, `npm ci --only=production` should be used instead of `npm install`
|
|
10
|
-
RUN npm install --omit=dev
|
|
11
|
-
# ==============================================================================
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# ==============================================================================
|
|
15
|
-
# Application
|
|
16
|
-
# ------------------------------------------------------------------------------
|
|
17
|
-
FROM alpine:3.17
|
|
18
|
-
|
|
19
|
-
ENV NODE_ENV=production
|
|
20
|
-
|
|
21
|
-
RUN addgroup -g 1000 node \
|
|
22
|
-
&& adduser -u 1000 -G node -s /bin/sh -D node \
|
|
23
|
-
&& apk add --no-cache nodejs=~18.14
|
|
24
|
-
|
|
25
|
-
COPY --chown=node:node . /app
|
|
26
|
-
COPY --chown=node:node --from=builder /usr/src/app/node_modules /app/node_modules
|
|
27
|
-
|
|
28
|
-
WORKDIR /app
|
|
29
|
-
|
|
30
|
-
CMD [ "node", "/app/src/main.mjs" ]
|
|
31
|
-
# ==============================================================================
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# ==============================================================================
|
|
35
|
-
# Metadata
|
|
36
|
-
# ------------------------------------------------------------------------------
|
|
37
|
-
# @TODO: Once development is stable, the following should also be added
|
|
38
|
-
# org.label-schema.build-date=${BUILD_DATE} \ # Usually $(date --iso-8601=seconds)
|
|
39
|
-
# org.label-schema.vcs-ref=${BUILD_REF} \ # Usually $(git describe --tags --always)
|
|
40
|
-
# org.label-schema.version=${VERSION} \ # Usually $(git describe --tags --abbrev=0)
|
|
41
|
-
# org.opencontainers.image.created="${BUILD_DATE}" \
|
|
42
|
-
# org.opencontainers.image.version="${VERSION}"
|
|
43
|
-
LABEL maintainer="Auke van Slooten <auke@muze.nl>" \
|
|
44
|
-
org.label-schema.description="JSONTag REST Server" \
|
|
45
|
-
org.label-schema.docker.cmd='docker run --expose 3000 --interactive --name=jsontag-rest-server --rm --tty --volume "\$PWD/my-data.json:/app/data.jsontag" jsontag-rest-server' \
|
|
46
|
-
org.label-schema.schema-version="1.0" \
|
|
47
|
-
org.label-schema.name="jsontag-rest-server" \
|
|
48
|
-
org.label-schema.url="https://github.com/poef/jsontag-rest-server" \
|
|
49
|
-
org.label-schema.usage="https://github.com/poef/jsontag-rest-server" \
|
|
50
|
-
org.label-schema.vcs-url="https://github.com/poef/jsontag-rest-server" \
|
|
51
|
-
org.label-schema.vendor="Auke van Slooten" \
|
|
52
|
-
org.opencontainers.image.authors="Auke van Slooten <auke@muze.nl>" \
|
|
53
|
-
org.opencontainers.image.description="JSONTag REST Server" \
|
|
54
|
-
org.opencontainers.image.documentation="https://github.com/poef/jsontag-rest-server" \
|
|
55
|
-
org.opencontainers.image.licenses="MIT" \
|
|
56
|
-
org.opencontainers.image.source="https://github.com/poef/jsontag-rest-server" \
|
|
57
|
-
org.opencontainers.image.title="jsontag-rest-server" \
|
|
58
|
-
org.opencontainers.image.url="https://github.com/poef/jsontag-rest-server" \
|
|
59
|
-
org.opencontainers.image.vendor="Auke van Slooten"
|
|
60
|
-
# ==============================================================================
|