@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
JavaScript
"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