UNPKG

@dfinity/agent

Version:

JavaScript and TypeScript library to interact with the Internet Computer

182 lines 8.39 kB
"use strict"; 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