UNPKG

23.3 kBJavaScriptView Raw
1// Copyright 2017-2022 @polkadot/api authors & contributors
2// SPDX-License-Identifier: Apache-2.0
3import { BehaviorSubject, combineLatest, from, map, of, switchMap, tap, toArray } from 'rxjs';
4import { getAvailableDerives } from '@polkadot/api-derive';
5import { memo, RpcCore } from '@polkadot/rpc-core';
6import { WsProvider } from '@polkadot/rpc-provider';
7import { expandMetadata, TypeRegistry, unwrapStorageType } from '@polkadot/types';
8import { arrayChunk, arrayFlatten, assert, assertReturn, BN, BN_ZERO, compactStripLength, lazyMethod, lazyMethods, logger, objectSpread, u8aToHex } from '@polkadot/util';
9import { createSubmittable } from "../submittable/index.js";
10import { augmentObject } from "../util/augmentObject.js";
11import { decorateDeriveSections } from "../util/decorate.js";
12import { extractStorageArgs } from "../util/validate.js";
13import { Events } from "./Events.js";
14import { findCall, findError } from "./find.js";
15// the max amount of keys/values that we will retrieve at once
16const PAGE_SIZE_K = 1000; // limit aligned with the 1k on the node (trie lookups are heavy)
17
18const PAGE_SIZE_V = 250; // limited since the data may be very large (e.g. misfiring elections)
19
20const l = logger('api/init');
21let instanceCounter = 0;
22
23function getAtQueryFn(api, {
24 method,
25 section
26}) {
27 return assertReturn(api.rx.query[section] && api.rx.query[section][method], () => `query.${section}.${method} is not available in this version of the metadata`);
28}
29
30export class Decorate extends Events {
31 #instanceId;
32 #registry; // HACK Use BN import so decorateDerive works... yes, wtf.
33
34 __phantom = new BN(0);
35 _consts = {};
36 _errors = {};
37 _events = {};
38 _extrinsicType = 4; // latest extrinsic version
39
40 _isReady = false;
41 _query = {};
42 _rx = {
43 consts: {},
44 query: {},
45 tx: {}
46 };
47
48 /**
49 * @description Create an instance of the class
50 *
51 * @param options Options object to create API instance or a Provider instance
52 *
53 * @example
54 * <BR>
55 *
56 * ```javascript
57 * import Api from '@polkadot/api/promise';
58 *
59 * const api = new Api().isReady();
60 *
61 * api.rpc.subscribeNewHeads((header) => {
62 * console.log(`new block #${header.number.toNumber()}`);
63 * });
64 * ```
65 */
66 constructor(options, type, decorateMethod) {
67 var _options$source;
68
69 super();
70 this.#instanceId = `${++instanceCounter}`;
71 this.#registry = ((_options$source = options.source) === null || _options$source === void 0 ? void 0 : _options$source.registry) || options.registry || new TypeRegistry();
72
73 this._rx.queryAt = (blockHash, knownVersion) => from(this.at(blockHash, knownVersion)).pipe(map(a => a.rx.query));
74
75 this._rx.registry = this.#registry;
76 const thisProvider = options.source ? options.source._rpcCore.provider.clone() : options.provider || new WsProvider();
77 this._decorateMethod = decorateMethod;
78 this._options = options;
79 this._type = type; // The RPC interface decorates the known interfaces on init
80
81 this._rpcCore = new RpcCore(this.#instanceId, this.#registry, thisProvider, this._options.rpc);
82 this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected);
83 this._rx.hasSubscriptions = this._rpcCore.provider.hasSubscriptions;
84 }
85
86 /**
87 * @description Return the current used registry
88 */
89 get registry() {
90 return this.#registry;
91 }
92 /**
93 * @description Creates an instance of a type as registered
94 */
95
96
97 createType(type, ...params) {
98 return this.#registry.createType(type, ...params);
99 }
100 /**
101 * @description Register additional user-defined of chain-specific types in the type registry
102 */
103
104
105 registerTypes(types) {
106 types && this.#registry.register(types);
107 }
108 /**
109 * @returns `true` if the API operates with subscriptions
110 */
111
112
113 get hasSubscriptions() {
114 return this._rpcCore.provider.hasSubscriptions;
115 }
116 /**
117 * @returns `true` if the API decorate multi-key queries
118 */
119
120
121 get supportMulti() {
122 return this._rpcCore.provider.hasSubscriptions || !!this._rpcCore.state.queryStorageAt;
123 }
124
125 _emptyDecorated(registry, blockHash) {
126 return {
127 consts: {},
128 errors: {},
129 events: {},
130 query: {},
131 registry,
132 rx: {
133 query: {}
134 },
135 tx: createSubmittable(this._type, this._rx, this._decorateMethod, registry, blockHash)
136 };
137 }
138
139 _createDecorated(registry, fromEmpty, decoratedApi, blockHash) {
140 if (!decoratedApi) {
141 decoratedApi = this._emptyDecorated(registry.registry, blockHash);
142 }
143
144 if (fromEmpty || !registry.decoratedMeta) {
145 registry.decoratedMeta = expandMetadata(registry.registry, registry.metadata);
146 }
147
148 const storage = this._decorateStorage(registry.decoratedMeta, this._decorateMethod, blockHash);
149
150 const storageRx = this._decorateStorage(registry.decoratedMeta, this._rxDecorateMethod, blockHash);
151
152 augmentObject('consts', registry.decoratedMeta.consts, decoratedApi.consts, fromEmpty);
153 augmentObject('errors', registry.decoratedMeta.errors, decoratedApi.errors, fromEmpty);
154 augmentObject('events', registry.decoratedMeta.events, decoratedApi.events, fromEmpty);
155 augmentObject('query', storage, decoratedApi.query, fromEmpty);
156 augmentObject('query', storageRx, decoratedApi.rx.query, fromEmpty);
157
158 decoratedApi.findCall = callIndex => findCall(registry.registry, callIndex);
159
160 decoratedApi.findError = errorIndex => findError(registry.registry, errorIndex);
161
162 decoratedApi.queryMulti = blockHash ? this._decorateMultiAt(decoratedApi, this._decorateMethod, blockHash) : this._decorateMulti(this._decorateMethod);
163 return {
164 decoratedApi,
165 decoratedMeta: registry.decoratedMeta
166 };
167 }
168
169 _injectMetadata(registry, fromEmpty = false) {
170 // clear the decoration, we are redoing it here
171 if (fromEmpty || !registry.decoratedApi) {
172 registry.decoratedApi = this._emptyDecorated(registry.registry);
173 }
174
175 const {
176 decoratedApi,
177 decoratedMeta
178 } = this._createDecorated(registry, fromEmpty, registry.decoratedApi);
179
180 this._consts = decoratedApi.consts;
181 this._errors = decoratedApi.errors;
182 this._events = decoratedApi.events;
183 this._query = decoratedApi.query;
184 this._rx.query = decoratedApi.rx.query;
185
186 const tx = this._decorateExtrinsics(decoratedMeta, this._decorateMethod);
187
188 const rxtx = this._decorateExtrinsics(decoratedMeta, this._rxDecorateMethod);
189
190 if (fromEmpty || !this._extrinsics) {
191 this._extrinsics = tx;
192 this._rx.tx = rxtx;
193 } else {
194 augmentObject('tx', tx, this._extrinsics, false);
195 augmentObject(null, rxtx, this._rx.tx, false);
196 }
197
198 augmentObject(null, decoratedMeta.consts, this._rx.consts, fromEmpty);
199 this.emit('decorated');
200 }
201 /**
202 * @deprecated
203 * backwards compatible endpoint for metadata injection, may be removed in the future (However, it is still useful for testing injection)
204 */
205
206
207 injectMetadata(metadata, fromEmpty, registry) {
208 this._injectMetadata({
209 metadata,
210 registry: registry || this.#registry,
211 specName: this.#registry.createType('Text'),
212 specVersion: BN_ZERO
213 }, fromEmpty);
214 }
215
216 _decorateFunctionMeta(input, output) {
217 output.meta = input.meta;
218 output.method = input.method;
219 output.section = input.section;
220 output.toJSON = input.toJSON;
221
222 if (input.callIndex) {
223 output.callIndex = input.callIndex;
224 }
225
226 return output;
227 } // Filter all RPC methods based on the results of the rpc_methods call. We do this in the following
228 // manner to cater for both old and new:
229 // - when the number of entries are 0, only remove the ones with isOptional (account & contracts)
230 // - when non-zero, remove anything that is not in the array (we don't do this)
231
232
233 _filterRpc(methods, additional) {
234 // add any specific user-base RPCs
235 if (Object.keys(additional).length !== 0) {
236 this._rpcCore.addUserInterfaces(additional); // re-decorate, only adding any new additional interfaces
237
238
239 this._decorateRpc(this._rpcCore, this._decorateMethod, this._rpc);
240
241 this._decorateRpc(this._rpcCore, this._rxDecorateMethod, this._rx.rpc);
242 }
243
244 this._filterRpcMethods(methods);
245 }
246
247 _filterRpcMethods(exposed) {
248 const hasResults = exposed.length !== 0;
249 const allKnown = [...this._rpcCore.mapping.entries()];
250 const allKeys = [];
251
252 for (let i = 0; i < allKnown.length; i++) {
253 const [, {
254 alias,
255 endpoint,
256 method,
257 pubsub,
258 section
259 }] = allKnown[i];
260 allKeys.push(`${section}_${method}`);
261
262 if (pubsub) {
263 allKeys.push(`${section}_${pubsub[1]}`);
264 allKeys.push(`${section}_${pubsub[2]}`);
265 }
266
267 if (alias) {
268 allKeys.push(...alias);
269 }
270
271 if (endpoint) {
272 allKeys.push(endpoint);
273 }
274 }
275
276 const filterKey = k => !allKeys.includes(k);
277
278 const unknown = exposed.filter(filterKey);
279
280 if (unknown.length) {
281 l.warn(`RPC methods not decorated: ${unknown.join(', ')}`);
282 } // loop through all entries we have (populated in decorate) and filter as required
283 // only remove when we have results and method missing, or with no results if optional
284
285
286 for (let i = 0; i < allKnown.length; i++) {
287 const [k, {
288 method,
289 section
290 }] = allKnown[i];
291
292 if (hasResults && !exposed.includes(k) && k !== 'rpc_methods') {
293 if (this._rpc[section]) {
294 delete this._rpc[section][method];
295 delete this._rx.rpc[section][method];
296 }
297 }
298 }
299 }
300
301 _decorateRpc(rpc, decorateMethod, input = {}) {
302 const out = input;
303
304 const decorateFn = (section, method) => {
305 const source = rpc[section][method];
306 const fn = decorateMethod(source, {
307 methodName: method
308 });
309 fn.meta = source.meta;
310 fn.raw = decorateMethod(source.raw, {
311 methodName: method
312 });
313 return fn;
314 };
315
316 for (let s = 0; s < rpc.sections.length; s++) {
317 const section = rpc.sections[s];
318
319 if (!Object.prototype.hasOwnProperty.call(out, section)) {
320 const methods = Object.keys(rpc[section]);
321
322 const decorateInternal = method => decorateFn(section, method);
323
324 for (let m = 0; m < methods.length; m++) {
325 const method = methods[m]; // skip subscriptions where we have a non-subscribe interface
326
327 if (this.hasSubscriptions || !(method.startsWith('subscribe') || method.startsWith('unsubscribe'))) {
328 if (!Object.prototype.hasOwnProperty.call(out, section)) {
329 out[section] = {};
330 }
331
332 lazyMethod(out[section], method, decorateInternal);
333 }
334 }
335 }
336 }
337
338 return out;
339 } // only be called if supportMulti is true
340
341
342 _decorateMulti(decorateMethod) {
343 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
344 return decorateMethod(calls => (this.hasSubscriptions ? this._rpcCore.state.subscribeStorage : this._rpcCore.state.queryStorageAt)(calls.map(args => Array.isArray(args) ? args[0].creator.meta.type.isPlain ? [args[0].creator] : args[0].creator.meta.type.asMap.hashers.length === 1 ? [args[0].creator, args.slice(1)] : [args[0].creator, ...args.slice(1)] : [args.creator])));
345 }
346
347 _decorateMultiAt(atApi, decorateMethod, blockHash) {
348 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
349 return decorateMethod(calls => this._rpcCore.state.queryStorageAt(calls.map(args => {
350 if (Array.isArray(args)) {
351 const {
352 creator
353 } = getAtQueryFn(atApi, args[0].creator);
354 return creator.meta.type.isPlain ? [creator] : creator.meta.type.asMap.hashers.length === 1 ? [creator, args.slice(1)] : [creator, ...args.slice(1)];
355 }
356
357 return [getAtQueryFn(atApi, args.creator).creator];
358 }), blockHash));
359 }
360
361 _decorateExtrinsics({
362 tx
363 }, decorateMethod) {
364 const result = createSubmittable(this._type, this._rx, decorateMethod);
365
366 const lazySection = section => lazyMethods({}, Object.keys(tx[section]), method => this._decorateExtrinsicEntry(tx[section][method], result));
367
368 const sections = Object.keys(tx);
369
370 for (let i = 0; i < sections.length; i++) {
371 lazyMethod(result, sections[i], lazySection);
372 }
373
374 return result;
375 }
376
377 _decorateExtrinsicEntry(method, creator) {
378 const decorated = (...params) => creator(method(...params)); // pass through the `.is`
379
380
381 decorated.is = other => method.is(other); // eslint-disable-next-line @typescript-eslint/no-unsafe-return
382
383
384 return this._decorateFunctionMeta(method, decorated);
385 }
386
387 _decorateStorage({
388 query,
389 registry
390 }, decorateMethod, blockHash) {
391 const result = {};
392
393 const lazySection = section => lazyMethods({}, Object.keys(query[section]), method => blockHash ? this._decorateStorageEntryAt(registry, query[section][method], decorateMethod, blockHash) : this._decorateStorageEntry(query[section][method], decorateMethod));
394
395 const sections = Object.keys(query);
396
397 for (let i = 0; i < sections.length; i++) {
398 lazyMethod(result, sections[i], lazySection);
399 }
400
401 return result;
402 }
403
404 _decorateStorageEntry(creator, decorateMethod) {
405 const getArgs = (args, registry) => extractStorageArgs(registry || this.#registry, creator, args);
406
407 const getQueryAt = blockHash => from(this.at(blockHash)).pipe(map(api => getAtQueryFn(api, creator))); // Disable this where it occurs for each field we are decorating
408
409 /* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
410
411
412 const decorated = this._decorateStorageCall(creator, decorateMethod);
413
414 decorated.creator = creator;
415 decorated.at = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap(q => q(...args))));
416 decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args)));
417
418 decorated.is = key => key.section === creator.section && key.method === creator.method;
419
420 decorated.key = (...args) => u8aToHex(compactStripLength(creator(...args))[1]);
421
422 decorated.keyPrefix = (...args) => u8aToHex(creator.keyPrefix(...args));
423
424 decorated.range = decorateMethod((range, ...args) => this._decorateStorageRange(decorated, args, range));
425 decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args)));
426 decorated.sizeAt = decorateMethod((blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap(q => this._rpcCore.state.getStorageSize(getArgs(args, q.creator.meta.registry), blockHash)))); // .keys() & .entries() only available on map types
427
428 if (creator.iterKey && creator.meta.type.isMap) {
429 decorated.entries = decorateMethod(memo(this.#instanceId, (...args) => this._retrieveMapEntries(creator, null, args)));
430 decorated.entriesAt = decorateMethod(memo(this.#instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap(q => this._retrieveMapEntries(q.creator, blockHash, args)))));
431 decorated.entriesPaged = decorateMethod(memo(this.#instanceId, opts => this._retrieveMapEntriesPaged(creator, undefined, opts)));
432 decorated.keys = decorateMethod(memo(this.#instanceId, (...args) => this._retrieveMapKeys(creator, null, args)));
433 decorated.keysAt = decorateMethod(memo(this.#instanceId, (blockHash, ...args) => getQueryAt(blockHash).pipe(switchMap(q => this._retrieveMapKeys(q.creator, blockHash, args)))));
434 decorated.keysPaged = decorateMethod(memo(this.#instanceId, opts => this._retrieveMapKeysPaged(creator, undefined, opts)));
435 }
436
437 if (this.supportMulti && creator.meta.type.isMap) {
438 // When using double map storage function, user need to pass double map key as an array
439 decorated.multi = decorateMethod(args => creator.meta.type.asMap.hashers.length === 1 ? this._retrieveMulti(args.map(a => [creator, [a]])) : this._retrieveMulti(args.map(a => [creator, a])));
440 }
441 /* eslint-enable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
442
443
444 return this._decorateFunctionMeta(creator, decorated);
445 }
446
447 _decorateStorageEntryAt(registry, creator, decorateMethod, blockHash) {
448 const getArgs = args => extractStorageArgs(registry, creator, args); // Disable this where it occurs for each field we are decorating
449
450 /* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
451
452
453 const decorated = decorateMethod((...args) => this._rpcCore.state.getStorage(getArgs(args), blockHash));
454 decorated.creator = creator;
455 decorated.hash = decorateMethod((...args) => this._rpcCore.state.getStorageHash(getArgs(args), blockHash));
456
457 decorated.is = key => key.section === creator.section && key.method === creator.method;
458
459 decorated.key = (...args) => u8aToHex(compactStripLength(creator(creator.meta.type.isPlain ? undefined : args))[1]);
460
461 decorated.keyPrefix = (...keys) => u8aToHex(creator.keyPrefix(...keys));
462
463 decorated.size = decorateMethod((...args) => this._rpcCore.state.getStorageSize(getArgs(args), blockHash)); // .keys() & .entries() only available on map types
464
465 if (creator.iterKey && creator.meta.type.isMap) {
466 decorated.entries = decorateMethod(memo(this.#instanceId, (...args) => this._retrieveMapEntries(creator, blockHash, args)));
467 decorated.entriesPaged = decorateMethod(memo(this.#instanceId, opts => this._retrieveMapEntriesPaged(creator, blockHash, opts)));
468 decorated.keys = decorateMethod(memo(this.#instanceId, (...args) => this._retrieveMapKeys(creator, blockHash, args)));
469 decorated.keysPaged = decorateMethod(memo(this.#instanceId, opts => this._retrieveMapKeysPaged(creator, blockHash, opts)));
470 }
471
472 if (this.supportMulti && creator.meta.type.isMap) {
473 // When using double map storage function, user need to pass double map key as an array
474 decorated.multi = decorateMethod(args => creator.meta.type.asMap.hashers.length === 1 ? this._retrieveMulti(args.map(a => [creator, [a]]), blockHash) : this._retrieveMulti(args.map(a => [creator, a]), blockHash));
475 }
476 /* eslint-enable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment */
477
478
479 return this._decorateFunctionMeta(creator, decorated);
480 } // Decorate the base storage call. In the case or rxjs or promise-without-callback (await)
481 // we make a subscription, alternatively we push this through a single-shot query
482
483
484 _decorateStorageCall(creator, decorateMethod) {
485 return decorateMethod((...args) => {
486 return this.hasSubscriptions ? this._rpcCore.state.subscribeStorage([extractStorageArgs(this.#registry, creator, args)]).pipe(map(([data]) => data) // extract first/only result from list
487 ) : this._rpcCore.state.getStorage(extractStorageArgs(this.#registry, creator, args));
488 }, {
489 methodName: creator.method,
490 overrideNoSub: (...args) => this._rpcCore.state.getStorage(extractStorageArgs(this.#registry, creator, args))
491 });
492 }
493
494 _decorateStorageRange(decorated, args, range) {
495 const outputType = unwrapStorageType(this.#registry, decorated.creator.meta.type, decorated.creator.meta.modifier.isOptional);
496 return this._rpcCore.state.queryStorage([decorated.key(...args)], ...range).pipe(map(result => result.map(([blockHash, [value]]) => [blockHash, this.createType(outputType, value.isSome ? value.unwrap().toHex() : undefined)])));
497 } // retrieve a set of values for a specific set of keys - here we chunk the keys into PAGE_SIZE sizes
498
499
500 _retrieveMulti(keys, blockHash) {
501 if (!keys.length) {
502 return of([]);
503 }
504
505 const queryCall = this.hasSubscriptions && !blockHash ? this._rpcCore.state.subscribeStorage : this._rpcCore.state.queryStorageAt;
506 return combineLatest(arrayChunk(keys, PAGE_SIZE_V).map(k => blockHash ? queryCall(k, blockHash) : queryCall(k))).pipe(map(arrayFlatten));
507 }
508
509 _retrieveMapKeys({
510 iterKey,
511 meta,
512 method,
513 section
514 }, at, args) {
515 assert(iterKey && meta.type.isMap, 'keys can only be retrieved on maps');
516 const headKey = iterKey(...args).toHex();
517 const startSubject = new BehaviorSubject(headKey);
518 const getKeysPaged = at ? startKey => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey, at) : startKey => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey);
519
520 const setMeta = key => key.setMeta(meta, section, method);
521
522 return startSubject.pipe(switchMap(getKeysPaged), map(keys => keys.map(setMeta)), tap(keys => {
523 setTimeout(() => {
524 keys.length === PAGE_SIZE_K ? startSubject.next(keys[PAGE_SIZE_K - 1].toHex()) : startSubject.complete();
525 }, 0);
526 }), toArray(), // toArray since we want to startSubject to be completed
527 map(arrayFlatten));
528 }
529
530 _retrieveMapKeysPaged({
531 iterKey,
532 meta,
533 method,
534 section
535 }, at, opts) {
536 assert(iterKey && meta.type.isMap, 'keys can only be retrieved on maps');
537
538 const setMeta = key => key.setMeta(meta, section, method);
539
540 const getKeysPaged = at ? headKey => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey, at) : headKey => this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey);
541 return getKeysPaged(iterKey(...opts.args).toHex()).pipe(map(keys => keys.map(setMeta)));
542 }
543
544 _retrieveMapEntries(entry, at, args) {
545 const queryStorageAt = at ? keys => this._rpcCore.state.queryStorageAt(keys, at) : keys => this._rpcCore.state.queryStorageAt(keys);
546 return this._retrieveMapKeys(entry, at, args).pipe(switchMap(keys => keys.length ? combineLatest(arrayChunk(keys, PAGE_SIZE_V).map(queryStorageAt)).pipe(map(valsArr => arrayFlatten(valsArr).map((value, index) => [keys[index], value]))) : of([])));
547 }
548
549 _retrieveMapEntriesPaged(entry, at, opts) {
550 const queryStorageAt = at ? keys => this._rpcCore.state.queryStorageAt(keys, at) : keys => this._rpcCore.state.queryStorageAt(keys);
551 return this._retrieveMapKeysPaged(entry, at, opts).pipe(switchMap(keys => keys.length ? queryStorageAt(keys).pipe(map(valsArr => valsArr.map((value, index) => [keys[index], value]))) : of([])));
552 }
553
554 _decorateDeriveRx(decorateMethod) {
555 var _this$_runtimeVersion, _this$_options$typesB, _this$_options$typesB2, _this$_options$typesB3;
556
557 const specName = (_this$_runtimeVersion = this._runtimeVersion) === null || _this$_runtimeVersion === void 0 ? void 0 : _this$_runtimeVersion.specName.toString(); // Pull in derive from api-derive
558
559 const available = getAvailableDerives(this.#instanceId, this._rx, objectSpread({}, this._options.derives, (_this$_options$typesB = this._options.typesBundle) === null || _this$_options$typesB === void 0 ? void 0 : (_this$_options$typesB2 = _this$_options$typesB.spec) === null || _this$_options$typesB2 === void 0 ? void 0 : (_this$_options$typesB3 = _this$_options$typesB2[specName || '']) === null || _this$_options$typesB3 === void 0 ? void 0 : _this$_options$typesB3.derives));
560 return decorateDeriveSections(decorateMethod, available);
561 }
562
563 _decorateDerive(decorateMethod) {
564 return decorateDeriveSections(decorateMethod, this._rx.derive);
565 }
566 /**
567 * Put the `this.onCall` function of ApiRx here, because it is needed by
568 * `api._rx`.
569 */
570
571
572 _rxDecorateMethod = method => {
573 return method;
574 };
575}
\No newline at end of file