@graffy/common 0.18.1-alpha.2 → 0.19.1-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/coding/alphabet.js +5 -0
- package/coding/args.d.ts +5 -0
- package/coding/args.js +93 -0
- package/coding/base64.d.ts +2 -0
- package/coding/base64.js +41 -0
- package/coding/decodeTree.d.ts +2 -0
- package/coding/decodeTree.js +197 -0
- package/coding/decorate.js +211 -0
- package/coding/encodeTree.d.ts +2 -0
- package/coding/encodeTree.js +248 -0
- package/coding/id.js +4 -0
- package/coding/index.d.ts +8 -0
- package/{types/coding/index.d.ts → coding/index.js} +4 -4
- package/coding/number.d.ts +2 -0
- package/coding/number.js +39 -0
- package/coding/pack.d.ts +2 -0
- package/coding/pack.js +71 -0
- package/coding/path.d.ts +3 -0
- package/coding/path.js +41 -0
- package/coding/string.d.ts +2 -0
- package/coding/string.js +8 -0
- package/coding/struct.d.ts +11 -0
- package/coding/struct.js +163 -0
- package/index.d.ts +6 -0
- package/node/find.d.ts +2 -0
- package/node/find.js +14 -0
- package/node/index.d.ts +2 -0
- package/{types/node/index.d.ts → node/index.js} +1 -0
- package/node/types.d.ts +6 -0
- package/node/types.js +18 -0
- package/object.d.ts +4 -0
- package/object.js +86 -0
- package/ops/add.js +64 -0
- package/{types/ops → ops}/finalize.d.ts +1 -1
- package/ops/finalize.js +19 -0
- package/{types/ops → ops}/getKnown.d.ts +1 -4
- package/ops/getKnown.js +24 -0
- package/ops/index.d.ts +9 -0
- package/ops/merge.d.ts +3 -0
- package/ops/merge.js +112 -0
- package/ops/path.d.ts +6 -0
- package/ops/path.js +91 -0
- package/ops/setVersion.js +29 -0
- package/ops/sieve.d.ts +3 -0
- package/ops/sieve.js +193 -0
- package/{types/ops → ops}/slice.d.ts +2 -6
- package/ops/slice.js +153 -0
- package/ops/step.d.ts +6 -0
- package/ops/step.js +61 -0
- package/package.json +14 -8
- package/stream/index.d.ts +2 -0
- package/stream/makeWatcher.d.ts +10 -0
- package/stream/makeWatcher.js +19 -0
- package/stream/mergeStreams.js +20 -0
- package/util.d.ts +15 -0
- package/util.js +106 -0
- package/index.cjs +0 -1673
- package/index.mjs +0 -1673
- package/types/coding/args.d.ts +0 -5
- package/types/coding/base64.d.ts +0 -2
- package/types/coding/decodeTree.d.ts +0 -2
- package/types/coding/encodeTree.d.ts +0 -2
- package/types/coding/number.d.ts +0 -2
- package/types/coding/pack.d.ts +0 -2
- package/types/coding/path.d.ts +0 -3
- package/types/coding/string.d.ts +0 -2
- package/types/coding/struct.d.ts +0 -11
- package/types/node/find.d.ts +0 -2
- package/types/node/types.d.ts +0 -6
- package/types/object.d.ts +0 -4
- package/types/ops/merge.d.ts +0 -3
- package/types/ops/path.d.ts +0 -6
- package/types/ops/sieve.d.ts +0 -3
- package/types/ops/step.d.ts +0 -6
- package/types/stream/makeWatcher.d.ts +0 -16
- package/types/util.d.ts +0 -15
- package/{types/coding → coding}/alphabet.d.ts +0 -0
- package/{types/coding → coding}/decorate.d.ts +0 -0
- package/{types/coding → coding}/id.d.ts +0 -0
- package/{types/index.d.ts → index.js} +0 -0
- package/{types/ops → ops}/add.d.ts +0 -0
- package/{types/ops/index.d.ts → ops/index.js} +1 -1
- /package/{types/ops → ops}/setVersion.d.ts +0 -0
- /package/{types/stream/index.d.ts → stream/index.js} +0 -0
- /package/{types/stream → stream}/mergeStreams.d.ts +0 -0
package/coding/args.d.ts
ADDED
package/coding/args.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { keyAfter, keyBefore, keyStep } from "../ops/step.js";
|
|
2
|
+
import { cmp, errIf, isDef, isEmpty, isMaxKey, isMinKey, isPlainObject, MAX_KEY, MIN_KEY, } from "../util.js";
|
|
3
|
+
import { decode as decodeValue, encode as encodeValue } from "./struct.js";
|
|
4
|
+
function decodeBound(bound) {
|
|
5
|
+
const { key, step } = keyStep(bound);
|
|
6
|
+
if (isMinKey(key) || isMaxKey(key))
|
|
7
|
+
return { step };
|
|
8
|
+
const value = decodeValue(key);
|
|
9
|
+
return { key: value, step };
|
|
10
|
+
}
|
|
11
|
+
const pageProps = {
|
|
12
|
+
$all: 1,
|
|
13
|
+
$first: 1,
|
|
14
|
+
$last: 1,
|
|
15
|
+
$after: 1,
|
|
16
|
+
$before: 1,
|
|
17
|
+
$since: 1,
|
|
18
|
+
$until: 1,
|
|
19
|
+
$cursor: 1,
|
|
20
|
+
};
|
|
21
|
+
export function splitArgs(arg) {
|
|
22
|
+
const page = {};
|
|
23
|
+
const filter = {};
|
|
24
|
+
for (const p in arg)
|
|
25
|
+
(p in pageProps ? page : filter)[p] = arg[p];
|
|
26
|
+
return [
|
|
27
|
+
isEmpty(page) ? undefined : page,
|
|
28
|
+
isEmpty(filter) ? undefined : filter,
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
export function encode(arg) {
|
|
32
|
+
if (!isPlainObject(arg))
|
|
33
|
+
return { key: encodeValue(arg) };
|
|
34
|
+
const [page, filter] = splitArgs(arg);
|
|
35
|
+
errIf('page_and_filter', page && filter, arg);
|
|
36
|
+
if (!page)
|
|
37
|
+
return { key: encodeValue(filter || {}) };
|
|
38
|
+
const { $cursor, ...range } = page;
|
|
39
|
+
// @ts-expect-error
|
|
40
|
+
const { $first, $all, $last, $after, $before, $since, $until } = range;
|
|
41
|
+
const hasRange = !isEmpty(range);
|
|
42
|
+
errIf('first_and_last', isDef($first) && isDef($last), arg);
|
|
43
|
+
errIf('all_and_last', isDef($all) && isDef($last), arg);
|
|
44
|
+
errIf('all_and_first', isDef($first) && isDef($all), arg);
|
|
45
|
+
errIf('after_and_since', isDef($after) && isDef($since), arg);
|
|
46
|
+
errIf('before_and_until', isDef($before) && isDef($until), arg);
|
|
47
|
+
errIf('cursor_and_range_arg', isDef($cursor) && hasRange, arg);
|
|
48
|
+
let [key, end] = hasRange ? [MIN_KEY, MAX_KEY] : [];
|
|
49
|
+
if (isDef($cursor))
|
|
50
|
+
key = encodeValue($cursor);
|
|
51
|
+
if (isDef($after))
|
|
52
|
+
key = keyAfter(encodeValue($after));
|
|
53
|
+
if (isDef($before))
|
|
54
|
+
end = keyBefore(encodeValue($before));
|
|
55
|
+
if (isDef($since))
|
|
56
|
+
key = encodeValue($since);
|
|
57
|
+
if (isDef($until))
|
|
58
|
+
end = encodeValue($until);
|
|
59
|
+
if (isDef($last))
|
|
60
|
+
[key, end] = [end, key];
|
|
61
|
+
const node = { key };
|
|
62
|
+
if (isDef(end))
|
|
63
|
+
node.end = end;
|
|
64
|
+
if ($first || $last)
|
|
65
|
+
node.limit = $first || $last;
|
|
66
|
+
return node;
|
|
67
|
+
}
|
|
68
|
+
export function decode(node) {
|
|
69
|
+
const { key, end, limit } = node;
|
|
70
|
+
errIf('no_key', !isDef(key));
|
|
71
|
+
errIf('limit_without_end', isDef(limit) && !isDef(end));
|
|
72
|
+
const kParts = decodeBound(key);
|
|
73
|
+
if (!isDef(end) || cmp(key, end) === 0)
|
|
74
|
+
return kParts.key;
|
|
75
|
+
const eParts = decodeBound(end);
|
|
76
|
+
const reverse = cmp(key, end) > 0;
|
|
77
|
+
const [lower, upper] = reverse ? [eParts, kParts] : [kParts, eParts];
|
|
78
|
+
const args = {};
|
|
79
|
+
if (limit) {
|
|
80
|
+
args[reverse ? '$last' : '$first'] = limit;
|
|
81
|
+
}
|
|
82
|
+
else if ((isMinKey(key) && isMaxKey(end)) ||
|
|
83
|
+
(isMinKey(end) && isMaxKey(key))) {
|
|
84
|
+
args.$all = true;
|
|
85
|
+
}
|
|
86
|
+
if (isDef(lower.key) && !isMinKey(lower.key)) {
|
|
87
|
+
args[lower.step === 1 ? '$after' : '$since'] = lower.key;
|
|
88
|
+
}
|
|
89
|
+
if (isDef(upper.key) && !isMaxKey(upper.key)) {
|
|
90
|
+
args[upper.step === -1 ? '$before' : '$until'] = upper.key;
|
|
91
|
+
}
|
|
92
|
+
return args;
|
|
93
|
+
}
|
package/coding/base64.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { addStringify } from "../util.js";
|
|
2
|
+
import alpha from "./alphabet.js";
|
|
3
|
+
function getByte(view, offset) {
|
|
4
|
+
return offset < view.byteLength ? view.getUint8(offset) : 0;
|
|
5
|
+
}
|
|
6
|
+
function getChar(string, offset) {
|
|
7
|
+
return offset < string.length ? alpha.indexOf(string[offset]) : 0;
|
|
8
|
+
}
|
|
9
|
+
export function encode(u8Arr) {
|
|
10
|
+
const { buffer, byteOffset, byteLength } = u8Arr;
|
|
11
|
+
const view = new DataView(buffer, byteOffset, byteLength);
|
|
12
|
+
let str = '';
|
|
13
|
+
for (let i = 0; i < view.byteLength; i += 3) {
|
|
14
|
+
let value = (getByte(view, i) << 16) +
|
|
15
|
+
(getByte(view, i + 1) << 8) +
|
|
16
|
+
getByte(view, i + 2);
|
|
17
|
+
let gstr = '';
|
|
18
|
+
for (let j = 0; j < 4; j++) {
|
|
19
|
+
gstr = alpha[value & 0x3f] + gstr;
|
|
20
|
+
value = (value >> 6) | 0;
|
|
21
|
+
}
|
|
22
|
+
str += gstr;
|
|
23
|
+
}
|
|
24
|
+
return str.substring(0, Math.ceil((view.byteLength * 4) / 3));
|
|
25
|
+
}
|
|
26
|
+
export function decode(string, start = 0) {
|
|
27
|
+
const buffer = new ArrayBuffer(Math.floor(((string.length - start) * 3) / 4));
|
|
28
|
+
const view = new DataView(buffer);
|
|
29
|
+
for (let i = start; i < string.length; i += 4) {
|
|
30
|
+
let value = (getChar(string, i) << 18) +
|
|
31
|
+
(getChar(string, i + 1) << 12) +
|
|
32
|
+
(getChar(string, i + 2) << 6) +
|
|
33
|
+
getChar(string, i + 3);
|
|
34
|
+
for (let j = (i * 3) / 4 + 2; j >= (i * 3) / 4; j--) {
|
|
35
|
+
if (j < view.byteLength)
|
|
36
|
+
view.setUint8(j, value & 0xff);
|
|
37
|
+
value = (value >> 8) | 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return addStringify(new Uint8Array(buffer));
|
|
41
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { isBranch, isLink, isPrefix, isRange } from "../node/index.js";
|
|
2
|
+
import { keyAfter } from "../ops/index.js";
|
|
3
|
+
import { clone, cmp, isDef, isEmpty, isMaxKey, isMinKey } from "../util.js";
|
|
4
|
+
import { decode as decodeArgs, splitArgs } from "./args.js";
|
|
5
|
+
import { decode as decodePath } from "./path.js";
|
|
6
|
+
const PRE_CHI_PUT = Symbol('PREFIX_CHILDREN_$PUT');
|
|
7
|
+
/**
|
|
8
|
+
@param {any[]} nodes
|
|
9
|
+
@param {{ isGraph?: boolean }} options
|
|
10
|
+
*/
|
|
11
|
+
function decode(nodes = [], { isGraph } = {}) {
|
|
12
|
+
function decodeChildren(nodes) {
|
|
13
|
+
let result = [];
|
|
14
|
+
let allStrs = true;
|
|
15
|
+
let allNums = true;
|
|
16
|
+
//Pushes objects to the result set and updates allStrs and allNums.
|
|
17
|
+
function pushResult(...objects) {
|
|
18
|
+
for (const object of objects) {
|
|
19
|
+
if (isDef(object) && !Number.isInteger(object.$key))
|
|
20
|
+
allNums = false;
|
|
21
|
+
if (isDef(object) && typeof object.$key !== 'string')
|
|
22
|
+
allStrs = false;
|
|
23
|
+
}
|
|
24
|
+
result.push(...objects);
|
|
25
|
+
}
|
|
26
|
+
const putRanges = [];
|
|
27
|
+
const prefixChildPuts = [];
|
|
28
|
+
let lastNode = null;
|
|
29
|
+
// Graphs only: Constructs the $put array. Returns true if this is a range
|
|
30
|
+
// node that does not require to be added to the results.
|
|
31
|
+
function addPutRange({ key, end }) {
|
|
32
|
+
if (lastNode) {
|
|
33
|
+
if (lastNode.end) {
|
|
34
|
+
if (cmp(key, keyAfter(lastNode.end)) === 0) {
|
|
35
|
+
lastNode.end = end || key;
|
|
36
|
+
return end && cmp(end, key) !== 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
if (cmp(key, keyAfter(lastNode.key)) === 0) {
|
|
41
|
+
key = lastNode.key;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (end && cmp(key, end) !== 0) {
|
|
46
|
+
lastNode = { key, end };
|
|
47
|
+
putRanges.push(lastNode);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
lastNode = { key };
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
for (const node of nodes) {
|
|
54
|
+
if (isGraph && addPutRange(node))
|
|
55
|
+
continue;
|
|
56
|
+
if (isPrefix(node)) {
|
|
57
|
+
const decodedChildren = decodePrefixNode(node);
|
|
58
|
+
if (PRE_CHI_PUT in decodedChildren) {
|
|
59
|
+
prefixChildPuts.push(...decodedChildren[PRE_CHI_PUT]);
|
|
60
|
+
}
|
|
61
|
+
pushResult(...decodedChildren);
|
|
62
|
+
}
|
|
63
|
+
else if (isGraph && isRange(node))
|
|
64
|
+
pushResult(decodeRangeNode(node));
|
|
65
|
+
else if (isBranch(node))
|
|
66
|
+
pushResult(decodeBranchNode(node));
|
|
67
|
+
else if (isLink(node))
|
|
68
|
+
pushResult(decodeLinkNode(node));
|
|
69
|
+
else
|
|
70
|
+
pushResult(decodeLeafNode(node));
|
|
71
|
+
}
|
|
72
|
+
// Use a simplified format if all the keys are strings or it's a plain array.
|
|
73
|
+
if (allStrs ||
|
|
74
|
+
(allNums &&
|
|
75
|
+
putRanges.length === 1 &&
|
|
76
|
+
cmp(putRanges[0].key, 0) === 0 &&
|
|
77
|
+
cmp(putRanges[0].end, Number.POSITIVE_INFINITY) === 0)) {
|
|
78
|
+
result = result.reduce((collection, item) => {
|
|
79
|
+
if (Array.isArray(item)) {
|
|
80
|
+
collection[item.$key] = item;
|
|
81
|
+
delete item.$key;
|
|
82
|
+
return collection;
|
|
83
|
+
}
|
|
84
|
+
let { $key, $val } = item;
|
|
85
|
+
delete item.$key;
|
|
86
|
+
delete item.$val;
|
|
87
|
+
if ($val === null) {
|
|
88
|
+
$val = { $val };
|
|
89
|
+
}
|
|
90
|
+
else if (typeof $val === 'object') {
|
|
91
|
+
$val = clone($val);
|
|
92
|
+
Object.defineProperty($val, '$val', { value: true });
|
|
93
|
+
}
|
|
94
|
+
// biome-ignore format: tertnary chain
|
|
95
|
+
collection[$key] = (isDef($val) ? $val :
|
|
96
|
+
!isEmpty(item) || item.$ref || item.$put ? item :
|
|
97
|
+
isGraph ? null : true);
|
|
98
|
+
return collection;
|
|
99
|
+
}, allStrs ? {} : []);
|
|
100
|
+
}
|
|
101
|
+
if (isGraph && putRanges.length) {
|
|
102
|
+
if (isMinKey(putRanges[0].key) && isMaxKey(putRanges[0].end)) {
|
|
103
|
+
Object.defineProperty(result, '$put', { value: true });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
Object.defineProperty(result, '$put', {
|
|
107
|
+
value: putRanges
|
|
108
|
+
.map((rNode) => decodeArgs(rNode))
|
|
109
|
+
.concat(prefixChildPuts),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (prefixChildPuts.length) {
|
|
114
|
+
Object.defineProperty(result, '$put', { value: prefixChildPuts });
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function decodePrefixNode(node) {
|
|
119
|
+
let args = decodeArgs(node);
|
|
120
|
+
if (!args)
|
|
121
|
+
args = {};
|
|
122
|
+
if (typeof args === 'string') {
|
|
123
|
+
throw Error(`decode.unencoded_prefix: ${args}`);
|
|
124
|
+
}
|
|
125
|
+
if (isLink(node)) {
|
|
126
|
+
args.$all = true;
|
|
127
|
+
const $ref = decodePath(node.path);
|
|
128
|
+
const lastKey = $ref[$ref.length - 1];
|
|
129
|
+
if (typeof lastKey === 'string') {
|
|
130
|
+
throw Error(`decode.unencoded_prefix_ref: ${node.path}`);
|
|
131
|
+
}
|
|
132
|
+
lastKey.$all = true;
|
|
133
|
+
const linkObject = { $key: args };
|
|
134
|
+
Object.defineProperty(linkObject, '$ref', { value: $ref });
|
|
135
|
+
return [linkObject];
|
|
136
|
+
}
|
|
137
|
+
/** @type {any[] & {$put?: any}} */
|
|
138
|
+
const children = decodeChildren(node.children);
|
|
139
|
+
if (!Array.isArray(children)) {
|
|
140
|
+
throw Error(`decode.prefix_without_encoded_child_keys:${node.key}`);
|
|
141
|
+
}
|
|
142
|
+
for (const child of children) {
|
|
143
|
+
if (typeof child.$key === 'string') {
|
|
144
|
+
throw Error(`decode.prefix_with_unencoded_child_key:${child.$key}`);
|
|
145
|
+
}
|
|
146
|
+
if (!splitArgs(child.$key)[0]) {
|
|
147
|
+
// splitArgs returns [page, filter]. If page is blank, it indicates
|
|
148
|
+
// we have a bare cursor.
|
|
149
|
+
child.$key = { $cursor: child.$key };
|
|
150
|
+
}
|
|
151
|
+
child.$key = { ...args, ...child.$key };
|
|
152
|
+
}
|
|
153
|
+
if (children.$put === true) {
|
|
154
|
+
children[PRE_CHI_PUT] = [{ ...args, $all: true }];
|
|
155
|
+
}
|
|
156
|
+
else if (Array.isArray(children.$put)) {
|
|
157
|
+
children[PRE_CHI_PUT] = children.$put.map((rarg) => ({
|
|
158
|
+
...args,
|
|
159
|
+
...rarg,
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
else if (isDef(children.$put)) {
|
|
163
|
+
children[PRE_CHI_PUT] = [{ ...args, ...children.$put }];
|
|
164
|
+
}
|
|
165
|
+
return children;
|
|
166
|
+
}
|
|
167
|
+
function decodeBranchNode(node) {
|
|
168
|
+
const child = decodeChildren(node.children);
|
|
169
|
+
child.$key = decodeArgs(node);
|
|
170
|
+
return child;
|
|
171
|
+
}
|
|
172
|
+
function decodeLeafNode(node) {
|
|
173
|
+
const child = isGraph ? { $val: node.value } : {};
|
|
174
|
+
child.$key = decodeArgs(node);
|
|
175
|
+
return child;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
Only for graphs;
|
|
179
|
+
*/
|
|
180
|
+
function decodeRangeNode(node) {
|
|
181
|
+
if (cmp(node.key, node.end) === 0) {
|
|
182
|
+
return { $key: decodeArgs({ key: node.key }) };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function decodeLinkNode(node) {
|
|
186
|
+
const linkObject = { $key: decodeArgs(node) };
|
|
187
|
+
Object.defineProperty(linkObject, '$ref', { value: decodePath(node.path) });
|
|
188
|
+
return linkObject;
|
|
189
|
+
}
|
|
190
|
+
return decodeChildren(nodes);
|
|
191
|
+
}
|
|
192
|
+
export function decodeGraph(graph) {
|
|
193
|
+
return decode(graph, { isGraph: true });
|
|
194
|
+
}
|
|
195
|
+
export function decodeQuery(query) {
|
|
196
|
+
return decode(query, { isGraph: false });
|
|
197
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { findFirst, isRange } from "../node/index.js";
|
|
2
|
+
import { getNodeValue, IS_VAL, unwrap } from "../ops/index.js";
|
|
3
|
+
import { cmp, isDef, isEmpty, isMinKey, isPlainObject, MIN_KEY, } from "../util.js";
|
|
4
|
+
import { decode as decodeArgs, encode as encodeArgs, splitArgs, } from "./args.js";
|
|
5
|
+
import { decodeGraph } from "./decodeTree.js";
|
|
6
|
+
import { decode as decodePath, encode as encodePath, splitRef, } from "./path.js";
|
|
7
|
+
const REF = Symbol();
|
|
8
|
+
const PRE = Symbol();
|
|
9
|
+
export default function decorate(rootGraph, rootQuery) {
|
|
10
|
+
// console.log('Decorating', rootGraph, rootQuery);
|
|
11
|
+
function construct(plumGraph, query) {
|
|
12
|
+
if (plumGraph === null)
|
|
13
|
+
return null;
|
|
14
|
+
if (!isDef(plumGraph))
|
|
15
|
+
plumGraph = [];
|
|
16
|
+
if (query.$key)
|
|
17
|
+
query = [query];
|
|
18
|
+
// console.log('Constructing', plumGraph, query);
|
|
19
|
+
let graph;
|
|
20
|
+
if (query.$ref) {
|
|
21
|
+
const { $ref, ...props } = query;
|
|
22
|
+
const [range, filter] = splitRef($ref);
|
|
23
|
+
const path = encodePath($ref);
|
|
24
|
+
const targetPlumGraph = unwrap(rootGraph, path);
|
|
25
|
+
if (targetPlumGraph) {
|
|
26
|
+
if (range)
|
|
27
|
+
targetPlumGraph[PRE] = filter;
|
|
28
|
+
graph = construct(targetPlumGraph, range ? { $key: range, ...props } : props);
|
|
29
|
+
Object.defineProperty(graph, '$ref', { value: $ref });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (Array.isArray(query)) {
|
|
33
|
+
let pageKey;
|
|
34
|
+
graph = query.flatMap((item, i) => {
|
|
35
|
+
if (!item?.$key) {
|
|
36
|
+
// This is an integer-indexed array.
|
|
37
|
+
return construct(descend(plumGraph, i), item);
|
|
38
|
+
}
|
|
39
|
+
const { $key, $chi, ...props } = item;
|
|
40
|
+
const subQuery = $chi || (isEmpty(props) ? 1 : props);
|
|
41
|
+
if (!(isPlainObject($key) && splitArgs($key)[0])) {
|
|
42
|
+
// This is a non-string argument without pagination
|
|
43
|
+
return construct(descend(plumGraph, $key), subQuery);
|
|
44
|
+
}
|
|
45
|
+
// This is a pagination query.
|
|
46
|
+
if (pageKey) {
|
|
47
|
+
throw Error(`decorate.multi_range_query:${JSON.stringify({ $key, pageKey })}`);
|
|
48
|
+
}
|
|
49
|
+
pageKey = $key;
|
|
50
|
+
const children = slice(plumGraph, $key);
|
|
51
|
+
return children
|
|
52
|
+
.filter((node) => !isRange(node))
|
|
53
|
+
.map((node) => {
|
|
54
|
+
const $key = decodeArgs(node);
|
|
55
|
+
const subResult = construct(getValue(node), subQuery);
|
|
56
|
+
if (typeof subResult === 'object' && subResult) {
|
|
57
|
+
subResult.$key =
|
|
58
|
+
children[PRE] && !isMinKey(children[PRE])
|
|
59
|
+
? { ...children[PRE], $cursor: $key }
|
|
60
|
+
: $key;
|
|
61
|
+
}
|
|
62
|
+
return subResult;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
// .filter(Boolean);
|
|
66
|
+
if (pageKey)
|
|
67
|
+
addPageMeta(graph, pageKey);
|
|
68
|
+
}
|
|
69
|
+
else if (typeof query === 'object') {
|
|
70
|
+
graph = {};
|
|
71
|
+
for (const prop in query) {
|
|
72
|
+
graph[prop] = construct(descend(plumGraph, prop), query[prop]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (query) {
|
|
76
|
+
if (Array.isArray(plumGraph) && !plumGraph.length) {
|
|
77
|
+
graph = undefined;
|
|
78
|
+
}
|
|
79
|
+
else if (typeof plumGraph !== 'object' || !plumGraph) {
|
|
80
|
+
graph = plumGraph;
|
|
81
|
+
}
|
|
82
|
+
else if (plumGraph[IS_VAL]) {
|
|
83
|
+
graph = Array.isArray(plumGraph)
|
|
84
|
+
? plumGraph.slice(0)
|
|
85
|
+
: { ...plumGraph };
|
|
86
|
+
graph.$val = true;
|
|
87
|
+
}
|
|
88
|
+
else if (Array.isArray(plumGraph)) {
|
|
89
|
+
graph = deValNull(decodeGraph(plumGraph));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
throw Error('decorate.unexpected_graph');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (plumGraph[REF]) {
|
|
96
|
+
Object.defineProperty(graph, '$ref', {
|
|
97
|
+
value: decodePath(plumGraph[REF]),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return graph;
|
|
101
|
+
}
|
|
102
|
+
function descend(children, $key) {
|
|
103
|
+
const key = ArrayBuffer.isView($key) ? $key : encodeArgs($key).key;
|
|
104
|
+
if (!Array.isArray(children))
|
|
105
|
+
return null;
|
|
106
|
+
const ix = findFirst(children, key);
|
|
107
|
+
const node = children[ix];
|
|
108
|
+
if (!node)
|
|
109
|
+
return;
|
|
110
|
+
if (isRange(node) && node.end >= key)
|
|
111
|
+
return null;
|
|
112
|
+
if (cmp(node.key, key) !== 0)
|
|
113
|
+
return;
|
|
114
|
+
const result = getValue(node);
|
|
115
|
+
if (node.prefix)
|
|
116
|
+
result[PRE] = $key;
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
function getValue(node) {
|
|
120
|
+
let result;
|
|
121
|
+
if (node.path) {
|
|
122
|
+
result = unwrap(rootGraph, node.path);
|
|
123
|
+
if (typeof result === 'object' && result)
|
|
124
|
+
result[REF] = node.path;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
result = getNodeValue(node);
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
function slice(children, $key) {
|
|
132
|
+
const [range, filter] = splitArgs($key);
|
|
133
|
+
if (isDef(filter)) {
|
|
134
|
+
children = descend(children, filter);
|
|
135
|
+
}
|
|
136
|
+
else if (isMinKey(children[0].key) && children[0].prefix) {
|
|
137
|
+
children = descend(children, MIN_KEY);
|
|
138
|
+
}
|
|
139
|
+
const { key, end, limit = Number.POSITIVE_INFINITY } = encodeArgs(range);
|
|
140
|
+
const ix = findFirst(children, key);
|
|
141
|
+
let i = ix;
|
|
142
|
+
let result;
|
|
143
|
+
if (cmp(key, end) < 0) {
|
|
144
|
+
for (let n = 0; i < children.length && n < limit; i++) {
|
|
145
|
+
if (!isRange(children[i]))
|
|
146
|
+
n++;
|
|
147
|
+
}
|
|
148
|
+
// console.log('slicing fwd', children, ix, i);
|
|
149
|
+
result = children.slice(ix, i);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
for (let n = 0; i >= 0 && n < limit; i--) {
|
|
153
|
+
if (!isRange(children[i]))
|
|
154
|
+
n++;
|
|
155
|
+
}
|
|
156
|
+
// console.log('slicing bkd', children, i + 1, ix + 1);
|
|
157
|
+
result = children.slice(i + 1, ix + 1);
|
|
158
|
+
}
|
|
159
|
+
if (children[REF])
|
|
160
|
+
result[REF] = children[REF];
|
|
161
|
+
if (children[PRE])
|
|
162
|
+
result[PRE] = children[PRE];
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
const result = construct(rootGraph, rootQuery);
|
|
166
|
+
// console.log('Decorate', result, rootGraph, rootQuery);
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
// Replace $val: null produced by
|
|
170
|
+
function deValNull(graph) {
|
|
171
|
+
if (typeof graph !== 'object' || !graph)
|
|
172
|
+
return graph;
|
|
173
|
+
if ('$val' in graph && graph.$val !== true)
|
|
174
|
+
return graph.$val;
|
|
175
|
+
// Important: update graph in-place to avoid losing non-enumerable props.
|
|
176
|
+
for (const prop in graph)
|
|
177
|
+
graph[prop] = deValNull(graph[prop]);
|
|
178
|
+
return graph;
|
|
179
|
+
}
|
|
180
|
+
function addPageMeta(graph, args) {
|
|
181
|
+
if (args.$all) {
|
|
182
|
+
Object.assign(graph, { $page: args, $prev: null, $next: null });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const [{ $first, $last, ...bounds }, filter] = splitArgs(args);
|
|
186
|
+
const count = $first || $last;
|
|
187
|
+
/** @type {any} */
|
|
188
|
+
const $page = { ...filter, ...bounds, $all: true };
|
|
189
|
+
if (graph.length === count) {
|
|
190
|
+
// This result was limited by the count; update the "outer" bound.
|
|
191
|
+
if ($first) {
|
|
192
|
+
const boundKey = graph[graph.length - 1].$key;
|
|
193
|
+
$page.$until = isDef(boundKey?.$cursor) ? boundKey.$cursor : boundKey;
|
|
194
|
+
delete $page.$before;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
const boundKey = graph[0].$key;
|
|
198
|
+
$page.$since = isDef(boundKey?.$cursor) ? boundKey.$cursor : boundKey;
|
|
199
|
+
delete $page.$after;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// biome-ignore format: ternary chain
|
|
203
|
+
const $prev = isDef($page.$after) ? { ...filter, $last: count, $until: $page.$after } :
|
|
204
|
+
isDef($page.$since) ? { ...filter, $last: count, $before: $page.$since } :
|
|
205
|
+
null;
|
|
206
|
+
// biome-ignore format: ternary chain
|
|
207
|
+
const $next = isDef($page.$before) ? { ...filter, $first: count, $since: $page.$before } :
|
|
208
|
+
isDef($page.$until) ? { ...filter, $first: count, $after: $page.$until } :
|
|
209
|
+
null;
|
|
210
|
+
Object.assign(graph, { $page, $next, $prev });
|
|
211
|
+
}
|