@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.
Files changed (4) hide show
  1. package/Readme.md +38 -0
  2. package/index.cjs +69 -36
  3. package/index.mjs +70 -37
  4. 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, vars = {}, version = 0) {
26
+ function linkGraphDef(graph, path, def, version = 0) {
27
27
  const [key, ...rest] = path;
28
28
  if (rest.length === 0) {
29
- common.merge(graph, [{ key, path: common.encodePath(makeRef(def, vars)), version }]);
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
- if (key[0] !== "$") {
33
- const node = graph[common.findFirst(graph, key)];
34
- if (!node || node.key !== key || !node.children) {
35
- throw Error("linkGraph.no_path " + key);
36
- }
37
- linkGraphDef(node.children, rest, def, vars, node.version);
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
- function getPath(template) {
53
- return template.split(".").map(getValue);
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, getPath(key.slice(2)));
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 getValue(key);
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.filter(({ path, def }) => prepQueryDef(rootQuery, path, def));
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 false;
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
- return true;
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
- return prepQueryDef(node.children, rest, def, vars, node.version);
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
- used = used || usedHere;
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
- result[prop] = addDefQueries(key[prop]);
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 { merge, encodePath, findFirst, isBranch, unwrap, add, wrapValue, wrap } from "@graffy/common";
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, vars = {}, version = 0) {
25
+ function linkGraphDef(graph, path, def, version = 0) {
26
26
  const [key, ...rest] = path;
27
27
  if (rest.length === 0) {
28
- merge(graph, [{ key, path: encodePath(makeRef(def, vars)), version }]);
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
- if (key[0] !== "$") {
32
- const node = graph[findFirst(graph, key)];
33
- if (!node || node.key !== key || !node.children) {
34
- throw Error("linkGraph.no_path " + key);
35
- }
36
- linkGraphDef(node.children, rest, def, vars, node.version);
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
- function getPath(template) {
52
- return template.split(".").map(getValue);
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, getPath(key.slice(2)));
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 getValue(key);
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.filter(({ path, def }) => prepQueryDef(rootQuery, path, def));
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 false;
82
- query.splice(ix, 1);
76
+ return [];
77
+ const [{ children: subQuery }] = query.splice(ix, 1);
83
78
  add(rootQuery, getDefQuery(def, vars, version));
84
- return true;
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
- return prepQueryDef(node.children, rest, def, vars, node.version);
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
- used = used || usedHere;
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
- result[prop] = addDefQueries(key[prop]);
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.11",
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.11"
19
+ "@graffy/common": "0.15.13-alpha.1"
20
20
  }
21
21
  }