@graffy/link 0.15.11 → 0.15.13-alpha.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 +38 -0
- package/index.cjs +69 -36
- package/index.mjs +70 -37
- package/package.json +2 -2
package/Readme.md
CHANGED
|
@@ -2,4 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
Graffy module for constructing links using an intuitive, declarative notation.
|
|
4
4
|
|
|
5
|
+
## Example
|
|
6
|
+
|
|
7
|
+
Consider a blog data model, with posts and users. Every post has an `authorId`
|
|
8
|
+
property, and we would like a property `author` on posts (which link to the
|
|
9
|
+
author), and a property `posts` on users, which link to an array of posts
|
|
10
|
+
authored by that user.
|
|
11
|
+
|
|
12
|
+
This is accomplished using:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import Graffy from '@graffy/core';
|
|
16
|
+
import link from '@graffy/link';
|
|
17
|
+
|
|
18
|
+
const store = new Graffy();
|
|
19
|
+
store.use(link({
|
|
20
|
+
'posts.$key.author': ['users', '$$posts.$key.authorId'],
|
|
21
|
+
'users.$key.posts': ['posts', { $all: true, authorId: '$$users.$key.id' }]
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Add downstream providers to the store
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Link definitions
|
|
28
|
+
|
|
29
|
+
The link module must be initialized with an object containing a set of link
|
|
30
|
+
definitions.
|
|
31
|
+
|
|
32
|
+
- Each link definition consists of two paths, the _source_ and the _target_.
|
|
33
|
+
The source path must be a dot-separated string. The target path must be an
|
|
34
|
+
array of strings or argument objects.
|
|
35
|
+
- The source may contain _wildcard_ segments starting with `$` and followed by
|
|
36
|
+
an arbitrary name. Wildcards that were _defined_ in a source path may be
|
|
37
|
+
included in the target path, where they are replaced with matched keys from
|
|
38
|
+
the query.
|
|
39
|
+
- The target may include _lookup_ strings starting with `$$` and followed by
|
|
40
|
+
dot-separated string paths (possibly including wildcards). These are replaced
|
|
41
|
+
with values returned by downstream providers.
|
|
42
|
+
|
|
5
43
|
See [Graffy documentation](https://aravindet.github.io/graffy/) for more.
|
package/index.cjs
CHANGED
|
@@ -23,38 +23,33 @@ function linkGraph(rootGraph, defs) {
|
|
|
23
23
|
for (const { path, def } of defs)
|
|
24
24
|
linkGraphDef(rootGraph, path, def);
|
|
25
25
|
return rootGraph;
|
|
26
|
-
function linkGraphDef(graph, path, def,
|
|
26
|
+
function linkGraphDef(graph, path, def, version = 0) {
|
|
27
27
|
const [key, ...rest] = path;
|
|
28
28
|
if (rest.length === 0) {
|
|
29
|
-
|
|
29
|
+
const ref = makeRef(def);
|
|
30
|
+
const [range] = common.splitRef(def);
|
|
31
|
+
const node2 = { key, path: common.encodePath(ref), version };
|
|
32
|
+
if (range)
|
|
33
|
+
node2.prefix = true;
|
|
34
|
+
common.merge(graph, [node2]);
|
|
30
35
|
return;
|
|
31
36
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
} else {
|
|
39
|
-
for (const node of graph) {
|
|
40
|
-
if (!common.isBranch(node))
|
|
41
|
-
continue;
|
|
42
|
-
linkGraphDef(node.children, rest, def, __spreadProps(__spreadValues({}, vars), {
|
|
43
|
-
[key.slice(1)]: node.key
|
|
44
|
-
}), node.version);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function makeRef(def, vars) {
|
|
49
|
-
function getValue(key) {
|
|
50
|
-
return key[0] === "$" ? vars[key.slice(1)] : key;
|
|
37
|
+
let node = graph[common.findFirst(graph, key)];
|
|
38
|
+
if (!node || node.key !== key || node.end) {
|
|
39
|
+
node = { key, version, value: 1 };
|
|
40
|
+
common.merge(graph, [node]);
|
|
41
|
+
delete node.value;
|
|
42
|
+
node.children = [];
|
|
51
43
|
}
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
if (!node.children) {
|
|
45
|
+
throw Error("linkGraph.unexpected_leaf " + key);
|
|
54
46
|
}
|
|
47
|
+
linkGraphDef(node.children, rest, def, node.version);
|
|
48
|
+
}
|
|
49
|
+
function makeRef(def) {
|
|
55
50
|
function replacePlaceholders(key) {
|
|
56
51
|
if (typeof key === "string" && key[0] === "$" && key[1] === "$") {
|
|
57
|
-
return common.unwrap(rootGraph,
|
|
52
|
+
return common.unwrap(rootGraph, key.slice(2).split("."));
|
|
58
53
|
}
|
|
59
54
|
if (Array.isArray(key)) {
|
|
60
55
|
return key.map(replacePlaceholders);
|
|
@@ -65,42 +60,60 @@ function linkGraph(rootGraph, defs) {
|
|
|
65
60
|
result[prop] = replacePlaceholders(key[prop]);
|
|
66
61
|
return result;
|
|
67
62
|
}
|
|
68
|
-
return
|
|
63
|
+
return key;
|
|
69
64
|
}
|
|
70
65
|
const ref = def.map(replacePlaceholders);
|
|
71
66
|
return ref;
|
|
72
67
|
}
|
|
73
68
|
}
|
|
74
69
|
function prepQueryLinks(rootQuery, defs) {
|
|
75
|
-
return defs.
|
|
70
|
+
return defs.flatMap(({ path, def }) => prepQueryDef(rootQuery, path, def));
|
|
76
71
|
function prepQueryDef(query, path, def, vars = {}, version = 0) {
|
|
77
72
|
var _a;
|
|
78
73
|
const [key, ...rest] = path;
|
|
79
74
|
if (rest.length === 0) {
|
|
80
75
|
const ix = common.findFirst(query, key);
|
|
81
76
|
if (((_a = query[ix]) == null ? void 0 : _a.key) !== key)
|
|
82
|
-
return
|
|
83
|
-
query.splice(ix, 1);
|
|
77
|
+
return [];
|
|
78
|
+
const [{ children: subQuery }] = query.splice(ix, 1);
|
|
84
79
|
common.add(rootQuery, getDefQuery(def, vars, version));
|
|
85
|
-
|
|
80
|
+
const [range, filter] = common.splitRef(def);
|
|
81
|
+
if (range && subQuery.length) {
|
|
82
|
+
return subQuery.map((node) => {
|
|
83
|
+
return {
|
|
84
|
+
path: path.concat(node.key),
|
|
85
|
+
def: prepareDef(def.slice(0, -1).concat(__spreadValues(__spreadValues(__spreadValues({}, filter), common.decodeArgs(node)), range)), vars)
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
return [{ path, def: prepareDef(def, vars) }];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function prefixKey(defs2, key2) {
|
|
93
|
+
return defs2.map(({ path: path2, def: def2 }) => ({
|
|
94
|
+
path: [key2, ...path2],
|
|
95
|
+
def: def2
|
|
96
|
+
}));
|
|
86
97
|
}
|
|
98
|
+
let used = [];
|
|
87
99
|
if (key[0] !== "$") {
|
|
88
100
|
const node = query[common.findFirst(query, key)];
|
|
89
101
|
if (!node || node.key !== key || !node.children)
|
|
90
|
-
return;
|
|
91
|
-
|
|
102
|
+
return [];
|
|
103
|
+
used = prepQueryDef(node.children, rest, def, vars, node.version);
|
|
104
|
+
used = prefixKey(used, node.key);
|
|
92
105
|
} else {
|
|
93
|
-
let used = false;
|
|
94
106
|
for (const node of query) {
|
|
95
107
|
if (!common.isBranch(node))
|
|
96
108
|
continue;
|
|
97
109
|
let usedHere = prepQueryDef(node.children, rest, def, __spreadProps(__spreadValues({}, vars), {
|
|
98
110
|
[key.slice(1)]: node.key
|
|
99
111
|
}), node.version);
|
|
100
|
-
|
|
112
|
+
usedHere = prefixKey(usedHere, node.key);
|
|
113
|
+
used = used.concat(usedHere);
|
|
101
114
|
}
|
|
102
|
-
return used;
|
|
103
115
|
}
|
|
116
|
+
return used;
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
119
|
function getDefQuery(def, vars, version) {
|
|
@@ -119,14 +132,34 @@ function getDefQuery(def, vars, version) {
|
|
|
119
132
|
key.map(addDefQueries);
|
|
120
133
|
}
|
|
121
134
|
if (typeof key === "object" && key) {
|
|
122
|
-
const result = {};
|
|
123
135
|
for (const prop in key)
|
|
124
|
-
|
|
136
|
+
addDefQueries(key[prop]);
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
def.map(addDefQueries);
|
|
128
140
|
return defQuery;
|
|
129
141
|
}
|
|
142
|
+
function prepareDef(def, vars) {
|
|
143
|
+
function getValue(key) {
|
|
144
|
+
return key[0] === "$" ? vars[key.slice(1)] : key;
|
|
145
|
+
}
|
|
146
|
+
function replacePlaceholders(key) {
|
|
147
|
+
if (typeof key === "string" && key[0] === "$" && key[1] === "$") {
|
|
148
|
+
return "$$" + key.slice(2).split(".").map(getValue).join(".");
|
|
149
|
+
}
|
|
150
|
+
if (Array.isArray(key)) {
|
|
151
|
+
return key.map(replacePlaceholders);
|
|
152
|
+
}
|
|
153
|
+
if (typeof key === "object" && key) {
|
|
154
|
+
const result = {};
|
|
155
|
+
for (const prop in key)
|
|
156
|
+
result[prop] = replacePlaceholders(key[prop]);
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
return getValue(key);
|
|
160
|
+
}
|
|
161
|
+
return def.map(replacePlaceholders);
|
|
162
|
+
}
|
|
130
163
|
var index = (defs) => (store) => {
|
|
131
164
|
const prefix = store.path;
|
|
132
165
|
const defEntries = Object.entries(defs).map(([prop, def]) => ({
|
package/index.mjs
CHANGED
|
@@ -17,43 +17,38 @@ var __spreadValues = (a, b) => {
|
|
|
17
17
|
return a;
|
|
18
18
|
};
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
import {
|
|
20
|
+
import { splitRef, encodePath, merge, findFirst, unwrap, add, decodeArgs, isBranch, wrapValue, wrap } from "@graffy/common";
|
|
21
21
|
function linkGraph(rootGraph, defs) {
|
|
22
22
|
for (const { path, def } of defs)
|
|
23
23
|
linkGraphDef(rootGraph, path, def);
|
|
24
24
|
return rootGraph;
|
|
25
|
-
function linkGraphDef(graph, path, def,
|
|
25
|
+
function linkGraphDef(graph, path, def, version = 0) {
|
|
26
26
|
const [key, ...rest] = path;
|
|
27
27
|
if (rest.length === 0) {
|
|
28
|
-
|
|
28
|
+
const ref = makeRef(def);
|
|
29
|
+
const [range] = splitRef(def);
|
|
30
|
+
const node2 = { key, path: encodePath(ref), version };
|
|
31
|
+
if (range)
|
|
32
|
+
node2.prefix = true;
|
|
33
|
+
merge(graph, [node2]);
|
|
29
34
|
return;
|
|
30
35
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} else {
|
|
38
|
-
for (const node of graph) {
|
|
39
|
-
if (!isBranch(node))
|
|
40
|
-
continue;
|
|
41
|
-
linkGraphDef(node.children, rest, def, __spreadProps(__spreadValues({}, vars), {
|
|
42
|
-
[key.slice(1)]: node.key
|
|
43
|
-
}), node.version);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function makeRef(def, vars) {
|
|
48
|
-
function getValue(key) {
|
|
49
|
-
return key[0] === "$" ? vars[key.slice(1)] : key;
|
|
36
|
+
let node = graph[findFirst(graph, key)];
|
|
37
|
+
if (!node || node.key !== key || node.end) {
|
|
38
|
+
node = { key, version, value: 1 };
|
|
39
|
+
merge(graph, [node]);
|
|
40
|
+
delete node.value;
|
|
41
|
+
node.children = [];
|
|
50
42
|
}
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
if (!node.children) {
|
|
44
|
+
throw Error("linkGraph.unexpected_leaf " + key);
|
|
53
45
|
}
|
|
46
|
+
linkGraphDef(node.children, rest, def, node.version);
|
|
47
|
+
}
|
|
48
|
+
function makeRef(def) {
|
|
54
49
|
function replacePlaceholders(key) {
|
|
55
50
|
if (typeof key === "string" && key[0] === "$" && key[1] === "$") {
|
|
56
|
-
return unwrap(rootGraph,
|
|
51
|
+
return unwrap(rootGraph, key.slice(2).split("."));
|
|
57
52
|
}
|
|
58
53
|
if (Array.isArray(key)) {
|
|
59
54
|
return key.map(replacePlaceholders);
|
|
@@ -64,42 +59,60 @@ function linkGraph(rootGraph, defs) {
|
|
|
64
59
|
result[prop] = replacePlaceholders(key[prop]);
|
|
65
60
|
return result;
|
|
66
61
|
}
|
|
67
|
-
return
|
|
62
|
+
return key;
|
|
68
63
|
}
|
|
69
64
|
const ref = def.map(replacePlaceholders);
|
|
70
65
|
return ref;
|
|
71
66
|
}
|
|
72
67
|
}
|
|
73
68
|
function prepQueryLinks(rootQuery, defs) {
|
|
74
|
-
return defs.
|
|
69
|
+
return defs.flatMap(({ path, def }) => prepQueryDef(rootQuery, path, def));
|
|
75
70
|
function prepQueryDef(query, path, def, vars = {}, version = 0) {
|
|
76
71
|
var _a;
|
|
77
72
|
const [key, ...rest] = path;
|
|
78
73
|
if (rest.length === 0) {
|
|
79
74
|
const ix = findFirst(query, key);
|
|
80
75
|
if (((_a = query[ix]) == null ? void 0 : _a.key) !== key)
|
|
81
|
-
return
|
|
82
|
-
query.splice(ix, 1);
|
|
76
|
+
return [];
|
|
77
|
+
const [{ children: subQuery }] = query.splice(ix, 1);
|
|
83
78
|
add(rootQuery, getDefQuery(def, vars, version));
|
|
84
|
-
|
|
79
|
+
const [range, filter] = splitRef(def);
|
|
80
|
+
if (range && subQuery.length) {
|
|
81
|
+
return subQuery.map((node) => {
|
|
82
|
+
return {
|
|
83
|
+
path: path.concat(node.key),
|
|
84
|
+
def: prepareDef(def.slice(0, -1).concat(__spreadValues(__spreadValues(__spreadValues({}, filter), decodeArgs(node)), range)), vars)
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
return [{ path, def: prepareDef(def, vars) }];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function prefixKey(defs2, key2) {
|
|
92
|
+
return defs2.map(({ path: path2, def: def2 }) => ({
|
|
93
|
+
path: [key2, ...path2],
|
|
94
|
+
def: def2
|
|
95
|
+
}));
|
|
85
96
|
}
|
|
97
|
+
let used = [];
|
|
86
98
|
if (key[0] !== "$") {
|
|
87
99
|
const node = query[findFirst(query, key)];
|
|
88
100
|
if (!node || node.key !== key || !node.children)
|
|
89
|
-
return;
|
|
90
|
-
|
|
101
|
+
return [];
|
|
102
|
+
used = prepQueryDef(node.children, rest, def, vars, node.version);
|
|
103
|
+
used = prefixKey(used, node.key);
|
|
91
104
|
} else {
|
|
92
|
-
let used = false;
|
|
93
105
|
for (const node of query) {
|
|
94
106
|
if (!isBranch(node))
|
|
95
107
|
continue;
|
|
96
108
|
let usedHere = prepQueryDef(node.children, rest, def, __spreadProps(__spreadValues({}, vars), {
|
|
97
109
|
[key.slice(1)]: node.key
|
|
98
110
|
}), node.version);
|
|
99
|
-
|
|
111
|
+
usedHere = prefixKey(usedHere, node.key);
|
|
112
|
+
used = used.concat(usedHere);
|
|
100
113
|
}
|
|
101
|
-
return used;
|
|
102
114
|
}
|
|
115
|
+
return used;
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
function getDefQuery(def, vars, version) {
|
|
@@ -118,14 +131,34 @@ function getDefQuery(def, vars, version) {
|
|
|
118
131
|
key.map(addDefQueries);
|
|
119
132
|
}
|
|
120
133
|
if (typeof key === "object" && key) {
|
|
121
|
-
const result = {};
|
|
122
134
|
for (const prop in key)
|
|
123
|
-
|
|
135
|
+
addDefQueries(key[prop]);
|
|
124
136
|
}
|
|
125
137
|
}
|
|
126
138
|
def.map(addDefQueries);
|
|
127
139
|
return defQuery;
|
|
128
140
|
}
|
|
141
|
+
function prepareDef(def, vars) {
|
|
142
|
+
function getValue(key) {
|
|
143
|
+
return key[0] === "$" ? vars[key.slice(1)] : key;
|
|
144
|
+
}
|
|
145
|
+
function replacePlaceholders(key) {
|
|
146
|
+
if (typeof key === "string" && key[0] === "$" && key[1] === "$") {
|
|
147
|
+
return "$$" + key.slice(2).split(".").map(getValue).join(".");
|
|
148
|
+
}
|
|
149
|
+
if (Array.isArray(key)) {
|
|
150
|
+
return key.map(replacePlaceholders);
|
|
151
|
+
}
|
|
152
|
+
if (typeof key === "object" && key) {
|
|
153
|
+
const result = {};
|
|
154
|
+
for (const prop in key)
|
|
155
|
+
result[prop] = replacePlaceholders(key[prop]);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
return getValue(key);
|
|
159
|
+
}
|
|
160
|
+
return def.map(replacePlaceholders);
|
|
161
|
+
}
|
|
129
162
|
var index = (defs) => (store) => {
|
|
130
163
|
const prefix = store.path;
|
|
131
164
|
const defEntries = Object.entries(defs).map(([prop, def]) => ({
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graffy/link",
|
|
3
3
|
"description": "Graffy module for constructing links using an intuitive, declarative notation.",
|
|
4
4
|
"author": "aravind (https://github.com/aravindet)",
|
|
5
|
-
"version": "0.15.
|
|
5
|
+
"version": "0.15.13-alpha.1",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
"import": "./index.mjs",
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@graffy/common": "0.15.
|
|
19
|
+
"@graffy/common": "0.15.13-alpha.1"
|
|
20
20
|
}
|
|
21
21
|
}
|