@ethersphere/bee-js 13.0.0-upcoming.gcfb9756 → 13.0.0
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 +104 -103
- package/dist/cjs/api/bytes.js +3 -4
- package/dist/cjs/api/bzz.js +5 -6
- package/dist/cjs/api/chunk.js +3 -3
- package/dist/cjs/api/envelope.js +3 -2
- package/dist/cjs/api/feed.js +4 -5
- package/dist/cjs/api/pin.js +2 -2
- package/dist/cjs/api/soc.js +2 -2
- package/dist/cjs/bee.js +18 -19
- package/dist/cjs/chunk/soc.js +21 -63
- package/dist/cjs/feed/identifier.js +3 -4
- package/dist/cjs/feed/index.js +17 -19
- package/dist/cjs/feed/retrievable.js +2 -2
- package/dist/cjs/index.js +18 -8
- package/dist/cjs/manifest/manifest.js +97 -333
- package/dist/cjs/modules/balance.js +3 -3
- package/dist/cjs/modules/cheque.js +4 -4
- package/dist/cjs/modules/chunk.js +8 -8
- package/dist/cjs/modules/collection.js +6 -6
- package/dist/cjs/modules/connectivity.js +3 -3
- package/dist/cjs/modules/data.js +6 -6
- package/dist/cjs/modules/feed.js +10 -10
- package/dist/cjs/modules/file.js +3 -3
- package/dist/cjs/modules/grantee.js +9 -9
- package/dist/cjs/modules/messaging.js +15 -17
- package/dist/cjs/modules/pin.js +6 -6
- package/dist/cjs/modules/settlement.js +2 -2
- package/dist/cjs/modules/soc.js +3 -3
- package/dist/cjs/modules/stamp.js +7 -7
- package/dist/cjs/modules/tag.js +2 -2
- package/dist/cjs/modules/transaction.js +4 -4
- package/dist/cjs/modules/wallet.js +3 -3
- package/dist/cjs/types/schema/chequebook.js +5 -5
- package/dist/cjs/types/schema/connectivity.js +5 -5
- package/dist/cjs/types/schema/envelope.js +5 -5
- package/dist/cjs/types/schema/grantee.js +4 -4
- package/dist/cjs/types/schema/stake.js +2 -2
- package/dist/cjs/types/schema/stamps.js +4 -4
- package/dist/cjs/types/schema/states.js +2 -2
- package/dist/cjs/types/schema/transactions.js +3 -3
- package/dist/cjs/types/schema/upload.js +2 -2
- package/dist/cjs/utils/chunk-stream.browser.js +24 -16
- package/dist/cjs/utils/chunk-stream.js +17 -9
- package/dist/cjs/utils/constants.js +3 -3
- package/dist/cjs/utils/expose.js +4 -4
- package/dist/cjs/utils/headers.js +4 -4
- package/dist/cjs/utils/http.js +3 -1
- package/dist/cjs/utils/pss.js +2 -2
- package/dist/cjs/utils/resource-locator.js +2 -2
- package/dist/cjs/utils/schema.js +4 -4
- package/dist/cjs/utils/stamps.js +15 -130
- package/dist/cjs/utils/type.js +2 -2
- package/dist/codemod/index.js +141 -0
- package/dist/codemod/transforms/v13.js +524 -0
- package/dist/index.browser.min.js +2 -1
- package/dist/index.browser.min.js.LICENSE.txt +3 -0
- package/dist/index.browser.min.js.map +1 -1
- package/dist/mjs/api/bytes.js +1 -2
- package/dist/mjs/api/bzz.js +1 -2
- package/dist/mjs/api/chunk.js +1 -1
- package/dist/mjs/api/envelope.js +3 -2
- package/dist/mjs/api/feed.js +1 -2
- package/dist/mjs/api/pin.js +1 -1
- package/dist/mjs/api/soc.js +1 -1
- package/dist/mjs/bee.js +13 -14
- package/dist/mjs/chunk/soc.js +11 -49
- package/dist/mjs/feed/identifier.js +2 -3
- package/dist/mjs/feed/index.js +6 -8
- package/dist/mjs/feed/retrievable.js +1 -1
- package/dist/mjs/index.js +2 -5
- package/dist/mjs/manifest/manifest.js +95 -339
- package/dist/mjs/modules/balance.js +1 -1
- package/dist/mjs/modules/cheque.js +1 -1
- package/dist/mjs/modules/chunk.js +1 -1
- package/dist/mjs/modules/collection.js +1 -1
- package/dist/mjs/modules/connectivity.js +1 -1
- package/dist/mjs/modules/data.js +3 -3
- package/dist/mjs/modules/feed.js +1 -1
- package/dist/mjs/modules/file.js +1 -1
- package/dist/mjs/modules/grantee.js +1 -1
- package/dist/mjs/modules/messaging.js +3 -5
- package/dist/mjs/modules/pin.js +1 -1
- package/dist/mjs/modules/settlement.js +1 -1
- package/dist/mjs/modules/soc.js +1 -1
- package/dist/mjs/modules/stamp.js +1 -1
- package/dist/mjs/modules/tag.js +1 -1
- package/dist/mjs/modules/transaction.js +1 -1
- package/dist/mjs/modules/wallet.js +1 -1
- package/dist/mjs/types/schema/chequebook.js +1 -1
- package/dist/mjs/types/schema/connectivity.js +1 -1
- package/dist/mjs/types/schema/envelope.js +5 -5
- package/dist/mjs/types/schema/grantee.js +1 -1
- package/dist/mjs/types/schema/stake.js +1 -1
- package/dist/mjs/types/schema/stamps.js +1 -1
- package/dist/mjs/types/schema/states.js +1 -1
- package/dist/mjs/types/schema/transactions.js +1 -1
- package/dist/mjs/types/schema/upload.js +1 -1
- package/dist/mjs/utils/chunk-stream.browser.js +28 -18
- package/dist/mjs/utils/chunk-stream.js +21 -11
- package/dist/mjs/utils/constants.js +1 -1
- package/dist/mjs/utils/expose.js +1 -1
- package/dist/mjs/utils/headers.js +1 -1
- package/dist/mjs/utils/http.js +6 -1
- package/dist/mjs/utils/pss.js +1 -1
- package/dist/mjs/utils/resource-locator.js +1 -1
- package/dist/mjs/utils/schema.js +1 -1
- package/dist/mjs/utils/stamps.js +5 -99
- package/dist/mjs/utils/type.js +2 -2
- package/dist/types/api/balance.d.ts +1 -1
- package/dist/types/api/batches.d.ts +1 -1
- package/dist/types/api/bytes.d.ts +3 -3
- package/dist/types/api/bzz.d.ts +1 -2
- package/dist/types/api/chequebook.d.ts +1 -1
- package/dist/types/api/chunk.d.ts +1 -1
- package/dist/types/api/connectivity.d.ts +1 -1
- package/dist/types/api/consumed.d.ts +1 -1
- package/dist/types/api/envelope.d.ts +1 -1
- package/dist/types/api/feed.d.ts +1 -2
- package/dist/types/api/grantee.d.ts +1 -1
- package/dist/types/api/pin.d.ts +1 -1
- package/dist/types/api/pss.d.ts +1 -1
- package/dist/types/api/settlement.d.ts +1 -1
- package/dist/types/api/soc.d.ts +1 -1
- package/dist/types/api/stake.d.ts +1 -1
- package/dist/types/api/stamp.d.ts +1 -1
- package/dist/types/api/stewardship.d.ts +1 -1
- package/dist/types/api/tag.d.ts +1 -1
- package/dist/types/api/transaction.d.ts +1 -1
- package/dist/types/api/wallet.d.ts +1 -1
- package/dist/types/bee.d.ts +11 -13
- package/dist/types/chunk/soc.d.ts +3 -48
- package/dist/types/feed/identifier.d.ts +1 -1
- package/dist/types/feed/index.d.ts +1 -3
- package/dist/types/feed/retrievable.d.ts +1 -1
- package/dist/types/index.d.ts +17 -20
- package/dist/types/manifest/manifest.d.ts +24 -33
- package/dist/types/modules/balance.d.ts +1 -1
- package/dist/types/modules/cheque.d.ts +1 -1
- package/dist/types/modules/chequebook.d.ts +1 -1
- package/dist/types/modules/chunk.d.ts +1 -2
- package/dist/types/modules/collection.d.ts +2 -2
- package/dist/types/modules/connectivity.d.ts +1 -1
- package/dist/types/modules/context.d.ts +1 -1
- package/dist/types/modules/data.d.ts +3 -3
- package/dist/types/modules/feed.d.ts +1 -1
- package/dist/types/modules/file.d.ts +1 -2
- package/dist/types/modules/grantee.d.ts +1 -1
- package/dist/types/modules/messaging.d.ts +1 -2
- package/dist/types/modules/pin.d.ts +1 -1
- package/dist/types/modules/settlement.d.ts +1 -1
- package/dist/types/modules/soc.d.ts +1 -1
- package/dist/types/modules/stake.d.ts +1 -1
- package/dist/types/modules/stamp.d.ts +1 -1
- package/dist/types/modules/storage.d.ts +1 -1
- package/dist/types/modules/tag.d.ts +1 -1
- package/dist/types/modules/transaction.d.ts +1 -1
- package/dist/types/modules/wallet.d.ts +1 -1
- package/dist/types/types/debug.d.ts +1 -1
- package/dist/types/types/index.d.ts +2 -11
- package/dist/types/types/schema/chequebook.d.ts +1 -1
- package/dist/types/types/schema/connectivity.d.ts +1 -1
- package/dist/types/types/schema/grantee.d.ts +1 -1
- package/dist/types/types/schema/stake.d.ts +1 -1
- package/dist/types/types/schema/stamps.d.ts +1 -1
- package/dist/types/types/schema/states.d.ts +1 -1
- package/dist/types/types/schema/transactions.d.ts +1 -1
- package/dist/types/types/schema/upload.d.ts +1 -1
- package/dist/types/utils/chunk-stream.browser.d.ts +1 -1
- package/dist/types/utils/chunk-stream.d.ts +2 -2
- package/dist/types/utils/constants.d.ts +1 -1
- package/dist/types/utils/expose.d.ts +1 -1
- package/dist/types/utils/headers.d.ts +1 -1
- package/dist/types/utils/pss.d.ts +1 -1
- package/dist/types/utils/resource-locator.d.ts +1 -1
- package/dist/types/utils/schema.d.ts +1 -1
- package/dist/types/utils/stamps.d.ts +2 -27
- package/dist/types/utils/tar-uploader.browser.d.ts +1 -1
- package/dist/types/utils/tar-uploader.d.ts +1 -1
- package/dist/types/utils/type.d.ts +1 -1
- package/package.json +7 -2
- package/dist/cjs/chunk/bmt.js +0 -37
- package/dist/cjs/chunk/cac.js +0 -43
- package/dist/cjs/stamper/stamper.js +0 -44
- package/dist/cjs/utils/bytes.js +0 -116
- package/dist/cjs/utils/cid.js +0 -37
- package/dist/cjs/utils/redundancy.js +0 -133
- package/dist/cjs/utils/typed-bytes.js +0 -186
- package/dist/mjs/chunk/bmt.js +0 -34
- package/dist/mjs/chunk/cac.js +0 -34
- package/dist/mjs/stamper/stamper.js +0 -40
- package/dist/mjs/utils/bytes.js +0 -102
- package/dist/mjs/utils/cid.js +0 -32
- package/dist/mjs/utils/redundancy.js +0 -98
- package/dist/mjs/utils/typed-bytes.js +0 -167
- package/dist/types/chunk/bmt.d.ts +0 -16
- package/dist/types/chunk/cac.d.ts +0 -40
- package/dist/types/stamper/stamper.d.ts +0 -15
- package/dist/types/utils/bytes.d.ts +0 -19
- package/dist/types/utils/cid.d.ts +0 -10
- package/dist/types/utils/redundancy.d.ts +0 -21
- package/dist/types/utils/typed-bytes.d.ts +0 -69
|
@@ -1,152 +1,101 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.MantarayNode =
|
|
3
|
+
exports.MantarayNode = void 0;
|
|
7
4
|
const cafe_utility_1 = require("cafe-utility");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
static split(a, b) {
|
|
27
|
-
const commonPart = cafe_utility_1.Binary.commonPrefix(a.prefix, b.prefix);
|
|
28
|
-
if (commonPart.length === a.prefix.length) {
|
|
29
|
-
const remainingB = b.prefix.slice(commonPart.length);
|
|
30
|
-
b.node.path = b.prefix.slice(commonPart.length);
|
|
31
|
-
b.prefix = b.prefix.slice(commonPart.length);
|
|
32
|
-
b.node.parent = a.node;
|
|
33
|
-
a.node.forks.set(remainingB[0], b);
|
|
34
|
-
return a;
|
|
35
|
-
}
|
|
36
|
-
if (commonPart.length === b.prefix.length) {
|
|
37
|
-
const remainingA = a.prefix.slice(commonPart.length);
|
|
38
|
-
a.node.path = a.prefix.slice(commonPart.length);
|
|
39
|
-
a.prefix = a.prefix.slice(commonPart.length);
|
|
40
|
-
a.node.parent = b.node;
|
|
41
|
-
b.node.forks.set(remainingA[0], a);
|
|
42
|
-
return b;
|
|
43
|
-
}
|
|
44
|
-
const node = new MantarayNode({ path: commonPart });
|
|
45
|
-
const newAFork = new Fork(a.prefix.slice(commonPart.length), a.node);
|
|
46
|
-
const newBFork = new Fork(b.prefix.slice(commonPart.length), b.node);
|
|
47
|
-
a.node.path = a.prefix.slice(commonPart.length);
|
|
48
|
-
b.node.path = b.prefix.slice(commonPart.length);
|
|
49
|
-
a.prefix = a.prefix.slice(commonPart.length);
|
|
50
|
-
b.prefix = b.prefix.slice(commonPart.length);
|
|
51
|
-
node.forks.set(newAFork.prefix[0], newAFork);
|
|
52
|
-
node.forks.set(newBFork.prefix[0], newBFork);
|
|
53
|
-
newAFork.node.parent = node;
|
|
54
|
-
newBFork.node.parent = node;
|
|
55
|
-
return new Fork(commonPart, node);
|
|
56
|
-
}
|
|
57
|
-
marshal() {
|
|
58
|
-
if (!this.node.selfAddress) {
|
|
59
|
-
throw Error('Fork#marshal node.selfAddress is not set');
|
|
60
|
-
}
|
|
61
|
-
const data = [];
|
|
62
|
-
// Re-emit the type byte read from the chunk when the node is untouched, so an
|
|
63
|
-
// unmarshal → marshal round-trip is byte-identical. determineType() can only
|
|
64
|
-
// recompute it correctly once the node's children are loaded/built in memory.
|
|
65
|
-
data.push(new Uint8Array([this.node.type ?? this.node.determineType()]));
|
|
66
|
-
data.push(cafe_utility_1.Binary.numberToUint8(this.prefix.length));
|
|
67
|
-
data.push(this.prefix);
|
|
68
|
-
if (this.prefix.length < 30) {
|
|
69
|
-
data.push(new Uint8Array(30 - this.prefix.length));
|
|
70
|
-
}
|
|
71
|
-
data.push(this.node.selfAddress);
|
|
72
|
-
debug('marshalling fork', {
|
|
73
|
-
prefixLength: this.prefix.length,
|
|
74
|
-
prefix: DECODER.decode(this.prefix),
|
|
75
|
-
address: cafe_utility_1.Binary.uint8ArrayToHex(this.node.selfAddress),
|
|
76
|
-
});
|
|
77
|
-
if (this.node.metadata) {
|
|
78
|
-
const metadataBytes = cafe_utility_1.Binary.padEndToMultiple(new Uint8Array([0x00, 0x00, ...ENCODER.encode(JSON.stringify(this.node.metadata))]), 32, 0x0a);
|
|
79
|
-
const metadataLengthBytes = cafe_utility_1.Binary.numberToUint16(metadataBytes.length - 2, 'BE');
|
|
80
|
-
metadataBytes.set(metadataLengthBytes, 0);
|
|
81
|
-
data.push(metadataBytes);
|
|
5
|
+
const core_sdk_1 = require("@ethersphere/core-sdk");
|
|
6
|
+
/**
|
|
7
|
+
* Uploads `node` and every node beneath it, updating each `selfAddress` in
|
|
8
|
+
* place. Only the network/ACT-history side is bee-js specific - the trie
|
|
9
|
+
* structure and marshaling live entirely in core-sdk's MantarayNode.
|
|
10
|
+
*/
|
|
11
|
+
async function saveRecursively(node, bee, postageBatchId, options, requestOptions) {
|
|
12
|
+
for (const fork of node.forks.values()) {
|
|
13
|
+
const uploadResult = await saveRecursively(fork.node, bee, postageBatchId, options, requestOptions);
|
|
14
|
+
if (options?.act) {
|
|
15
|
+
let historyAddress;
|
|
16
|
+
uploadResult.historyAddress.ifPresent(ref => (historyAddress = ref));
|
|
17
|
+
if (historyAddress) {
|
|
18
|
+
if (!fork.node.metadata) {
|
|
19
|
+
fork.node.metadata = {};
|
|
20
|
+
}
|
|
21
|
+
fork.node.metadata['swarm-act-history-address'] = historyAddress.toHex();
|
|
22
|
+
}
|
|
82
23
|
}
|
|
83
|
-
return cafe_utility_1.Binary.concatBytes(...data);
|
|
84
24
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
25
|
+
const result = await bee.data.upload(postageBatchId, await node.marshal(), options, requestOptions);
|
|
26
|
+
node.selfAddress = result.reference.toUint8Array();
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Downloads and unmarshals every node beneath `node`, following ACT history
|
|
31
|
+
* metadata left by {@link saveRecursively} where present.
|
|
32
|
+
*/
|
|
33
|
+
async function loadRecursively(node, bee, options, requestOptions) {
|
|
34
|
+
for (const fork of node.forks.values()) {
|
|
35
|
+
if (!fork.node.selfAddress) {
|
|
36
|
+
throw Error('MantarayNode#loadRecursively fork.node.selfAddress is not set');
|
|
37
|
+
}
|
|
38
|
+
let downloadOptions = options;
|
|
39
|
+
if (fork.node.metadata && fork.node.metadata['swarm-act-history-address']) {
|
|
40
|
+
downloadOptions = {
|
|
41
|
+
...options,
|
|
42
|
+
actHistoryAddress: fork.node.metadata['swarm-act-history-address'],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const data = (await bee.data.download(fork.node.selfAddress, downloadOptions, requestOptions)).toUint8Array();
|
|
46
|
+
const loaded = core_sdk_1.MantarayNode.unmarshalFromData(data, fork.node.selfAddress);
|
|
47
|
+
fork.node.targetAddress = loaded.targetAddress;
|
|
48
|
+
fork.node.forks = loaded.forks;
|
|
49
|
+
fork.node.path = fork.prefix;
|
|
50
|
+
fork.node.parent = node;
|
|
51
|
+
await loadRecursively(fork.node, bee, options, requestOptions);
|
|
106
52
|
}
|
|
107
53
|
}
|
|
108
|
-
|
|
54
|
+
/**
|
|
55
|
+
* bee-js's Bee-client-coupled view of a Mantaray node: the trie structure,
|
|
56
|
+
* marshaling, and byte-level operations all delegate to core-sdk's
|
|
57
|
+
* `MantarayNode`; this class only adds what needs a live `Bee` instance
|
|
58
|
+
* (uploading/downloading, ACT history, feed resolution).
|
|
59
|
+
*/
|
|
109
60
|
class MantarayNode {
|
|
110
|
-
constructor(
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
this.type = options?.type ?? null;
|
|
61
|
+
constructor(core = new core_sdk_1.MantarayNode()) {
|
|
62
|
+
this.core = core;
|
|
63
|
+
}
|
|
64
|
+
get obfuscationKey() {
|
|
65
|
+
return this.core.obfuscationKey;
|
|
66
|
+
}
|
|
67
|
+
get selfAddress() {
|
|
68
|
+
return this.core.selfAddress;
|
|
69
|
+
}
|
|
70
|
+
get targetAddress() {
|
|
71
|
+
return this.core.targetAddress;
|
|
72
|
+
}
|
|
73
|
+
get metadata() {
|
|
74
|
+
return this.core.metadata;
|
|
75
|
+
}
|
|
76
|
+
get path() {
|
|
77
|
+
return this.core.path;
|
|
78
|
+
}
|
|
79
|
+
get forks() {
|
|
80
|
+
return this.core.forks;
|
|
81
|
+
}
|
|
82
|
+
get parent() {
|
|
83
|
+
return this.core.parent;
|
|
84
|
+
}
|
|
85
|
+
get type() {
|
|
86
|
+
return this.core.type;
|
|
138
87
|
}
|
|
139
88
|
get fullPath() {
|
|
140
|
-
return
|
|
89
|
+
return this.core.fullPath;
|
|
141
90
|
}
|
|
142
91
|
get fullPathString() {
|
|
143
|
-
return
|
|
92
|
+
return this.core.fullPathString;
|
|
144
93
|
}
|
|
145
94
|
/**
|
|
146
95
|
* Returns the metadata at the `/` path to access idiomatic properties.
|
|
147
96
|
*/
|
|
148
97
|
getRootMetadata() {
|
|
149
|
-
const node = this.find('/');
|
|
98
|
+
const node = this.core.find('/');
|
|
150
99
|
if (node && node.metadata) {
|
|
151
100
|
return cafe_utility_1.Optional.of(node.metadata);
|
|
152
101
|
}
|
|
@@ -156,7 +105,7 @@ class MantarayNode {
|
|
|
156
105
|
* Returns the `swarm-index-document` and `swarm-error-document` metadata values.
|
|
157
106
|
*/
|
|
158
107
|
getDocsMetadata() {
|
|
159
|
-
const node = this.find('/');
|
|
108
|
+
const node = this.core.find('/');
|
|
160
109
|
if (!node || !node.metadata) {
|
|
161
110
|
return { indexDocument: null, errorDocument: null };
|
|
162
111
|
}
|
|
@@ -169,7 +118,7 @@ class MantarayNode {
|
|
|
169
118
|
* Attempts to resolve the manifest as a feed, returning the latest update.
|
|
170
119
|
*/
|
|
171
120
|
async resolveFeed(bee, requestOptions) {
|
|
172
|
-
const node = this.find('/');
|
|
121
|
+
const node = this.core.find('/');
|
|
173
122
|
if (!node || !node.metadata) {
|
|
174
123
|
return cafe_utility_1.Optional.empty();
|
|
175
124
|
}
|
|
@@ -184,40 +133,7 @@ class MantarayNode {
|
|
|
184
133
|
* Gets the binary representation of the node.
|
|
185
134
|
*/
|
|
186
135
|
async marshal() {
|
|
187
|
-
|
|
188
|
-
if (!fork.node.selfAddress) {
|
|
189
|
-
fork.node.selfAddress = (await fork.node.calculateSelfAddress()).toUint8Array();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
const hasEntry = !cafe_utility_1.Binary.equals(this.targetAddress, __1.NULL_ADDRESS);
|
|
193
|
-
let refBytesSize = 0;
|
|
194
|
-
if (hasEntry) {
|
|
195
|
-
refBytesSize = this.targetAddress.length;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
for (const fork of this.forks.values()) {
|
|
199
|
-
if (fork.node.selfAddress) {
|
|
200
|
-
refBytesSize = fork.node.selfAddress.length;
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const header = new Uint8Array(32);
|
|
206
|
-
header.set(VERSION_02_HASH, 0);
|
|
207
|
-
header.set(cafe_utility_1.Binary.numberToUint8(refBytesSize), 31);
|
|
208
|
-
const entry = hasEntry ? this.targetAddress : new Uint8Array(refBytesSize);
|
|
209
|
-
const forkBitmap = new Uint8Array(32);
|
|
210
|
-
for (const fork of this.forks.keys()) {
|
|
211
|
-
cafe_utility_1.Binary.setBit(forkBitmap, fork, 1, 'LE');
|
|
212
|
-
}
|
|
213
|
-
const forks = [];
|
|
214
|
-
for (let i = 0; i < 256; i++) {
|
|
215
|
-
if (cafe_utility_1.Binary.getBit(forkBitmap, i, 'LE')) {
|
|
216
|
-
forks.push(this.forks.get(i).marshal());
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
const data = cafe_utility_1.Binary.xorCypher(cafe_utility_1.Binary.concatBytes(header, entry, forkBitmap, ...forks), this.obfuscationKey);
|
|
220
|
-
return cafe_utility_1.Binary.concatBytes(this.obfuscationKey, data);
|
|
136
|
+
return this.core.marshal();
|
|
221
137
|
}
|
|
222
138
|
/**
|
|
223
139
|
* Downloads and unmarshals a MantarayNode from the given reference.
|
|
@@ -225,7 +141,7 @@ class MantarayNode {
|
|
|
225
141
|
* Do not forget calling `loadRecursively` on the returned node to load the entire tree.
|
|
226
142
|
*/
|
|
227
143
|
static async unmarshal(bee, reference, options, requestOptions) {
|
|
228
|
-
reference = new
|
|
144
|
+
reference = new core_sdk_1.Reference(reference);
|
|
229
145
|
const data = (await bee.data.download(reference, options, requestOptions)).toUint8Array();
|
|
230
146
|
return this.unmarshalFromData(data, reference.toUint8Array());
|
|
231
147
|
}
|
|
@@ -235,167 +151,49 @@ class MantarayNode {
|
|
|
235
151
|
* Do not forget calling `loadRecursively` on the returned node to load the entire tree.
|
|
236
152
|
*/
|
|
237
153
|
static unmarshalFromData(data, selfAddress) {
|
|
238
|
-
|
|
239
|
-
const decrypted = cafe_utility_1.Binary.xorCypher(data.subarray(32), obfuscationKey);
|
|
240
|
-
const reader = new cafe_utility_1.Uint8ArrayReader(decrypted);
|
|
241
|
-
const versionHash = reader.read(31);
|
|
242
|
-
if (!cafe_utility_1.Binary.equals(versionHash, VERSION_02_HASH.slice(0, 31))) {
|
|
243
|
-
throw new Error('MantarayNode#unmarshal invalid version hash');
|
|
244
|
-
}
|
|
245
|
-
const targetAddressLength = cafe_utility_1.Binary.uint8ToNumber(reader.read(1));
|
|
246
|
-
const targetAddress = targetAddressLength ? reader.read(targetAddressLength) : __1.NULL_ADDRESS;
|
|
247
|
-
const node = new MantarayNode({ selfAddress, targetAddress, obfuscationKey });
|
|
248
|
-
const forkBitmap = reader.read(32);
|
|
249
|
-
if (targetAddressLength > 0) {
|
|
250
|
-
for (let i = 0; i < 256; i++) {
|
|
251
|
-
if (cafe_utility_1.Binary.getBit(forkBitmap, i, 'LE')) {
|
|
252
|
-
const newFork = Fork.unmarshal(reader, selfAddress.length);
|
|
253
|
-
node.forks.set(i, newFork);
|
|
254
|
-
newFork.node.parent = node;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return node;
|
|
154
|
+
return new MantarayNode(core_sdk_1.MantarayNode.unmarshalFromData(data, selfAddress));
|
|
259
155
|
}
|
|
260
156
|
/**
|
|
261
157
|
* Adds a fork to the node.
|
|
262
158
|
*/
|
|
263
159
|
addFork(path, reference, metadata) {
|
|
264
|
-
this.
|
|
265
|
-
this.type = null;
|
|
266
|
-
path = path instanceof Uint8Array ? path : ENCODER.encode(path);
|
|
267
|
-
debug('adding fork', { path: DECODER.decode(path), reference: new typed_bytes_1.Reference(reference).represent() });
|
|
268
|
-
// TODO: this should not be ignored
|
|
269
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
270
|
-
let tip = this;
|
|
271
|
-
while (path.length) {
|
|
272
|
-
const prefix = path.slice(0, 30);
|
|
273
|
-
path = path.slice(30);
|
|
274
|
-
const isLast = path.length === 0;
|
|
275
|
-
const [bestMatch, matchedPath] = tip.findClosest(prefix);
|
|
276
|
-
const remainingPath = prefix.slice(matchedPath.length);
|
|
277
|
-
if (matchedPath.length) {
|
|
278
|
-
tip = bestMatch;
|
|
279
|
-
}
|
|
280
|
-
if (!remainingPath.length) {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
const newFork = new Fork(remainingPath, new MantarayNode({
|
|
284
|
-
targetAddress: isLast ? new typed_bytes_1.Reference(reference).toUint8Array() : undefined,
|
|
285
|
-
metadata: isLast ? metadata : undefined,
|
|
286
|
-
path: remainingPath,
|
|
287
|
-
}));
|
|
288
|
-
const existing = bestMatch.forks.get(remainingPath[0]);
|
|
289
|
-
if (existing) {
|
|
290
|
-
const fork = Fork.split(newFork, existing);
|
|
291
|
-
tip.forks.set(remainingPath[0], fork);
|
|
292
|
-
fork.node.parent = tip;
|
|
293
|
-
tip.selfAddress = null;
|
|
294
|
-
tip.type = null;
|
|
295
|
-
tip = newFork.node;
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
tip.forks.set(remainingPath[0], newFork);
|
|
299
|
-
newFork.node.parent = tip;
|
|
300
|
-
tip.selfAddress = null;
|
|
301
|
-
tip.type = null;
|
|
302
|
-
tip = newFork.node;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
160
|
+
this.core.addFork(path, reference instanceof core_sdk_1.Bytes ? reference.toUint8Array() : reference, metadata);
|
|
305
161
|
}
|
|
306
162
|
/**
|
|
307
163
|
* Removes a fork from the node.
|
|
308
164
|
*/
|
|
309
165
|
removeFork(path) {
|
|
310
|
-
this.
|
|
311
|
-
this.type = null;
|
|
312
|
-
path = path instanceof Uint8Array ? path : ENCODER.encode(path);
|
|
313
|
-
if (path.length === 0) {
|
|
314
|
-
throw Error('MantarayNode#removeFork [path] parameter cannot be empty');
|
|
315
|
-
}
|
|
316
|
-
const match = this.find(path);
|
|
317
|
-
if (!match) {
|
|
318
|
-
throw Error('MantarayNode#removeFork fork not found');
|
|
319
|
-
}
|
|
320
|
-
const [parent, matchedPath] = this.findClosest(path.slice(0, path.length - 1));
|
|
321
|
-
parent.forks.delete(path.slice(matchedPath.length)[0]);
|
|
322
|
-
for (const fork of match.forks.values()) {
|
|
323
|
-
parent.addFork(cafe_utility_1.Binary.concatBytes(match.path, fork.prefix), fork.node.targetAddress, fork.node.metadata);
|
|
324
|
-
}
|
|
166
|
+
this.core.removeFork(path);
|
|
325
167
|
}
|
|
326
168
|
/**
|
|
327
169
|
* Calculates the self address of the node.
|
|
328
170
|
*/
|
|
329
171
|
async calculateSelfAddress() {
|
|
330
|
-
|
|
331
|
-
return new typed_bytes_1.Reference(this.selfAddress);
|
|
332
|
-
}
|
|
333
|
-
return new typed_bytes_1.Reference((await cafe_utility_1.MerkleTree.root(await this.marshal())).hash());
|
|
172
|
+
return this.core.calculateSelfAddress();
|
|
334
173
|
}
|
|
335
174
|
/**
|
|
336
175
|
* Saves the node and its children recursively.
|
|
337
176
|
*/
|
|
338
177
|
async saveRecursively(bee, postageBatchId, options, requestOptions) {
|
|
339
|
-
|
|
340
|
-
const uploadResult = await fork.node.saveRecursively(bee, postageBatchId, options, requestOptions);
|
|
341
|
-
if (options?.act) {
|
|
342
|
-
let historyAddress;
|
|
343
|
-
uploadResult.historyAddress.ifPresent(ref => (historyAddress = ref));
|
|
344
|
-
if (historyAddress) {
|
|
345
|
-
if (!fork.node.metadata) {
|
|
346
|
-
fork.node.metadata = {};
|
|
347
|
-
}
|
|
348
|
-
fork.node.metadata['swarm-act-history-address'] = historyAddress.toHex();
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const result = await bee.data.upload(postageBatchId, await this.marshal(), options, requestOptions);
|
|
353
|
-
this.selfAddress = result.reference.toUint8Array();
|
|
354
|
-
return result;
|
|
178
|
+
return saveRecursively(this.core, bee, postageBatchId, options, requestOptions);
|
|
355
179
|
}
|
|
356
180
|
/**
|
|
357
181
|
* Loads the node and its children recursively.
|
|
358
182
|
*/
|
|
359
183
|
async loadRecursively(bee, options, requestOptions) {
|
|
360
|
-
|
|
361
|
-
if (!fork.node.selfAddress) {
|
|
362
|
-
throw Error('MantarayNode#loadRecursively fork.node.selfAddress is not set');
|
|
363
|
-
}
|
|
364
|
-
let downloadOptions = options;
|
|
365
|
-
if (fork.node.metadata && fork.node.metadata['swarm-act-history-address']) {
|
|
366
|
-
downloadOptions = {
|
|
367
|
-
...options,
|
|
368
|
-
actHistoryAddress: fork.node.metadata['swarm-act-history-address'],
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
const node = await MantarayNode.unmarshal(bee, fork.node.selfAddress, downloadOptions, requestOptions);
|
|
372
|
-
fork.node.targetAddress = node.targetAddress;
|
|
373
|
-
fork.node.forks = node.forks;
|
|
374
|
-
fork.node.path = fork.prefix;
|
|
375
|
-
fork.node.parent = this;
|
|
376
|
-
await fork.node.loadRecursively(bee, options, requestOptions);
|
|
377
|
-
}
|
|
184
|
+
return loadRecursively(this.core, bee, options, requestOptions);
|
|
378
185
|
}
|
|
379
186
|
/**
|
|
380
187
|
* Finds a node in the tree by its path.
|
|
381
188
|
*/
|
|
382
189
|
find(path) {
|
|
383
|
-
|
|
384
|
-
return match.length === path.length ? closest : null;
|
|
190
|
+
return this.core.find(path);
|
|
385
191
|
}
|
|
386
192
|
/**
|
|
387
193
|
* Finds the closest node in the tree to the given path.
|
|
388
194
|
*/
|
|
389
|
-
findClosest(path, current
|
|
390
|
-
|
|
391
|
-
if (path.length === 0) {
|
|
392
|
-
return [this, current];
|
|
393
|
-
}
|
|
394
|
-
const fork = this.forks.get(path[0]);
|
|
395
|
-
if (fork && cafe_utility_1.Binary.commonPrefix(fork.prefix, path).length === fork.prefix.length) {
|
|
396
|
-
return fork.node.findClosest(path.slice(fork.prefix.length), cafe_utility_1.Binary.concatBytes(current, fork.prefix));
|
|
397
|
-
}
|
|
398
|
-
return [this, current];
|
|
195
|
+
findClosest(path, current) {
|
|
196
|
+
return this.core.findClosest(path, current);
|
|
399
197
|
}
|
|
400
198
|
/**
|
|
401
199
|
* Returns an array of all nodes in the tree which have a target address set.
|
|
@@ -403,13 +201,7 @@ class MantarayNode {
|
|
|
403
201
|
* Must be called after `loadRecursively`.
|
|
404
202
|
*/
|
|
405
203
|
collect(nodes = []) {
|
|
406
|
-
|
|
407
|
-
if (!cafe_utility_1.Binary.equals(fork.node.targetAddress, __1.NULL_ADDRESS)) {
|
|
408
|
-
nodes.push(fork.node);
|
|
409
|
-
}
|
|
410
|
-
fork.node.collect(nodes);
|
|
411
|
-
}
|
|
412
|
-
return nodes;
|
|
204
|
+
return this.core.collect(nodes);
|
|
413
205
|
}
|
|
414
206
|
/**
|
|
415
207
|
* Returns a path:reference map of all nodes in the tree which have a target address set.
|
|
@@ -417,38 +209,10 @@ class MantarayNode {
|
|
|
417
209
|
* Must be called after `loadRecursively`.
|
|
418
210
|
*/
|
|
419
211
|
collectAndMap() {
|
|
420
|
-
|
|
421
|
-
const result = {};
|
|
422
|
-
for (const node of nodes) {
|
|
423
|
-
result[node.fullPathString] = new typed_bytes_1.Reference(node.targetAddress).toHex();
|
|
424
|
-
}
|
|
425
|
-
return result;
|
|
212
|
+
return this.core.collectAndMap();
|
|
426
213
|
}
|
|
427
214
|
determineType() {
|
|
428
|
-
|
|
429
|
-
// Mirror Bee (pkg/manifest/mantaray/node.go): Add() marks every explicitly
|
|
430
|
-
// added leaf as a value (makeValue), even one with a null entry such as a
|
|
431
|
-
// metadata-only "/" node. In final-state terms a leaf (no forks) is always an
|
|
432
|
-
// added entry, so it is a value; a node with forks is a value only when it also
|
|
433
|
-
// carries an entry. The path-separator flag is set only when a separator occurs
|
|
434
|
-
// past the first byte (IndexRune > 0), so a prefix that merely starts with '/'
|
|
435
|
-
// does not qualify.
|
|
436
|
-
if (!cafe_utility_1.Binary.equals(this.targetAddress, __1.NULL_ADDRESS) || this.forks.size === 0) {
|
|
437
|
-
type |= TYPE_VALUE;
|
|
438
|
-
}
|
|
439
|
-
if (this.forks.size > 0) {
|
|
440
|
-
type |= TYPE_EDGE;
|
|
441
|
-
}
|
|
442
|
-
if (cafe_utility_1.Binary.indexOf(this.path, PATH_SEPARATOR) > 0) {
|
|
443
|
-
type |= TYPE_WITH_PATH_SEPARATOR;
|
|
444
|
-
}
|
|
445
|
-
if (this.metadata) {
|
|
446
|
-
type |= TYPE_WITH_METADATA;
|
|
447
|
-
}
|
|
448
|
-
return type;
|
|
215
|
+
return this.core.determineType();
|
|
449
216
|
}
|
|
450
217
|
}
|
|
451
218
|
exports.MantarayNode = MantarayNode;
|
|
452
|
-
function isType(value, type) {
|
|
453
|
-
return (value & type) === type;
|
|
454
|
-
}
|
|
@@ -34,9 +34,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Balance = void 0;
|
|
37
|
+
const core_sdk_1 = require("@ethersphere/core-sdk");
|
|
37
38
|
const balanceApi = __importStar(require("../api/balance"));
|
|
38
39
|
const consumedApi = __importStar(require("../api/consumed"));
|
|
39
|
-
const typed_bytes_1 = require("../utils/typed-bytes");
|
|
40
40
|
/**
|
|
41
41
|
* SWAP balance operations. Related to the bandwidth incentives and the chequebook.
|
|
42
42
|
*
|
|
@@ -61,7 +61,7 @@ class Balance {
|
|
|
61
61
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
62
62
|
*/
|
|
63
63
|
async getPeer(address, requestOptions) {
|
|
64
|
-
const peer = new
|
|
64
|
+
const peer = new core_sdk_1.PeerAddress(address);
|
|
65
65
|
return balanceApi.getPeerBalance(this.context.getRequestOptionsForCall(requestOptions), peer);
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
@@ -79,7 +79,7 @@ class Balance {
|
|
|
79
79
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
80
80
|
*/
|
|
81
81
|
async getAllPastDueConsumptionForPeer(address, requestOptions) {
|
|
82
|
-
const peer = new
|
|
82
|
+
const peer = new core_sdk_1.PeerAddress(address);
|
|
83
83
|
return consumedApi.getPastDueConsumptionPeerBalance(this.context.getRequestOptionsForCall(requestOptions), peer);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -34,8 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Cheque = void 0;
|
|
37
|
+
const core_sdk_1 = require("@ethersphere/core-sdk");
|
|
37
38
|
const schema_1 = require("../utils/schema");
|
|
38
|
-
const typed_bytes_1 = require("../utils/typed-bytes");
|
|
39
39
|
const api = __importStar(require("../api/chequebook"));
|
|
40
40
|
/**
|
|
41
41
|
* Cheque operations (last cheques and cashouts).
|
|
@@ -61,7 +61,7 @@ class Cheque {
|
|
|
61
61
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
62
62
|
*/
|
|
63
63
|
async getAllLatestForPeer(address, requestOptions) {
|
|
64
|
-
const peer = new
|
|
64
|
+
const peer = new core_sdk_1.PeerAddress(address);
|
|
65
65
|
return api.getLastChequesForPeer(this.context.getRequestOptionsForCall(requestOptions), peer);
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
@@ -71,7 +71,7 @@ class Cheque {
|
|
|
71
71
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
72
72
|
*/
|
|
73
73
|
async getLastCashoutAction(address, requestOptions) {
|
|
74
|
-
const peer = new
|
|
74
|
+
const peer = new core_sdk_1.PeerAddress(address);
|
|
75
75
|
return api.getLastCashoutAction(this.context.getRequestOptionsForCall(requestOptions), peer);
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
@@ -82,7 +82,7 @@ class Cheque {
|
|
|
82
82
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
83
83
|
*/
|
|
84
84
|
async cashoutLast(address, options, requestOptions) {
|
|
85
|
-
const peer = new
|
|
85
|
+
const peer = new core_sdk_1.PeerAddress(address);
|
|
86
86
|
if (options) {
|
|
87
87
|
options = schema_1.TransactionOptionsSchema.parse(options);
|
|
88
88
|
}
|
|
@@ -34,10 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Chunk = void 0;
|
|
37
|
+
const core_sdk_1 = require("@ethersphere/core-sdk");
|
|
37
38
|
const types_1 = require("../types");
|
|
38
39
|
const error_1 = require("../utils/error");
|
|
39
40
|
const schema_1 = require("../utils/schema");
|
|
40
|
-
const typed_bytes_1 = require("../utils/typed-bytes");
|
|
41
41
|
const api = __importStar(require("../api/chunk"));
|
|
42
42
|
/**
|
|
43
43
|
* Chunk operations backed by the `/chunks` endpoint.
|
|
@@ -64,14 +64,14 @@ class Chunk {
|
|
|
64
64
|
if (options) {
|
|
65
65
|
options = schema_1.UploadOptionsSchema.parse(options);
|
|
66
66
|
}
|
|
67
|
-
if (data.length <
|
|
68
|
-
throw new error_1.BeeArgumentError(`Chunk has to have size of at least ${
|
|
67
|
+
if (data.length < core_sdk_1.Span.LENGTH) {
|
|
68
|
+
throw new error_1.BeeArgumentError(`Chunk has to have size of at least ${core_sdk_1.Span.LENGTH}.`, data);
|
|
69
69
|
}
|
|
70
|
-
if (!isSOC && data.length > types_1.CHUNK_SIZE +
|
|
71
|
-
throw new error_1.BeeArgumentError(`Content Addressed Chunk must not exceed ${types_1.CHUNK_SIZE +
|
|
70
|
+
if (!isSOC && data.length > types_1.CHUNK_SIZE + core_sdk_1.Span.LENGTH) {
|
|
71
|
+
throw new error_1.BeeArgumentError(`Content Addressed Chunk must not exceed ${types_1.CHUNK_SIZE + core_sdk_1.Span.LENGTH} bytes.`, data);
|
|
72
72
|
}
|
|
73
|
-
if (isSOC && data.length > types_1.CHUNK_SIZE +
|
|
74
|
-
throw new error_1.BeeArgumentError(`Single Owner Chunk must not exceed ${types_1.CHUNK_SIZE +
|
|
73
|
+
if (isSOC && data.length > types_1.CHUNK_SIZE + core_sdk_1.Span.LENGTH + core_sdk_1.Signature.LENGTH + core_sdk_1.Identifier.LENGTH) {
|
|
74
|
+
throw new error_1.BeeArgumentError(`Single Owner Chunk must not exceed ${types_1.CHUNK_SIZE + core_sdk_1.Span.LENGTH + core_sdk_1.Signature.LENGTH + core_sdk_1.Identifier.LENGTH} bytes.`, data);
|
|
75
75
|
}
|
|
76
76
|
return api.upload(this.context.getRequestOptionsForCall(requestOptions), data, stamp, options);
|
|
77
77
|
}
|
|
@@ -83,7 +83,7 @@ class Chunk {
|
|
|
83
83
|
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
|
|
84
84
|
*/
|
|
85
85
|
async download(reference, options, requestOptions) {
|
|
86
|
-
const ref = new
|
|
86
|
+
const ref = new core_sdk_1.Reference(reference);
|
|
87
87
|
if (options) {
|
|
88
88
|
options = schema_1.DownloadOptionsSchema.parse(options);
|
|
89
89
|
}
|