@dfinity/agent
Version:
JavaScript and TypeScript library to interact with the Internet Computer
182 lines • 8.39 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_POLLING_OPTIONS = exports.defaultStrategy = exports.strategy = void 0;
exports.pollForResponse = pollForResponse;
exports.constructRequest = constructRequest;
const certificate_ts_1 = require("../certificate.js");
const errors_ts_1 = require("../errors.js");
exports.strategy = __importStar(require("./strategy.js"));
const strategy_ts_1 = require("./strategy.js");
const types_ts_1 = require("../agent/http/types.js");
const index_ts_1 = require("../agent/index.js");
const utils_1 = require("@noble/hashes/utils");
var strategy_ts_2 = require("./strategy.js");
Object.defineProperty(exports, "defaultStrategy", { enumerable: true, get: function () { return strategy_ts_2.defaultStrategy; } });
exports.DEFAULT_POLLING_OPTIONS = {
strategy: (0, strategy_ts_1.defaultStrategy)(),
preSignReadStateRequest: false,
};
/**
* Check if an object has a property
* @param value the object that might have the property
* @param property the key of property we're looking for
*/
function hasProperty(value, property) {
return Object.prototype.hasOwnProperty.call(value, property);
}
function isObjectWithProperty(value, property) {
return value !== null && typeof value === 'object' && hasProperty(value, property);
}
function hasFunction(value, property) {
return hasProperty(value, property) && typeof value[property] === 'function';
}
/**
* Check if value is a signed read state request with expiry
* @param value to check
*/
function isSignedReadStateRequestWithExpiry(value) {
return (isObjectWithProperty(value, 'body') &&
isObjectWithProperty(value.body, 'content') &&
value.body.content.request_type ===
types_ts_1.ReadRequestType.ReadState &&
isObjectWithProperty(value.body.content, 'ingress_expiry') &&
typeof value.body.content.ingress_expiry === 'object' &&
value.body.content.ingress_expiry !== null &&
hasFunction(value.body.content.ingress_expiry, 'toHash'));
}
/**
* Polls the IC to check the status of the given request then
* returns the response bytes once the request has been processed.
* @param agent The agent to use to poll read_state.
* @param canisterId The effective canister ID.
* @param requestId The Request ID to poll status for.
* @param options polling options to control behavior
*/
async function pollForResponse(agent, canisterId, requestId, options = {}) {
const path = [(0, utils_1.utf8ToBytes)('request_status'), requestId];
let state;
let currentRequest;
const preSignReadStateRequest = options.preSignReadStateRequest ?? false;
if (preSignReadStateRequest) {
// If preSignReadStateRequest is true, we need to create a new request
currentRequest = await constructRequest({
paths: [path],
agent,
pollingOptions: options,
});
state = await agent.readState(canisterId, { paths: [path] }, undefined, currentRequest);
}
else {
// If preSignReadStateRequest is false, we use the default strategy and sign the request each time
state = await agent.readState(canisterId, { paths: [path] });
}
if (agent.rootKey == null) {
throw errors_ts_1.ExternalError.fromCode(new errors_ts_1.MissingRootKeyErrorCode());
}
const cert = await certificate_ts_1.Certificate.create({
certificate: state.certificate,
rootKey: agent.rootKey,
canisterId: canisterId,
blsVerify: options.blsVerify,
agent,
});
const maybeBuf = (0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path([...path, (0, utils_1.utf8ToBytes)('status')]));
let status;
if (typeof maybeBuf === 'undefined') {
// Missing requestId means we need to wait
status = index_ts_1.RequestStatusResponseStatus.Unknown;
}
else {
status = new TextDecoder().decode(maybeBuf);
}
switch (status) {
case index_ts_1.RequestStatusResponseStatus.Replied: {
return {
reply: (0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path([...path, 'reply'])),
certificate: cert,
};
}
case index_ts_1.RequestStatusResponseStatus.Received:
case index_ts_1.RequestStatusResponseStatus.Unknown:
case index_ts_1.RequestStatusResponseStatus.Processing: {
// Execute the polling strategy, then retry.
const strategy = options.strategy ?? (0, strategy_ts_1.defaultStrategy)();
await strategy(canisterId, requestId, status);
return pollForResponse(agent, canisterId, requestId, {
...options,
request: currentRequest,
});
}
case index_ts_1.RequestStatusResponseStatus.Rejected: {
const rejectCode = new Uint8Array((0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path([...path, 'reject_code'])))[0];
const rejectMessage = new TextDecoder().decode((0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path([...path, 'reject_message'])));
const errorCodeBuf = (0, certificate_ts_1.lookupResultToBuffer)(cert.lookup_path([...path, 'error_code']));
const errorCode = errorCodeBuf ? new TextDecoder().decode(errorCodeBuf) : undefined;
throw errors_ts_1.RejectError.fromCode(new errors_ts_1.CertifiedRejectErrorCode(requestId, rejectCode, rejectMessage, errorCode));
}
case index_ts_1.RequestStatusResponseStatus.Done:
// This is _technically_ not an error, but we still didn't see the `Replied` status so
// we don't know the result and cannot decode it.
throw errors_ts_1.UnknownError.fromCode(new errors_ts_1.RequestStatusDoneNoReplyErrorCode(requestId));
}
throw errors_ts_1.UNREACHABLE_ERROR;
}
// Determine if we should reuse the read state request or create a new one
// based on the options provided.
/**
* Constructs a read state request for the given paths.
* If the request is already signed and has an expiry, it will be returned as is.
* Otherwise, a new request will be created.
* @param options The options to use for creating the request.
* @param options.paths The paths to read from.
* @param options.agent The agent to use to create the request.
* @param options.pollingOptions The options to use for creating the request.
* @returns The read state request.
*/
async function constructRequest(options) {
const { paths, agent, pollingOptions } = options;
if (pollingOptions.request && isSignedReadStateRequestWithExpiry(pollingOptions.request)) {
return pollingOptions.request;
}
const request = await agent.createReadStateRequest?.({
paths,
}, undefined);
if (!isSignedReadStateRequestWithExpiry(request)) {
throw errors_ts_1.InputError.fromCode(new errors_ts_1.InvalidReadStateRequestErrorCode(request));
}
return request;
}
//# sourceMappingURL=index.js.map
;