UNPKG

@daml/ledger

Version:

Client side API implementation for a Daml based ledger. This library implements the JSON based API for a Daml ledger documented in https://docs.daml.com/json-api/index.html.

1,071 lines • 76.2 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Ledger = exports.WsState = exports.UserRightHelper = void 0; exports.assert = assert; // Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. // SPDX-License-Identifier: Apache-2.0 var types_1 = require("@daml/types"); var jtv = __importStar(require("@mojotech/json-type-validation")); var cross_fetch_1 = __importDefault(require("cross-fetch")); var events_1 = require("events"); var isomorphic_ws_1 = __importDefault(require("isomorphic-ws")); var lodash_1 = __importStar(require("lodash")); var partyInfoDecoder = jtv.object({ identifier: jtv.string(), displayName: jtv.optional(jtv.string()), isLocal: jtv.boolean(), }); var userDecoder = jtv.object({ userId: jtv.string(), primaryParty: jtv.optional(jtv.string()), }); var UserRightHelper = /** @class */ (function () { function UserRightHelper() { } UserRightHelper.canActAs = function (party) { return { type: "CanActAs", party: party }; }; UserRightHelper.canReadAs = function (party) { return { type: "CanReadAs", party: party }; }; UserRightHelper.participantAdmin = { type: "ParticipantAdmin", }; return UserRightHelper; }()); exports.UserRightHelper = UserRightHelper; var userRightDecoder = jtv.oneOf(jtv.object({ type: jtv.constant("CanActAs"), party: jtv.string(), }), jtv.object({ type: jtv.constant("CanReadAs"), party: jtv.string(), }), jtv.object({ type: jtv.constant("ParticipantAdmin"), })); var decode = function (decoder, data) { return jtv.Result.withException(decoder.run(data)); }; function lookupTemplateOrUnknownInterface(templateId) { try { return (0, types_1.lookupTemplate)(templateId); } catch (e) { if (e instanceof Error) return { templateId: templateId, sdkVersion: "2.10.2", // there is no way to properly decode in this case, so we // discard the data instead. #15042 decoder: jtv.succeed({}), keyDecoder: jtv.succeed(undefined), }; else throw e; } } var decodeTemplateId = function () { return jtv.string(); }; /** * Decoder for a [[CreateEvent]]. */ var decodeCreateEvent = function (template) { return jtv.object({ templateId: decodeTemplateId(), contractId: (0, types_1.ContractId)(template).decoder, signatories: (0, types_1.List)(types_1.Party).decoder, observers: (0, types_1.List)(types_1.Party).decoder, agreementText: types_1.Text.decoder, key: template.keyDecoder, payload: template.decoder, }); }; /** * Decoder for a [[CreateEvent]] of unknown contract template. */ var decodeCreateEventUnknown = jtv .valueAt(["templateId"], jtv.string()) .andThen(function (templateId) { return decodeCreateEvent(lookupTemplateOrUnknownInterface(templateId)); }); /** * Decoder for an [[ArchiveEvent]]. */ var decodeArchiveEvent = function (template) { return jtv.object({ templateId: decodeTemplateId(), contractId: (0, types_1.ContractId)(template).decoder, }); }; /** * Decoder for an [[ArchiveEvent]] of unknown contract template. */ var decodeArchiveEventUnknown = jtv .valueAt(["templateId"], jtv.string()) .andThen(function (templateId) { return decodeArchiveEvent(lookupTemplateOrUnknownInterface(templateId)); }); /** * Decoder for an [[Event]]. */ var decodeEvent = function (template) { return jtv.oneOf(jtv.object({ created: decodeCreateEvent(template), matchedQueries: jtv.array(jtv.number()), }), jtv.object({ created: decodeCreateEvent(template) }), jtv.object({ archived: decodeArchiveEvent(template) })); }; /** * Decoder for an [[Event]] with unknown contract template. */ var decodeEventUnknown = jtv.oneOf(jtv.object({ created: decodeCreateEventUnknown, matchedQueries: jtv.array(jtv.number()), }), jtv.object({ created: decodeCreateEventUnknown }), jtv.object({ archived: decodeArchiveEventUnknown })); /** * @internal */ function stripPackage(templateId) { return templateId.split(":").slice(1).join(":"); } /** * @internal */ function decodeArchiveResponse(template, archiveMethod, // eslint-disable-next-line @typescript-eslint/ban-types archiveCommand) { return __awaiter(this, void 0, void 0, function () { var _a, _b, events; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, archiveCommand()]; case 1: _a = _c.sent(), _b = _a[0], events = _a[1]; if (events.length === 1 && "archived" in events[0] && stripPackage(events[0].archived.templateId) === stripPackage(template.templateId)) { return [2 /*return*/, events[0].archived]; } else { throw Error("Ledger.".concat(archiveMethod, " is expected to cause one archive event for template ").concat(template.templateId, " but caused ").concat(JSON.stringify(events), ".")); } return [2 /*return*/]; } }); }); } /** * @internal */ function isRecordWith(field, x) { return typeof x === "object" && x !== null && field in x; } /** * * @internal */ function isCreateWithMatchedQueries(event) { return isRecordWith("created", event); } /** @internal * exported for testing only */ function assert(b, m) { if (!b) { throw m; } } // TODO(MH): Support comparison queries. /** @internal * * Official documentation (docs/source/json-api/search-query-language.rst) * currently explicitly forbids the use of lists, textmaps and genmaps in * queries. As long as that restriction stays, there is no need for any kind of * encoding here. */ function encodeQuery(_template, query) { return query; } /** * @internal */ var decodeLedgerResponse = jtv.object({ status: jtv.number(), result: jtv.unknownJson(), warnings: jtv.optional(jtv.unknownJson()), }); /** * @internal */ var decodeLedgerError = jtv.object({ status: jtv.number(), errors: jtv.array(jtv.string()), warnings: jtv.optional(jtv.unknownJson()), }); var StreamEventEmitter = /** @class */ (function (_super) { __extends(StreamEventEmitter, _super); function StreamEventEmitter(_a) { var beforeClosing = _a.beforeClosing; var _this = _super.call(this) || this; _this.beforeClosing = beforeClosing; return _this; } StreamEventEmitter.prototype.close = function () { this.beforeClosing(); this.emit("close", { code: 4000, reason: "called .close()" }); this.removeAllListeners(); }; return StreamEventEmitter; }(events_1.EventEmitter)); var NoOffsetReceivedYet = Symbol("NoOffsetReceivedYet"); var NullOffsetReceived = Symbol("NullOffsetReceived"); function append(map, key, value) { var _a; if (map.has(key)) { (_a = map.get(key)) === null || _a === void 0 ? void 0 : _a.push(value); } else { map.set(key, [value]); } } /** * @deprecated All usages of this function should be replaced by just * iterating over the iterator. For this to happen, the TS * compiler requires the --downlevelIteration flag, which * however does not play nicely with Jest when running the * tests. * * TODO Moving the compilation target to ES6 probably would solve this, investigate. */ function materialize(iterator) { return Array.from(iterator); } var WsState; (function (WsState) { WsState[WsState["Connecting"] = 0] = "Connecting"; WsState[WsState["Open"] = 1] = "Open"; WsState[WsState["Closing"] = 2] = "Closing"; WsState[WsState["Closed"] = 3] = "Closed"; })(WsState || (exports.WsState = WsState = {})); /** * @internal * * A special handler for stream requests to the /v1/stream/query endpoint. * The query endpoint supports providing offsets on a per-query basis. * This class leverages this feature by multiplexing virtual streaming requests to a single web socket. */ var QueryStreamsManager = /** @class */ (function () { function QueryStreamsManager(_a) { var token = _a.token, wsBaseUrl = _a.wsBaseUrl, reconnectThreshold = _a.reconnectThreshold; // Mutable state BEGIN // Ongoing streaming queries that will be the downstream consumers of web socket messages this.queries = new Set(); // Lookup tables used to route events to the relevant consumers: // - consumers for create events can be looked up based on their match index // - store the offset by which a match indexes needs to be shifted before the event is passed to the consumer // - archive events can be lookup up by template identifier // - this causes the consumer to observe what is known as "phantom archives", which are known and documented this.matchIndexLookupTable = []; this.templateIdsLookupTable = {}; // Accumulates each query in a flattened form to be sent as a single request to the JSON API this.request = []; // to track changes on web socket queries this.wsQueriesChange = false; this.protocols = ["jwt.token." + token, "daml.ws.auth"]; this.url = wsBaseUrl + QueryStreamsManager.ENDPOINT; this.reconnectThresholdMs = reconnectThreshold; } QueryStreamsManager.toRequest = function (query) { var request = query.queries.length == 0 ? [{ templateIds: [query.template.templateId] }] : query.queries.map(function (q) { return ({ templateIds: [query.template.templateId], query: encodeQuery(query.template, q), }); }); if (typeof query.offset === "string") { for (var _i = 0, request_1 = request; _i < request_1.length; _i++) { var r = request_1[_i]; r.offset = query.offset; } } return request; }; QueryStreamsManager.prototype.resetAllState = function () { // close ws if defined if (this.ws !== undefined) { this.ws.close(); } this.queries.clear(); this.matchIndexLookupTable = []; this.templateIdsLookupTable = {}; this.request = []; this.ws = undefined; this.wsLiveSince = undefined; this.wsQueriesChange = false; }; QueryStreamsManager.prototype.handleQueriesChange = function () { //eslint-disable-next-line @typescript-eslint/no-this-alias var manager = this; // stable self-reference for callbacks if (manager.queries.size > 0) { if (manager.ws !== undefined) { //set the queries change flag to true, this should eventually get reset once the ws is closed. manager.wsQueriesChange = true; manager.wsLiveSince = undefined; manager.ws.close(); manager.ws = undefined; } var ws = new isomorphic_ws_1.default(manager.url, manager.protocols); var onWsMessage_1 = function (ws) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return function (_a) { var data = _a.data; if (ws.readyState === WsState.Open) { var json = JSON.parse(data.toString()); if (isRecordWith("events", json)) { var events = jtv.Result.withException(jtv.array(decodeEventUnknown).run(json.events)); var multiplexer = new Map(); for (var _i = 0, events_2 = events; _i < events_2.length; _i++) { var event_1 = events_2[_i]; if (isCreateWithMatchedQueries(event_1)) { var consumersToMatchedQueries = new Map(); for (var _b = 0, _c = event_1.matchedQueries; _b < _c.length; _b++) { var matchIndex = _c[_b]; var _d = manager.matchIndexLookupTable[matchIndex], consumer = _d[0], matchIndexOffset = _d[1]; append(consumersToMatchedQueries, consumer, matchIndexOffset); } for (var _e = 0, _f = materialize(consumersToMatchedQueries.entries()); _e < _f.length; _e++) { var _g = _f[_e], consumer = _g[0], matchedQueries = _g[1]; // Create a new copy of the event for each consumer to freely mangle the matched queries and avoid sharing mutable state append(multiplexer, consumer, __assign(__assign({}, event_1), { matchedQueries: matchedQueries })); } } else if ("archived" in event_1) { var consumers = manager.templateIdsLookupTable[event_1.archived.templateId]; for (var _h = 0, _j = materialize(consumers.values()); _h < _j.length; _h++) { var consumer = _j[_h]; // Create a new copy of the event for each consumer to avoid sharing mutable state append(multiplexer, consumer, __assign({}, event_1)); } } else { console.error("".concat(event_1, " unknown event type received, expected created with matchedQueries or archived"), json); } } for (var _k = 0, _m = materialize(multiplexer.entries()); _k < _m.length; _k++) { var _o = _m[_k], consumer = _o[0], events_4 = _o[1]; for (var _p = 0, events_3 = events_4; _p < events_3.length; _p++) { var event_2 = events_3[_p]; if (isCreateWithMatchedQueries(event_2)) { consumer.state.set(event_2.created.contractId, event_2.created); } else if ("archived" in event_2) { consumer.state.delete(event_2.archived.contractId); } } consumer.stream.emit("change", Array.from(consumer.state.values()), events_4); } if (isRecordWith("offset", json)) { var offset = jtv.Result.withException(jtv.oneOf(jtv.constant(null), jtv.string()).run(json.offset)); if (manager.wsLiveSince === undefined) { //on receiving the first offset event we consider the web socket to be live. manager.wsLiveSince = Date.now(); } for (var _q = 0, _r = materialize(manager.queries.values()); _q < _r.length; _q++) { var consumer = _r[_q]; if (!(typeof consumer.offset === "string")) { // Rebuilding the state array from scratch to make sure mutable state is not shared between the 'change' and 'live' event consumer.stream.emit("live", Array.from(consumer.state.values())); } if (typeof offset === "string") { consumer.offset = offset; } else { consumer.offset = NullOffsetReceived; } } } } else if (isRecordWith("warnings", json)) { for (var _s = 0, _u = materialize(manager.queries.values()); _s < _u.length; _s++) { var query = _u[_s]; console.warn("".concat(query.caller, " warnings"), json); } } else if (isRecordWith("errors", json)) { for (var _v = 0, _w = materialize(manager.queries.values()); _v < _w.length; _v++) { var query = _w[_v]; console.warn("".concat(query.caller, " errors"), json); } } else { for (var _x = 0, _y = materialize(manager.queries.values()); _x < _y.length; _x++) { var query = _y[_x]; console.error("".concat(query.caller, " unknown message"), json); } } } }; }; var onWsOpen_1 = function () { var _a; // only make a new websocket request if we have registered queries if (manager.queries.size > 0) { var newRequests = []; var newMatchIndexLookupTable = []; for (var _i = 0, _b = materialize(manager.queries.values()); _i < _b.length; _i++) { var query = _b[_i]; var request = QueryStreamsManager.toRequest(query); // Add entries to the lookup table for create events var matchIndexOffset = newMatchIndexLookupTable.length; var matchIndexLookupTableEntries = new Array(request.length).fill([query, matchIndexOffset]); newMatchIndexLookupTable = newMatchIndexLookupTable.concat(matchIndexLookupTableEntries); // Add entries to the lookup table for archive events for (var _c = 0, request_2 = request; _c < request_2.length; _c++) { var templateIds = request_2[_c].templateIds; for (var _d = 0, templateIds_1 = templateIds; _d < templateIds_1.length; _d++) { var templateId = templateIds_1[_d]; manager.templateIdsLookupTable[templateId] = manager.templateIdsLookupTable[templateId] || new Set(); manager.templateIdsLookupTable[templateId].add(query); } } //since we go through all queries on the manager, we should be safely able to rebuild the whole request newRequests = newRequests.concat(request); } manager.request = newRequests; manager.matchIndexLookupTable = newMatchIndexLookupTable; (_a = manager.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(Array.from(manager.request.values()))); } }; var onWsClose_1 = function () { //if not a web socket queries change then we need to initiate reconnect logic. if (!manager.wsQueriesChange) { // The web socket has been closed due to an error // If the conditions are met, attempt to reconnect and/or inform downstream consumers var now = Date.now(); if (manager.wsLiveSince !== undefined && now - manager.wsLiveSince >= manager.reconnectThresholdMs) { console.log("Reconnecting ws, previously liveSince: ".concat(manager.wsLiveSince, " and reconnectThresholdMs: ").concat(manager.reconnectThresholdMs)); manager.wsLiveSince = undefined; var ws_1 = new isomorphic_ws_1.default(manager.url, manager.protocols); ws_1.addEventListener("open", onWsOpen_1); ws_1.addEventListener("message", onWsMessage_1(ws_1)); ws_1.addEventListener("close", onWsClose_1); manager.ws = ws_1; } else { // ws has closed too quickly / never managed to connect: we give up for (var _i = 0, _a = materialize(manager.queries.values()); _i < _a.length; _i++) { var consumer = _a[_i]; consumer.stream.emit("close", { code: 4001, reason: "ws connection failed", }); consumer.stream.removeAllListeners(); } manager.resetAllState(); } } else { //this was triggered due to queries change , reset the flag manager.wsQueriesChange = false; } }; // Purposefully ignoring 'error' events; they are always followed by a 'close' event, which needs to be handled anyway ws.addEventListener("open", onWsOpen_1); ws.addEventListener("message", onWsMessage_1(ws)); ws.addEventListener("close", onWsClose_1); //eslint-disable-next-line @typescript-eslint/no-this-alias manager.ws = ws; } }; QueryStreamsManager.prototype.streamSubmit = function (template, queries, caller) { //eslint-disable-next-line @typescript-eslint/no-this-alias var manager = this; var query = { template: template, queries: queries, stream: new StreamEventEmitter({ beforeClosing: function () { manager.queries.delete(query); // if no more queries then just let it go if (manager.queries.size > 0) { manager.handleQueriesChange(); } }, }), state: new Map(), offset: NoOffsetReceivedYet, caller: caller, }; manager.queries.add(query); manager.handleQueriesChange(); // eslint-disable-next-line @typescript-eslint/no-explicit-any var on = function (type, listener) { var _a, _b; if (((_a = manager.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WsState.Open || ((_b = manager.ws) === null || _b === void 0 ? void 0 : _b.readyState) === WsState.Connecting) { query.stream.on(type, listener); } else { console.error("Trying to add a listener to a closed stream."); } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any var off = function (type, listener) { var _a, _b; if (((_a = manager.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WsState.Open || ((_b = manager.ws) === null || _b === void 0 ? void 0 : _b.readyState) === WsState.Connecting) { query.stream.off(type, listener); } else { console.error("Trying to remove a listener from a closed stream."); } }; var close = function () { query.stream.close(); }; return { on: on, off: off, close: close }; }; QueryStreamsManager.ENDPOINT = "v1/stream/query"; return QueryStreamsManager; }()); /** * An object of type `Ledger` represents a handle to a Daml ledger. */ var Ledger = /** @class */ (function () { /** * Construct a new `Ledger` object. See [[LedgerOptions]] for the constructor arguments. */ function Ledger(_a) { var token = _a.token, httpBaseUrl = _a.httpBaseUrl, wsBaseUrl = _a.wsBaseUrl, _b = _a.reconnectThreshold, reconnectThreshold = _b === void 0 ? 30000 : _b, _c = _a.multiplexQueryStreams, multiplexQueryStreams = _c === void 0 ? false : _c; if (!httpBaseUrl) { httpBaseUrl = "".concat(window.location.protocol, "//").concat(window.location.host, "/"); } if (!(httpBaseUrl.startsWith("http://") || httpBaseUrl.startsWith("https://"))) { throw Error("Ledger: httpBaseUrl must start with 'http://' or 'https://'. (".concat(httpBaseUrl, ")")); } if (!httpBaseUrl.endsWith("/")) { throw Error("Ledger: httpBaseUrl must end with '/'. (".concat(httpBaseUrl, ")")); } if (!wsBaseUrl) { wsBaseUrl = "ws" + httpBaseUrl.slice(4); } if (!(wsBaseUrl.startsWith("ws://") || wsBaseUrl.startsWith("wss://"))) { throw Error("Ledger: wsBaseUrl must start with 'ws://' or 'wss://'. (".concat(wsBaseUrl, ")")); } if (!wsBaseUrl.endsWith("/")) { throw Error("Ledger: wsBaseUrl must end with '/'. (".concat(wsBaseUrl, ")")); } this.token = token; this.httpBaseUrl = httpBaseUrl; this.wsBaseUrl = wsBaseUrl; this.reconnectThreshold = reconnectThreshold; this.multiplexQueryStreams = multiplexQueryStreams; this.queryStreamsManager = new QueryStreamsManager({ token: token, wsBaseUrl: wsBaseUrl, reconnectThreshold: reconnectThreshold, }); } /** * @internal */ Ledger.prototype.auth = function () { return { Authorization: "Bearer " + this.token }; }; /** * @internal */ Ledger.prototype.throwOnError = function (r) { return __awaiter(this, void 0, void 0, function () { var json; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!!r.ok) return [3 /*break*/, 2]; return [4 /*yield*/, r.json()]; case 1: json = _a.sent(); console.log(json); throw decode(decodeLedgerError, json); case 2: return [2 /*return*/]; } }); }); }; /** * @internal * * Internal function to submit a command to the JSON API. */ Ledger.prototype.submit = function (endpoint_1, payload_1) { return __awaiter(this, arguments, void 0, function (endpoint, payload, method) { var httpResponse, json, ledgerResponse; if (method === void 0) { method = "post"; } return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, (0, cross_fetch_1.default)(this.httpBaseUrl + endpoint, { body: JSON.stringify(payload), headers: __assign(__assign({}, this.auth()), { "Content-type": "application/json" }), method: method, })]; case 1: httpResponse = _a.sent(); return [4 /*yield*/, this.throwOnError(httpResponse)]; case 2: _a.sent(); return [4 /*yield*/, httpResponse.json()]; case 3: json = _a.sent(); ledgerResponse = jtv.Result.withException(decodeLedgerResponse.run(json)); if (!(ledgerResponse.status >= 200 && ledgerResponse.status <= 299)) { console.log("Request to ".concat(endpoint, " returned status ").concat(ledgerResponse.status, " with response body: ").concat(JSON.stringify(json), ".")); throw decode(decodeLedgerError, json); } if (ledgerResponse.warnings) { console.warn(ledgerResponse.warnings); } return [2 /*return*/, ledgerResponse.result]; } }); }); }; /** * Retrieve contracts for a given template. * * When no `query` argument is given, all contracts visible to the submitting party are returned. * When a `query` argument is given, only those contracts matching the query are returned. See * https://docs.daml.com/json-api/search-query-language.html for a description of the query * language. * * @param template The contract template of the contracts to be matched against. * @param query The contract query for the contracts to be matched against. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. * */ Ledger.prototype.query = function (template, query) { return __awaiter(this, void 0, void 0, function () { var payload, json; return __generator(this, function (_a) { switch (_a.label) { case 0: payload = { templateIds: [template.templateId], query: encodeQuery(template, query), }; return [4 /*yield*/, this.submit("v1/query", payload)]; case 1: json = _a.sent(); return [2 /*return*/, jtv.Result.withException(jtv.array(decodeCreateEvent(template)).run(json))]; } }); }); }; /** * Fetch a contract identified by its contract ID. * * @param template The template of the contract to be fetched. * @param contractId The contract id of the contract to be fetched. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. * */ Ledger.prototype.fetch = function (template, contractId) { return __awaiter(this, void 0, void 0, function () { var payload, json; return __generator(this, function (_a) { switch (_a.label) { case 0: payload = { templateId: template.templateId, contractId: (0, types_1.ContractId)(template).encode(contractId), }; return [4 /*yield*/, this.submit("v1/fetch", payload)]; case 1: json = _a.sent(); return [2 /*return*/, jtv.Result.withException(jtv.oneOf(jtv.constant(null), decodeCreateEvent(template)).run(json))]; } }); }); }; /** * Fetch a contract identified by its contract key. * * Same as [[fetch]], but the contract to be fetched is identified by its contract key instead of * its contract id. * * @param template The template of the contract to be fetched. * @param key The contract key of the contract to be fetched. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. */ Ledger.prototype.fetchByKey = function (template, key) { return __awaiter(this, void 0, void 0, function () { var payload, json; return __generator(this, function (_a) { switch (_a.label) { case 0: if (key === undefined) { throw Error("Cannot lookup by key on template ".concat(template.templateId, " because it does not define a key.")); } payload = { templateId: template.templateId, key: template.keyEncode(key), }; return [4 /*yield*/, this.submit("v1/fetch", payload)]; case 1: json = _a.sent(); return [2 /*return*/, jtv.Result.withException(jtv.oneOf(jtv.constant(null), decodeCreateEvent(template)).run(json))]; } }); }); }; /** * Create a contract for a given template. * * @param template The template of the contract to be created. * @param payload The template arguments for the contract to be created. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. * */ Ledger.prototype.create = function (template, payload, meta) { return __awaiter(this, void 0, void 0, function () { var command, json; return __generator(this, function (_a) { switch (_a.label) { case 0: command = { templateId: template.templateId, payload: template.encode(payload), meta: meta, }; return [4 /*yield*/, this.submit("v1/create", command)]; case 1: json = _a.sent(); return [2 /*return*/, jtv.Result.withException(decodeCreateEvent(template).run(json))]; } }); }); }; /** * Exercise a choice on a contract identified by its contract ID. * * @param choice The choice to exercise. * @param contractId The contract id of the contract to exercise. * @param argument The choice arguments. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam C The type of the contract choice. * @typeparam R The return type of the choice. * * @returns The return value of the choice together with a list of * [[event]]'s that were created as a result of exercising the choice. */ Ledger.prototype.exercise = function (choice, contractId, argument, meta) { return __awaiter(this, void 0, void 0, function () { var payload, json, responseDecoder, _a, exerciseResult, events; return __generator(this, function (_b) { switch (_b.label) { case 0: payload = { templateId: choice.template().templateId, contractId: (0, types_1.ContractId)(choice.template()).encode(contractId), choice: choice.choiceName, argument: choice.argumentEncode(argument), meta: meta, }; return [4 /*yield*/, this.submit("v1/exercise", payload)]; case 1: json = _b.sent(); responseDecoder = jtv.object({ exerciseResult: choice.resultDecoder, events: jtv.array(decodeEventUnknown), }); _a = jtv.Result.withException(responseDecoder.run(json)), exerciseResult = _a.exerciseResult, events = _a.events; return [2 /*return*/, [exerciseResult, events]]; } }); }); }; /** * Exercse a choice on a newly-created contract, in a single transaction. * * @param choice The choice to exercise. * @param payload The template arguments for the newly-created contract. * @param argument The choice arguments. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam C The type of the contract choice. * @typeparam R The return type of the choice. * * @returns The return value of the choice together with a list of * [[event]]'s that includes the creation event for the created contract as * well as all the events that were created as a result of exercising the * choice, including the archive event for the created contract if the choice * is consuming (or otherwise archives it as part of its execution). * */ Ledger.prototype.createAndExercise = function (choice, payload, argument, meta) { return __awaiter(this, void 0, void 0, function () { var command, json, responseDecoder, _a, exerciseResult, events; return __generator(this, function (_b) { switch (_b.label) { case 0: command = { templateId: choice.template().templateId, payload: choice.template().encode(payload), choice: choice.choiceName, argument: choice.argumentEncode(argument), meta: meta, }; return [4 /*yield*/, this.submit("v1/create-and-exercise", command)]; case 1: json = _b.sent(); responseDecoder = jtv.object({ exerciseResult: choice.resultDecoder, events: jtv.array(decodeEventUnknown), }); _a = jtv.Result.withException(responseDecoder.run(json)), exerciseResult = _a.exerciseResult, events = _a.events; return [2 /*return*/, [exerciseResult, events]]; } }); }); }; /** * Exercise a choice on a contract identified by its contract key. * * Same as [[exercise]], but the contract is identified by its contract key instead of its * contract id. * * @param choice The choice to exercise. * @param key The contract key of the contract to exercise. * @param argument The choice arguments. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam C The type of the contract choice. * @typeparam R The return type of the choice. * @typeparam K The type of the contract key. * * @returns The return value of the choice together with a list of [[event]]'s that where created * as a result of exercising the choice. */ Ledger.prototype.exerciseByKey = function (choice, key, argument, meta) { return __awaiter(this, void 0, void 0, function () { var payload, json, responseDecoder, _a, exerciseResult, events; return __generator(this, function (_b) { switch (_b.label) { case 0: if (key === undefined) { throw Error("Cannot exercise by key on template ".concat(choice.template().templateId, " because it does not define a key.")); } payload = { templateId: choice.template().templateId, key: choice.template().keyEncode(key), choice: choice.choiceName, argument: choice.argumentEncode(argument), meta: meta, }; return [4 /*yield*/, this.submit("v1/exercise", payload)]; case 1: json = _b.sent(); responseDecoder = jtv.object({ exerciseResult: choice.resultDecoder, events: jtv.array(decodeEventUnknown), }); _a = jtv.Result.withException(responseDecoder.run(json)), exerciseResult = _a.exerciseResult, events = _a.events; return [2 /*return*/, [exerciseResult, events]]; } }); }); }; /** * Archive a contract identified by its contract ID. * * @param template The template of the contract to archive. * @param contractId The contract id of the contract to archive. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. * */ Ledger.prototype.archive = function (template, contractId, meta) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2 /*return*/, decodeArchiveResponse(template, "archive", function () { return _this.exercise(template.Archive, contractId, {}, meta); })]; }); }); }; /** * Archive a contract identified by its contract key. * Same as [[archive]], but the contract to be archived is identified by its contract key. * * @param template The template of the contract to be archived. * @param key The contract key of the contract to be archived. * @param meta Optional meta fields to specify additional data on a command submission. * * @typeparam T The contract template type. * @typeparam K The contract key type. * @typeparam I The contract id type. * */ Ledger.prototype.archiveByKey = function (template, key, meta) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2 /*return*/, decodeArchiveResponse(template, "archiveByKey", function () { return _this.exerciseByKey(template.Archive, key, {}, meta); })]; }); }); }; /** * @internal * * Internal command to submit a request to a streaming endpoint of the * JSON API. Returns a stream consisting of accumulated state together with * the events that produced the latest state change. The `change` function * must be an operation of the monoid `Event<T, K, I>[]` on the set `State`, * i.e., for all `s: State` and `x, y: Event<T, K, I>[]` we * must have the structural equalities * ``` * change(s, []) == s * change(s, x.concat(y)) == change(change(s, x), y) * ``` * Also, `change` must never change its arguments. */ Ledger.prototype.streamSubmit = function (callerName, template, endpoint, request, reconnectRequest, init, change) { var _this = this; var protocols = ["jwt.token." + this.token, "daml.ws.auth"]; var ws = new isomorphic_ws_1.default(this.wsBaseUrl + endpoint, protocols); var isLiveSince = undefined; var lastOffset = undefined; var state = init; var isReconnecting = false; var streamClosed = false; var emitter = new events_1.EventEmitter(); var onWsOpen = function () { if (isReconnecting) { // the JSON API server can't handle null offsets, even though it sends them out under // special conditions when there are no transactions yet. Not sending the `offset` message // will start the stream from the very beginning of the transaction log. if (lastOffset !== null) ws.send(JSON