|
1 | 1 | // @ts-check
|
2 | 2 |
|
3 |
| -import { createV1 } from './cid.js' |
| 3 | +import { createV1, asCID } from './cid.js' |
4 | 4 |
|
5 | 5 | /**
|
6 | 6 | * @class
|
@@ -92,6 +92,102 @@ export class Block {
|
92 | 92 | return cid
|
93 | 93 | }
|
94 | 94 | }
|
| 95 | + |
| 96 | + links () { |
| 97 | + return links(this.data, [], this) |
| 98 | + } |
| 99 | + |
| 100 | + tree () { |
| 101 | + return tree(this.data, [], this) |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * @param {string} path |
| 106 | + */ |
| 107 | + get (path) { |
| 108 | + return get(this.data, path.split('/').filter(Boolean), this) |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +/** |
| 113 | + * @template T |
| 114 | + * @param {T} source |
| 115 | + * @param {Array<string|number>} base |
| 116 | + * @param {BlockConfig} config |
| 117 | + * @returns {Iterable<[string, CID]>} |
| 118 | + */ |
| 119 | +const links = function * (source, base, config) { |
| 120 | + for (const [key, value] of Object.entries(source)) { |
| 121 | + const path = [...base, key] |
| 122 | + if (value != null && typeof value === 'object') { |
| 123 | + if (Array.isArray(value)) { |
| 124 | + for (const [index, element] of value.entries()) { |
| 125 | + const elementPath = [...path, index] |
| 126 | + const cid = asCID(element, config) |
| 127 | + if (cid) { |
| 128 | + yield [elementPath.join('/'), cid] |
| 129 | + } else if (typeof element === 'object') { |
| 130 | + yield * links(element, elementPath, config) |
| 131 | + } |
| 132 | + } |
| 133 | + } else { |
| 134 | + const cid = asCID(value, config) |
| 135 | + if (cid) { |
| 136 | + yield [path.join('/'), cid] |
| 137 | + } else { |
| 138 | + yield * links(value, path, config) |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +/** |
| 146 | + * @template T |
| 147 | + * @param {T} source |
| 148 | + * @param {Array<string|number>} base |
| 149 | + * @param {BlockConfig} config |
| 150 | + * @returns {Iterable<string>} |
| 151 | + */ |
| 152 | +const tree = function * (source, base, config) { |
| 153 | + for (const [key, value] of Object.entries(source)) { |
| 154 | + const path = [...base, key] |
| 155 | + yield path.join('/') |
| 156 | + if (value != null && typeof value === 'object' && !asCID(value, config)) { |
| 157 | + if (Array.isArray(value)) { |
| 158 | + for (const [index, element] of value.entries()) { |
| 159 | + const elementPath = [...path, index] |
| 160 | + yield elementPath.join('/') |
| 161 | + if (typeof element === 'object' && !asCID(elementPath, config)) { |
| 162 | + yield * tree(element, elementPath, config) |
| 163 | + } |
| 164 | + } |
| 165 | + } else { |
| 166 | + yield * tree(value, path, config) |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +/** |
| 173 | + * @template T |
| 174 | + * @param {T} source |
| 175 | + * @param {string[]} path |
| 176 | + * @param {BlockConfig} config |
| 177 | + */ |
| 178 | +const get = (source, path, config) => { |
| 179 | + let node = source |
| 180 | + for (const [index, key] of path.entries()) { |
| 181 | + node = node[key] |
| 182 | + if (node == null) { |
| 183 | + throw new Error(`Object has no property at ${path.slice(0, index - 1).map(part => `[${JSON.stringify(part)}]`).join('')}`) |
| 184 | + } |
| 185 | + const cid = asCID(node, config) |
| 186 | + if (cid) { |
| 187 | + return { value: cid, remaining: path.slice(index).join('/') } |
| 188 | + } |
| 189 | + } |
| 190 | + return { value: node } |
95 | 191 | }
|
96 | 192 |
|
97 | 193 | /**
|
|
0 commit comments