@attocash/n8n-nodes-atto 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,225 +1,200 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deriveAccountFromSecret = void 0;
4
- exports.deriveAddressFromSecret = deriveAddressFromSecret;
5
- exports.createNodeClient = createNodeClient;
6
- exports.createWorkerClient = createWorkerClient;
7
- exports.clearWorkerClientCache = clearWorkerClientCache;
8
3
  exports.executeAttoOperation = executeAttoOperation;
9
- exports.createAttoTriggerSubscription = createAttoTriggerSubscription;
10
- const node_crypto_1 = require("node:crypto");
11
- const commons_core_1 = require("@attocash/commons-core");
12
- const commons_node_1 = require("@attocash/commons-node");
13
- const commons_node_remote_1 = require("@attocash/commons-node-remote");
14
- const commons_wallet_1 = require("@attocash/commons-wallet");
15
- const commons_worker_remote_1 = require("@attocash/commons-worker-remote");
4
+ exports.pollAttoEvent = pollAttoEvent;
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ const protocol_1 = require("./protocol");
16
7
  const DEFAULT_STREAM_TIMEOUT_MS = 5000;
17
8
  const DEFAULT_PUBLISH_TIMEOUT_MS = 60000;
18
- const MAX_WORKER_CLIENT_CACHE_SIZE = 32;
19
- const workerClientCache = new Map();
20
- function normalizeCredentials(credentials) {
21
- return (credentials ?? {});
9
+ const POLL_MAX_ITEMS = 100;
10
+ const POLL_TIMEOUT_MS = 2000;
11
+ const MAX_SEEN_POLL_ITEMS = 500;
12
+ function credentials(value) {
13
+ return (value ?? {});
22
14
  }
23
15
  function text(value) {
24
16
  return typeof value === 'string' ? value.trim() : '';
25
17
  }
26
18
  function optionalText(value) {
27
- const normalized = text(value);
28
- return normalized ? normalized : undefined;
19
+ const valueText = text(value);
20
+ return valueText || undefined;
29
21
  }
30
- function positiveInteger(value, fieldName) {
22
+ function nonNegativeInteger(value, fieldName) {
31
23
  const numberValue = typeof value === 'number' ? value : Number(value);
32
- if (!Number.isInteger(numberValue) || numberValue < 0) {
24
+ if (!Number.isSafeInteger(numberValue) || numberValue < 0)
33
25
  throw new Error(`${fieldName} must be a non-negative integer`);
34
- }
35
26
  return numberValue;
36
27
  }
37
- function positiveTimeout(value, fieldName) {
38
- const timeoutMs = positiveInteger(value, fieldName);
39
- if (timeoutMs < 1)
28
+ function positiveInteger(value, fieldName) {
29
+ const numberValue = nonNegativeInteger(value, fieldName);
30
+ if (numberValue < 1)
40
31
  throw new Error(`${fieldName} must be greater than zero`);
41
- return timeoutMs;
32
+ return numberValue;
42
33
  }
43
- function parseSecretType(value, fieldName) {
44
- if (value === 'mnemonic' || value === 'privateKey')
45
- return value;
46
- throw new Error(`${fieldName} must be either mnemonic or privateKey`);
47
- }
48
- function secretInput(parameters, credentials) {
49
- const source = text(parameters.secretSource || 'credentials');
50
- if (source === 'node') {
51
- const walletSecret = text(parameters.walletSecret);
52
- if (!walletSecret)
53
- throw new Error('Wallet Secret is required');
54
- return {
55
- secretType: parseSecretType(parameters.walletSecretType ?? 'mnemonic', 'Wallet Secret Type'),
56
- walletSecret,
57
- keyIndex: positiveInteger(parameters.keyIndex ?? 0, 'Key Index'),
58
- };
59
- }
60
- const walletSecret = text(credentials.walletSecret);
61
- if (!walletSecret)
62
- throw new Error('Atto credentials must include a Wallet Secret');
63
- return {
64
- secretType: parseSecretType(credentials.walletMaterialType ?? 'mnemonic', 'Credential Wallet Secret Type'),
65
- walletSecret,
66
- keyIndex: positiveInteger(credentials.keyIndex ?? 0, 'Credential Key Index'),
67
- };
34
+ function positiveHeight(value, fieldName) {
35
+ const valueText = optionalText(value);
36
+ if (!valueText)
37
+ return undefined;
38
+ if (!/^\d+$/.test(valueText) || BigInt(valueText) < 1n)
39
+ throw new Error(`${fieldName} must be a positive integer`);
40
+ return valueText;
68
41
  }
69
- async function deriveAddressFromSecret(parameters, credentials) {
70
- const input = secretInput(parameters, normalizeCredentials(credentials));
71
- const index = (0, commons_core_1.toAttoIndex)(input.keyIndex);
72
- if (input.secretType === 'mnemonic') {
73
- const mnemonic = commons_core_1.AttoMnemonic.fromPhrase(input.walletSecret);
74
- const seed = await (0, commons_core_1.toSeedAsync)(mnemonic);
75
- const privateKey = (0, commons_core_1.toPrivateKey)(seed, index);
76
- const publicKey = (0, commons_core_1.toPublicKey)(privateKey);
77
- const address = new commons_core_1.AttoAddress(commons_core_1.AttoAlgorithm.V1, publicKey);
78
- return {
79
- secretType: input.secretType,
80
- keyIndex: input.keyIndex,
81
- privateKey,
82
- publicKey,
83
- address,
84
- seed,
85
- };
86
- }
87
- const privateKey = commons_core_1.AttoPrivateKey.Companion.parse(input.walletSecret);
88
- const publicKey = (0, commons_core_1.toPublicKey)(privateKey);
89
- const address = new commons_core_1.AttoAddress(commons_core_1.AttoAlgorithm.V1, publicKey);
90
- return {
91
- secretType: input.secretType,
92
- keyIndex: input.keyIndex,
93
- privateKey,
94
- publicKey,
95
- address,
96
- };
42
+ function simplifyOutput(parameters) {
43
+ return parameters.simplify !== false;
97
44
  }
98
- exports.deriveAccountFromSecret = deriveAddressFromSecret;
99
- function requireNodeUrl(credentials) {
100
- const nodeUrl = text(credentials.nodeUrl);
101
- if (!nodeUrl)
45
+ function requireNodeUrl(value) {
46
+ const url = text(value.nodeUrl);
47
+ if (!url)
102
48
  throw new Error('Atto credentials must include a Node Base URL');
103
- return nodeUrl.replace(/\/+$/, '');
49
+ return url.replace(/\/+$/, '');
104
50
  }
105
- function requireWorkerUrl(credentials) {
106
- const workerUrl = text(credentials.workerUrl);
107
- if (!workerUrl)
51
+ function requireWorkerUrl(value) {
52
+ const url = text(value.workerUrl);
53
+ if (!url)
108
54
  throw new Error('Atto credentials must include a Worker Base URL');
109
- return workerUrl.replace(/\/+$/, '');
55
+ return url.replace(/\/+$/, '');
110
56
  }
111
- function applyHeaders(builder, credentials) {
112
- const apiKey = text(credentials.apiKey);
57
+ function requestHeaders(value, accept = 'application/json') {
58
+ const headers = { Accept: accept };
59
+ const apiKey = text(value.apiKey);
113
60
  if (!apiKey)
114
- return builder;
115
- const header = text(credentials.authHeaderName || 'Authorization');
116
- if (!header)
61
+ return headers;
62
+ const headerName = text(value.authHeaderName || 'Authorization');
63
+ if (!headerName)
117
64
  throw new Error('API Key Header is required when API Key is set');
118
- const prefix = text(credentials.authHeaderPrefix);
119
- return builder.header(header, prefix ? `${prefix} ${apiKey}` : apiKey);
120
- }
121
- function sha256(value) {
122
- return (0, node_crypto_1.createHash)('sha256').update(value).digest('hex');
123
- }
124
- function workerClientCacheKey(credentials) {
125
- const workerUrl = requireWorkerUrl(credentials);
126
- const apiKey = text(credentials.apiKey);
127
- if (!apiKey) {
128
- return JSON.stringify({
129
- workerUrl,
130
- auth: 'none',
131
- });
132
- }
133
- const header = text(credentials.authHeaderName || 'Authorization');
134
- if (!header)
135
- throw new Error('API Key Header is required when API Key is set');
136
- const prefix = text(credentials.authHeaderPrefix);
137
- const headerValue = prefix ? `${prefix} ${apiKey}` : apiKey;
138
- return JSON.stringify({
139
- workerUrl,
140
- header,
141
- headerValueHash: sha256(headerValue),
142
- });
65
+ const prefix = text(value.authHeaderPrefix);
66
+ headers[headerName] = prefix ? `${prefix} ${apiKey}` : apiKey;
67
+ return headers;
143
68
  }
144
- function createNodeClient(credentials) {
145
- const attoCredentials = normalizeCredentials(credentials);
146
- return applyHeaders(new commons_node_remote_1.AttoNodeClientAsyncBuilder(requireNodeUrl(attoCredentials)), attoCredentials).build();
147
- }
148
- function createWorkerClient(credentials) {
149
- const attoCredentials = normalizeCredentials(credentials);
150
- const cacheKey = workerClientCacheKey(attoCredentials);
151
- const cachedWorker = workerClientCache.get(cacheKey);
152
- if (cachedWorker)
153
- return cachedWorker;
154
- const worker = applyHeaders(new commons_worker_remote_1.AttoWorkerAsyncBuilder(requireWorkerUrl(attoCredentials)), attoCredentials)
155
- .cached(true)
156
- .build();
157
- if (workerClientCache.size >= MAX_WORKER_CLIENT_CACHE_SIZE) {
158
- const oldestKey = workerClientCache.keys().next().value;
159
- if (typeof oldestKey === 'string')
160
- workerClientCache.delete(oldestKey);
161
- }
162
- workerClientCache.set(cacheKey, worker);
163
- return worker;
69
+ function apiError(context, error, message) {
70
+ const cause = error && typeof error === 'object' ? error : { message: String(error) };
71
+ return new n8n_workflow_1.NodeApiError(context.getNode(), cause, { message });
164
72
  }
165
- function clearWorkerClientCache() {
166
- for (const worker of workerClientCache.values()) {
167
- try {
168
- worker.close?.();
169
- }
170
- catch {
171
- }
172
- }
173
- workerClientCache.clear();
174
- }
175
- async function createWalletRuntime(parameters, credentials) {
176
- const derived = await deriveAddressFromSecret(parameters, credentials);
177
- const node = createNodeClient(credentials);
178
- const worker = createWorkerClient(credentials);
179
- const builder = new commons_wallet_1.AttoWalletAsyncBuilder(node, worker);
180
- if (derived.seed) {
181
- builder.signerProviderSeed(derived.seed);
73
+ function responseStatus(response) {
74
+ if (!response || typeof response !== 'object')
75
+ return undefined;
76
+ const value = response;
77
+ const status = Number(value.statusCode ?? value.status);
78
+ return Number.isFinite(status) ? status : undefined;
79
+ }
80
+ function responseBody(response) {
81
+ if (!response || typeof response !== 'object' || !('body' in response))
82
+ return response;
83
+ return response.body;
84
+ }
85
+ async function requestText(context, value, baseUrl, method, path, options = {}) {
86
+ const request = {
87
+ url: `${baseUrl}/${path}`,
88
+ method,
89
+ headers: {
90
+ ...requestHeaders(value),
91
+ ...(options.body ? { 'Content-Type': 'application/json' } : {}),
92
+ },
93
+ encoding: 'text',
94
+ json: false,
95
+ timeout: options.timeoutMs ?? 10_000,
96
+ returnFullResponse: options.allowNotFound,
97
+ ignoreHttpStatusErrors: options.allowNotFound,
98
+ ...(options.body ? { body: (0, protocol_1.stringifyAttoJson)(options.body) } : {}),
99
+ };
100
+ try {
101
+ const response = await context.helpers.httpRequest(request);
102
+ if (options.allowNotFound && responseStatus(response) === 404)
103
+ return undefined;
104
+ const status = responseStatus(response);
105
+ if (status !== undefined && status >= 400)
106
+ throw new Error(`Atto API returned HTTP ${status}`);
107
+ const body = responseBody(response);
108
+ return typeof body === 'string' ? body : JSON.stringify(body);
109
+ }
110
+ catch (error) {
111
+ throw apiError(context, error, `Atto API request failed: ${method} /${path}`);
112
+ }
113
+ }
114
+ async function requestJson(context, value, baseUrl, method, path, options = {}) {
115
+ const body = await requestText(context, value, baseUrl, method, path, options);
116
+ if (body === undefined || !body.trim())
117
+ return undefined;
118
+ try {
119
+ return (0, protocol_1.parseAttoJson)(body);
182
120
  }
183
- else {
184
- builder.signerProviderFunction({
185
- get: async () => (0, commons_core_1.privateKeyToSigner)(derived.privateKey),
186
- });
121
+ catch (error) {
122
+ throw apiError(context, error, `Atto API returned invalid JSON for ${method} /${path}`);
187
123
  }
188
- const wallet = builder.build();
189
- await wallet.openAccount((0, commons_core_1.toAttoIndex)(derived.keyIndex));
190
- return { node, derived, wallet };
191
124
  }
192
- function parseAddress(value, fieldName) {
193
- const raw = text(value);
194
- if (!raw)
195
- throw new Error(`${fieldName} is required`);
125
+ function streamChunk(value) {
126
+ if (typeof value === 'string')
127
+ return value;
128
+ if (Buffer.isBuffer(value))
129
+ return value.toString('utf8');
130
+ if (value instanceof Uint8Array)
131
+ return Buffer.from(value).toString('utf8');
132
+ return String(value);
133
+ }
134
+ function isExpectedStreamEnd(error) {
135
+ if (!error || typeof error !== 'object')
136
+ return false;
137
+ const value = error;
138
+ const message = String(value.message ?? '').toLowerCase();
139
+ return (value.code === 'ECONNABORTED' ||
140
+ value.name === 'AbortError' ||
141
+ message.includes('aborted') ||
142
+ message.includes('timeout') ||
143
+ message.includes('timed out'));
144
+ }
145
+ async function requestStream(context, value, request) {
146
+ const controller = new AbortController();
147
+ const options = {
148
+ url: `${requireNodeUrl(value)}/${request.path}`,
149
+ method: request.method,
150
+ headers: {
151
+ ...requestHeaders(value, 'application/x-ndjson'),
152
+ ...(request.body ? { 'Content-Type': 'application/json' } : {}),
153
+ },
154
+ encoding: 'stream',
155
+ json: false,
156
+ timeout: request.timeoutMs,
157
+ abortSignal: controller.signal,
158
+ ...(request.body ? { body: (0, protocol_1.stringifyAttoJson)(request.body) } : {}),
159
+ };
160
+ const items = [];
161
+ let pending = '';
196
162
  try {
197
- return commons_core_1.AttoAddress.parse(raw);
163
+ const response = await context.helpers.httpRequest(options);
164
+ if (!response || typeof response !== 'object' || !(Symbol.asyncIterator in response)) {
165
+ throw new Error('Atto stream response is not readable');
166
+ }
167
+ for await (const chunk of response) {
168
+ pending += streamChunk(chunk);
169
+ const lines = pending.split('\n');
170
+ pending = lines.pop() ?? '';
171
+ for (const line of lines) {
172
+ if (!line.trim())
173
+ continue;
174
+ const parsed = (0, protocol_1.parseAttoJson)(line);
175
+ items.push(parsed);
176
+ if (items.length >= request.maxItems) {
177
+ controller.abort();
178
+ return items;
179
+ }
180
+ }
181
+ }
182
+ if (pending.trim() && items.length < request.maxItems)
183
+ items.push((0, protocol_1.parseAttoJson)(pending));
184
+ return items;
198
185
  }
199
- catch {
200
- throw new Error(`${fieldName} must be a valid Atto address`);
186
+ catch (error) {
187
+ if (controller.signal.aborted || isExpectedStreamEnd(error))
188
+ return items;
189
+ throw apiError(context, error, `Atto API stream failed: ${request.method} /${request.path}`);
201
190
  }
202
191
  }
203
- function parseOptionalAddress(value, fieldName) {
204
- const raw = optionalText(value);
205
- return raw ? parseAddress(raw, fieldName) : undefined;
206
- }
207
- function parseAddressList(value, fieldName) {
208
- const raw = typeof value === 'string'
209
- ? value
210
- : Array.isArray(value)
211
- ? value.join('\n')
212
- : '';
213
- const addresses = raw
214
- .split(/[\n,]+/)
215
- .map((address) => address.trim())
216
- .filter(Boolean)
217
- .map((address) => parseAddress(address, fieldName));
218
- if (addresses.length === 0)
219
- throw new Error(`${fieldName} is required`);
220
- return addresses;
192
+ function contextRequired(context) {
193
+ if (!context)
194
+ throw new Error('This Atto operation requires an n8n execution context');
195
+ return context;
221
196
  }
222
- function parseAddressSource(value, allowAll) {
197
+ function addressSource(value, allowAll) {
223
198
  const source = text(value || 'credentials');
224
199
  if (source === 'credentials' || source === 'manual')
225
200
  return source;
@@ -227,398 +202,341 @@ function parseAddressSource(value, allowAll) {
227
202
  return source;
228
203
  throw new Error('Address Source must be credentials, manual, or all');
229
204
  }
230
- function parseQueryMode(value) {
205
+ function queryMode(value) {
231
206
  const mode = text(value || 'credentials');
232
207
  if (mode === 'credentials' || mode === 'manual' || mode === 'all' || mode === 'hash')
233
208
  return mode;
234
209
  throw new Error('Query Mode must be credentials, manual, all, or hash');
235
210
  }
236
- async function addressesFromSource(parameters, credentials, allowAll = false) {
237
- const source = parseAddressSource(parameters.addressSource, allowAll);
238
- if (source === 'all')
239
- return undefined;
240
- if (source === 'manual')
241
- return parseAddressList(parameters.addresses ?? parameters.address, 'Address');
242
- const derived = await deriveAddressFromSecret({ secretSource: 'credentials' }, credentials);
243
- return [derived.address];
244
- }
245
- async function addressForCredentials(credentials) {
246
- const derived = await deriveAddressFromSecret({ secretSource: 'credentials' }, credentials);
247
- return derived.address;
248
- }
249
- function parseAmount(amount, unit, fieldName) {
250
- const raw = text(amount);
251
- if (!raw)
211
+ function parseAddresses(value, fieldName) {
212
+ const source = typeof value === 'string' ? value : Array.isArray(value) ? value.join('\n') : '';
213
+ const result = source
214
+ .split(/[\n,]+/)
215
+ .map((address) => address.trim())
216
+ .filter(Boolean)
217
+ .map((address) => (0, protocol_1.parseAddress)(address, fieldName));
218
+ if (result.length === 0)
252
219
  throw new Error(`${fieldName} is required`);
253
- try {
254
- const amountValue = text(unit).toUpperCase() === 'RAW'
255
- ? commons_core_1.AttoAmount.from(commons_core_1.AttoUnit.RAW, raw)
256
- : commons_core_1.AttoAmount.from(commons_core_1.AttoUnit.ATTO, raw);
257
- if (amountValue.toString() === '0') {
258
- throw new Error('zero');
259
- }
260
- return amountValue;
261
- }
262
- catch {
263
- throw new Error(`${fieldName} must be a positive Atto amount`);
264
- }
220
+ return result;
265
221
  }
266
- function parseOptionalAmount(amount, unit, fieldName) {
267
- const raw = optionalText(amount);
268
- return raw ? parseAmount(raw, unit, fieldName) : undefined;
222
+ async function derivedAddress(value) {
223
+ return await (0, protocol_1.deriveAddressFromSecret)({ secretSource: 'credentials' }, value);
269
224
  }
270
- function parseHash(value, fieldName) {
271
- const raw = text(value);
272
- if (!raw)
273
- throw new Error(`${fieldName} is required`);
274
- try {
275
- return commons_core_1.AttoHash.Companion.parse(raw);
276
- }
277
- catch {
278
- throw new Error(`${fieldName} must be a valid Atto hash`);
279
- }
280
- }
281
- function parseOptionalHeight(value, fieldName) {
282
- const raw = optionalText(value);
283
- if (!raw)
225
+ async function addressesForSource(parameters, value, allowAll = false) {
226
+ const source = addressSource(parameters.addressSource, allowAll);
227
+ if (source === 'all')
284
228
  return undefined;
285
- try {
286
- return (0, commons_core_1.toAttoHeight)(raw);
287
- }
288
- catch {
289
- throw new Error(`${fieldName} must be a valid Atto height`);
290
- }
291
- }
292
- function parseRequiredHeight(value, fieldName) {
293
- return parseOptionalHeight(value, fieldName) ?? (0, commons_core_1.toAttoHeight)('1');
294
- }
295
- function assertSameAddress(expected, actual, fieldName) {
296
- if (!expected.equals(actual)) {
297
- throw new Error(`${fieldName} must match the address derived from the wallet secret`);
298
- }
299
- }
300
- function assertOptionalSameAddress(expected, actual, fieldName) {
301
- if (actual)
302
- assertSameAddress(expected, actual, fieldName);
303
- }
304
- function parseJsonObject(value) {
305
- return JSON.parse(value);
306
- }
307
- function amountOutput(amount) {
308
- return {
309
- raw: amount.toString(),
310
- atto: amount.toFormattedString(commons_core_1.AttoUnit.ATTO),
311
- };
229
+ if (source === 'manual')
230
+ return parseAddresses(parameters.addresses ?? parameters.address, 'Address');
231
+ return [await derivedAddress(value)];
312
232
  }
313
- function accountOutput(account) {
314
- return {
315
- found: true,
316
- address: account.address.value,
317
- publicKey: account.publicKey.toString(),
318
- balance: amountOutput(account.balance),
319
- representativeAddress: account.representativeAddress.value,
320
- height: account.height.toString(),
321
- frontier: account.lastTransactionHash.toString(),
322
- account: parseJsonObject((0, commons_node_1.accountToJson)(account)),
323
- };
233
+ async function addressesForQuery(parameters, value) {
234
+ const mode = queryMode(parameters.queryMode);
235
+ if (mode === 'all' || mode === 'hash')
236
+ return undefined;
237
+ return mode === 'manual'
238
+ ? parseAddresses(parameters.addresses ?? parameters.address, 'Address')
239
+ : [await derivedAddress(value)];
324
240
  }
325
- function receivableOutput(receivable) {
241
+ function streamLimits(parameters) {
326
242
  return {
327
- hash: receivable.hash.toString(),
328
- address: receivable.receiverAddress.value,
329
- fromAddress: receivable.address.value,
330
- amount: amountOutput(receivable.amount),
331
- receivable: parseJsonObject((0, commons_node_1.receivableToJson)(receivable)),
243
+ maxItems: positiveInteger(parameters.maxItems ?? 25, 'Max Items'),
244
+ timeoutMs: positiveInteger(parameters.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS, 'Timeout'),
332
245
  };
333
246
  }
334
- function transactionOutput(transaction, status) {
335
- return {
336
- ...(status ? { status } : {}),
337
- hash: transaction.hash.toString(),
338
- address: transaction.address.value,
339
- height: transaction.height.toString(),
340
- transaction: parseJsonObject((0, commons_node_1.transactionToJson)(transaction)),
341
- };
247
+ function heightQuery(parameters) {
248
+ const fromHeight = positiveHeight(parameters.fromHeight, 'From Height') ?? '1';
249
+ const toHeight = positiveHeight(parameters.toHeight, 'To Height');
250
+ return `fromHeight=${encodeURIComponent(fromHeight)}${toHeight ? `&toHeight=${encodeURIComponent(toHeight)}` : ''}`;
342
251
  }
343
- function accountEntryOutput(accountEntry) {
252
+ function heightSearch(addresses, parameters) {
253
+ const fromHeight = positiveHeight(parameters.fromHeight, 'From Height') ?? '1';
254
+ const toHeight = positiveHeight(parameters.toHeight, 'To Height');
344
255
  return {
345
- hash: accountEntry.hash.toString(),
346
- address: accountEntry.address.value,
347
- subjectAddress: accountEntry.subjectAddress.value,
348
- height: accountEntry.height.toString(),
349
- blockType: accountEntry.blockType.name,
350
- previousBalance: amountOutput(accountEntry.previousBalance),
351
- balance: amountOutput(accountEntry.balance),
352
- accountEntry: parseJsonObject((0, commons_node_1.accountEntryToJson)(accountEntry)),
256
+ search: addresses.map((address) => ({
257
+ address: address.value,
258
+ fromHeight,
259
+ ...(toHeight ? { toHeight } : {}),
260
+ })),
353
261
  };
354
262
  }
355
- function streamOptions(parameters) {
263
+ async function receivableStreamRequest(parameters, value, maxItems, timeoutMs) {
264
+ const addresses = (await addressesForSource(parameters, value)) ?? [];
265
+ const minimum = optionalText(parameters.minAmount)
266
+ ? (0, protocol_1.parseAmount)(parameters.minAmount, parameters.minAmountUnit ?? 'RAW', 'Minimum Amount').toString()
267
+ : '0';
268
+ const limits = streamLimits(parameters);
356
269
  return {
357
- maxItems: positiveInteger(parameters.maxItems ?? 25, 'Max Items') || 1,
358
- timeoutMs: positiveTimeout(parameters.timeoutMs ?? DEFAULT_STREAM_TIMEOUT_MS, 'Timeout'),
270
+ method: 'POST',
271
+ path: `accounts/receivables/stream?minAmount=${minimum}`,
272
+ body: { addresses: addresses.map((address) => address.value) },
273
+ maxItems: maxItems ?? limits.maxItems,
274
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
359
275
  };
360
276
  }
361
- function cancelJob(job) {
362
- try {
363
- job?.cancel?.();
364
- job?.close?.();
365
- }
366
- catch {
367
- }
368
- }
369
- function errorFromUnknown(error, fallback) {
370
- if (!error)
371
- return new Error(fallback);
372
- if (error instanceof Error)
373
- return error;
374
- return new Error(String(error));
375
- }
376
- async function collectStream(start, options) {
377
- return await new Promise((resolve, reject) => {
378
- const items = [];
379
- let settled = false;
380
- const state = {};
381
- const finish = (error) => {
382
- if (settled)
383
- return;
384
- settled = true;
385
- clearTimeout(timer);
386
- cancelJob(state.job);
387
- if (error) {
388
- reject(error);
389
- return;
390
- }
391
- resolve(items);
277
+ async function transactionStreamRequest(parameters, value, maxItems, timeoutMs) {
278
+ const mode = queryMode(parameters.queryMode);
279
+ const limits = streamLimits(parameters);
280
+ if (mode === 'hash') {
281
+ return {
282
+ method: 'GET',
283
+ path: `transactions/${(0, protocol_1.parseHash)(parameters.hash, 'Hash')}/stream`,
284
+ maxItems: maxItems ?? 1,
285
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
392
286
  };
393
- const timer = setTimeout(() => finish(), options.timeoutMs);
394
- state.job = start((item) => {
395
- if (settled)
396
- return;
397
- items.push(item);
398
- if (items.length >= options.maxItems)
399
- finish();
400
- }, (error) => {
401
- if (settled)
402
- return;
403
- finish(error ? errorFromUnknown(error, 'Atto stream stopped') : undefined);
404
- });
405
- });
406
- }
407
- function inputItem(parameters) {
408
- const item = parameters.inputItem;
409
- if (!item || typeof item !== 'object' || Array.isArray(item)) {
410
- throw new Error('Input item must contain a receivable object from Atto Trigger or Get Receivables');
411
- }
412
- return item;
413
- }
414
- function parseInputReceivable(parameters) {
415
- const item = inputItem(parameters);
416
- const value = item.receivable ?? item;
417
- const json = typeof value === 'string' ? value : JSON.stringify(value);
418
- try {
419
- return (0, commons_node_1.receivableFromJson)(json);
420
287
  }
421
- catch {
422
- throw new Error('Input item must contain a valid Atto receivable object');
423
- }
424
- }
425
- async function withTimeout(promise, timeoutMs, operationName) {
426
- let timer;
427
- try {
428
- return await Promise.race([
429
- promise,
430
- new Promise((_resolve, reject) => {
431
- timer = setTimeout(() => reject(new Error(`${operationName} timed out after ${timeoutMs}ms`)), timeoutMs);
432
- }),
433
- ]);
288
+ const addresses = await addressesForQuery(parameters, value);
289
+ if (!addresses) {
290
+ return { method: 'GET', path: 'transactions/stream', maxItems: maxItems ?? limits.maxItems, timeoutMs: timeoutMs ?? limits.timeoutMs };
434
291
  }
435
- finally {
436
- if (timer)
437
- clearTimeout(timer);
292
+ if (addresses.length === 1) {
293
+ return {
294
+ method: 'GET',
295
+ path: `accounts/${addresses[0].publicKey}/transactions/stream?${heightQuery(parameters)}`,
296
+ maxItems: maxItems ?? limits.maxItems,
297
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
298
+ };
438
299
  }
300
+ return {
301
+ method: 'POST',
302
+ path: 'accounts/transactions/stream',
303
+ body: heightSearch(addresses, parameters),
304
+ maxItems: maxItems ?? limits.maxItems,
305
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
306
+ };
439
307
  }
440
- function heightSearch(addresses, parameters) {
441
- const fromHeight = parseRequiredHeight(parameters.fromHeight, 'From Height');
442
- const toHeight = parseOptionalHeight(parameters.toHeight, 'To Height');
443
- const searches = addresses.map((address) => new commons_node_1.AccountHeightSearch(address, fromHeight, toHeight));
444
- return commons_node_1.HeightSearch.Companion.fromArray(searches);
445
- }
446
- async function collectReceivables(node, parameters, credentials) {
447
- const addresses = await addressesFromSource(parameters, credentials);
448
- const minAmount = parseOptionalAmount(parameters.minAmount, parameters.minAmountUnit ?? 'RAW', 'Minimum Amount');
449
- const options = streamOptions(parameters);
450
- const receivables = await collectStream((onItem, onCancel) => node.onReceivableByAddresses(addresses, minAmount, onItem, onCancel), options);
451
- return receivables.map(receivableOutput);
452
- }
453
- async function collectTransactionsForMode(node, parameters, credentials) {
454
- const mode = parseQueryMode(parameters.queryMode);
308
+ async function accountEntryStreamRequest(parameters, value, maxItems, timeoutMs) {
309
+ const mode = queryMode(parameters.queryMode);
310
+ const limits = streamLimits(parameters);
455
311
  if (mode === 'hash') {
456
- const transaction = await node.transaction(parseHash(parameters.hash, 'Hash'));
457
- return [transactionOutput(transaction)];
312
+ return {
313
+ method: 'GET',
314
+ path: `accounts/entries/${(0, protocol_1.parseHash)(parameters.hash, 'Hash')}/stream`,
315
+ maxItems: maxItems ?? 1,
316
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
317
+ };
458
318
  }
459
- const addresses = mode === 'credentials' ? [await addressForCredentials(credentials)] : mode === 'manual' ? parseAddressList(parameters.addresses ?? parameters.address, 'Address') : undefined;
460
- const fromHeight = parseOptionalHeight(parameters.fromHeight, 'From Height');
461
- const toHeight = parseOptionalHeight(parameters.toHeight, 'To Height');
462
- const options = streamOptions(parameters);
463
- const transactions = await collectStream((onItem, onCancel) => subscribeToTransactionAddresses(node, addresses, fromHeight, toHeight, parameters, onItem, onCancel), options);
464
- return transactions.map((transaction) => transactionOutput(transaction));
465
- }
466
- async function collectAccountEntries(node, parameters, credentials) {
467
- const mode = parseQueryMode(parameters.queryMode);
468
- if (mode === 'hash') {
469
- const accountEntry = await node.accountEntry(parseHash(parameters.hash, 'Hash'));
470
- return [accountEntryOutput(accountEntry)];
319
+ const addresses = await addressesForQuery(parameters, value);
320
+ if (!addresses) {
321
+ return { method: 'GET', path: 'accounts/entries/stream', maxItems: maxItems ?? limits.maxItems, timeoutMs: timeoutMs ?? limits.timeoutMs };
471
322
  }
472
- const addresses = mode === 'credentials' ? [await addressForCredentials(credentials)] : mode === 'manual' ? parseAddressList(parameters.addresses ?? parameters.address, 'Address') : undefined;
473
- const fromHeight = parseOptionalHeight(parameters.fromHeight, 'From Height');
474
- const toHeight = parseOptionalHeight(parameters.toHeight, 'To Height');
475
- const options = streamOptions(parameters);
476
- const accountEntries = await collectStream((onItem, onCancel) => subscribeToAccountEntryAddresses(node, addresses, fromHeight, toHeight, parameters, onItem, onCancel), options);
477
- return accountEntries.map((accountEntry) => accountEntryOutput(accountEntry));
478
- }
479
- function subscribeToTransactionAddresses(node, addresses, fromHeight, toHeight, parameters, onItem, onCancel) {
480
- if (!addresses)
481
- return node.onTransactionAll(onItem, onCancel);
482
323
  if (addresses.length === 1) {
483
- return node.onTransactionByPublicKey(addresses[0].publicKey, fromHeight, toHeight, onItem, onCancel);
324
+ return {
325
+ method: 'GET',
326
+ path: `accounts/${addresses[0].publicKey}/entries/stream?${heightQuery(parameters)}`,
327
+ maxItems: maxItems ?? limits.maxItems,
328
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
329
+ };
484
330
  }
485
- return node.onTransactionByHeightSearch(heightSearch(addresses, parameters), onItem, onCancel);
331
+ return {
332
+ method: 'POST',
333
+ path: 'accounts/entries/stream',
334
+ body: heightSearch(addresses, parameters),
335
+ maxItems: maxItems ?? limits.maxItems,
336
+ timeoutMs: timeoutMs ?? limits.timeoutMs,
337
+ };
486
338
  }
487
- function subscribeToAccountEntryAddresses(node, addresses, fromHeight, toHeight, parameters, onItem, onCancel) {
488
- if (!addresses)
489
- return node.onAccountEntryAll(onItem, onCancel);
490
- if (addresses.length === 1) {
491
- return node.onAccountEntryByPublicKey(addresses[0].publicKey, fromHeight, toHeight, onItem, onCancel);
339
+ async function accountByAddress(context, value, address) {
340
+ return (await requestJson(context, value, requireNodeUrl(value), 'GET', `accounts/${address.publicKey}`, {
341
+ allowNotFound: true,
342
+ timeoutMs: 3000,
343
+ }));
344
+ }
345
+ async function nodeTimestamp(context, value) {
346
+ const localTimestamp = Date.now();
347
+ const response = (await requestJson(context, value, requireNodeUrl(value), 'GET', `instants/${encodeURIComponent(new Date(localTimestamp).toISOString())}`));
348
+ const difference = Number(response?.differenceMillis ?? 0);
349
+ if (!Number.isSafeInteger(difference))
350
+ throw new Error('Atto node returned an invalid clock difference');
351
+ return Date.now() + difference;
352
+ }
353
+ async function workForBlock(context, value, block, timeoutMs) {
354
+ const response = (await requestJson(context, value, requireWorkerUrl(value), 'POST', 'works', {
355
+ body: {
356
+ network: block.network.name,
357
+ timestamp: Number(block.timestamp.toEpochMilliseconds()),
358
+ target: (0, protocol_1.workTarget)(block),
359
+ },
360
+ timeoutMs,
361
+ }));
362
+ const work = text(response?.work);
363
+ if (!work)
364
+ throw new Error('Atto worker response is missing work');
365
+ return work;
366
+ }
367
+ async function publishBlock(context, value, block, signer, timeoutMs) {
368
+ const transaction = await (0, protocol_1.signedTransaction)(block, signer, await workForBlock(context, value, block, timeoutMs));
369
+ const accepted = await requestStream(context, value, {
370
+ method: 'POST',
371
+ path: 'transactions/stream',
372
+ body: transaction.raw,
373
+ maxItems: 1,
374
+ timeoutMs,
375
+ });
376
+ if (accepted.length === 0)
377
+ throw new Error('Atto node did not acknowledge the transaction before timeout');
378
+ if ((0, protocol_1.blockHash)((accepted[0].block ?? {})) !== transaction.model.hash.toString()) {
379
+ throw new Error('Atto node acknowledged a different transaction');
492
380
  }
493
- return node.onAccountEntryByHeightSearch(heightSearch(addresses, parameters), onItem, onCancel);
381
+ return transaction.raw;
494
382
  }
495
- async function executeAttoOperation(operation, parameters, credentials) {
496
- const attoCredentials = normalizeCredentials(credentials);
383
+ async function receivableFromInput(context, value, parameters) {
384
+ const item = parameters.inputItem;
385
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
386
+ throw new Error('Input item must contain a receivable from Atto Trigger or Get Receivables');
387
+ }
388
+ const input = item;
389
+ const nested = input.receivable;
390
+ if (nested && typeof nested === 'object' && !Array.isArray(nested))
391
+ return nested;
392
+ if (input.network && input.receiverPublicKey && input.amount)
393
+ return input;
394
+ const hash = (0, protocol_1.parseHash)(input.hash, 'Input receivable hash');
395
+ const transaction = (await requestJson(context, value, requireNodeUrl(value), 'GET', `transactions/${hash}`));
396
+ const block = transaction.block;
397
+ if (!block || block.type !== 'SEND')
398
+ throw new Error('Input receivable hash does not identify an Atto send transaction');
399
+ return {
400
+ network: block.network,
401
+ hash,
402
+ version: block.version,
403
+ algorithm: block.algorithm,
404
+ publicKey: block.publicKey,
405
+ timestamp: block.timestamp,
406
+ receiverAlgorithm: block.receiverAlgorithm,
407
+ receiverPublicKey: block.receiverPublicKey,
408
+ amount: block.amount,
409
+ };
410
+ }
411
+ async function executeAttoOperation(context, operation, parameters, credentialData) {
412
+ const value = credentials(credentialData);
413
+ const simplify = simplifyOutput(parameters);
497
414
  if (operation === 'deriveAddress' || operation === 'deriveAccount') {
498
- const derived = await deriveAddressFromSecret(parameters, credentials);
499
- return {
500
- address: derived.address.value,
501
- publicKey: derived.publicKey.toString(),
502
- keyIndex: derived.keyIndex,
503
- secretType: derived.secretType,
504
- };
415
+ const derived = await (0, protocol_1.deriveAddressFromSecret)(parameters, value);
416
+ return { address: derived.value, publicKey: derived.publicKey, keyIndex: derived.keyIndex, secretType: derived.secretType };
505
417
  }
418
+ const execution = contextRequired(context);
506
419
  if (operation === 'getAccount') {
507
- const node = createNodeClient(attoCredentials);
508
- const address = parseAddress(parameters.address ?? parameters.lookupAddress, 'Address');
509
- const account = await node.accountByPublicKey(address.publicKey);
510
- if (!account) {
511
- return {
512
- found: false,
513
- address: address.value,
514
- };
515
- }
516
- return accountOutput(account);
420
+ const address = (0, protocol_1.parseAddress)(parameters.address ?? parameters.lookupAddress, 'Address');
421
+ const account = await accountByAddress(execution, value, address);
422
+ return account ? (0, protocol_1.accountOutput)(account, simplify) : { found: false, address: address.value };
517
423
  }
518
424
  if (operation === 'getReceivables') {
519
- const node = createNodeClient(attoCredentials);
520
- return await collectReceivables(node, parameters, attoCredentials);
425
+ return (await requestStream(execution, value, await receivableStreamRequest(parameters, value))).map((item) => (0, protocol_1.receivableOutput)(item, simplify));
521
426
  }
522
427
  if (operation === 'getTransactions') {
523
- const node = createNodeClient(attoCredentials);
524
- return await collectTransactionsForMode(node, parameters, attoCredentials);
428
+ if (queryMode(parameters.queryMode) === 'hash') {
429
+ const hash = (0, protocol_1.parseHash)(parameters.hash, 'Hash');
430
+ const transaction = (await requestJson(execution, value, requireNodeUrl(value), 'GET', `transactions/${hash}`));
431
+ return [(0, protocol_1.transactionOutput)(transaction, simplify)];
432
+ }
433
+ return (await requestStream(execution, value, await transactionStreamRequest(parameters, value))).map((item) => (0, protocol_1.transactionOutput)(item, simplify));
525
434
  }
526
435
  if (operation === 'getAccountEntries') {
527
- const node = createNodeClient(attoCredentials);
528
- return await collectAccountEntries(node, parameters, attoCredentials);
436
+ return (await requestStream(execution, value, await accountEntryStreamRequest(parameters, value))).map((item) => (0, protocol_1.accountEntryOutput)(item, simplify));
529
437
  }
438
+ const derived = await (0, protocol_1.deriveAddressFromSecret)(parameters, value);
439
+ const account = await accountByAddress(execution, value, derived);
440
+ const timestamp = await nodeTimestamp(execution, value);
441
+ const timeoutMs = positiveInteger(parameters.timeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS, 'Timeout');
530
442
  if (operation === 'sendTransaction') {
531
- const destinationAddress = parseAddress(parameters.destinationAddress, 'Destination Address');
532
- const amount = parseAmount(parameters.amount, parameters.amountUnit, 'Amount');
533
- const timeoutMs = positiveTimeout(parameters.timeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS, 'Timeout');
534
- const runtime = await createWalletRuntime(parameters, attoCredentials);
535
- assertOptionalSameAddress(runtime.derived.address, parseOptionalAddress(parameters.fromAddress, 'From Address'), 'From Address');
536
- const transaction = await withTimeout(Promise.resolve(runtime.wallet.sendByAddress(runtime.derived.address, destinationAddress, amount, null)), timeoutMs, 'Send transaction');
443
+ if (!account)
444
+ throw new Error('The wallet account is not open yet');
445
+ const destination = (0, protocol_1.parseAddress)(parameters.destinationAddress, 'Destination Address');
446
+ const amount = (0, protocol_1.parseAmount)(parameters.amount, parameters.amountUnit, 'Amount');
447
+ const block = (0, protocol_1.createSendBlock)(account, destination, amount, timestamp);
448
+ const transaction = await publishBlock(execution, value, block, derived.signer, timeoutMs);
537
449
  return {
538
- ...transactionOutput(transaction, 'published'),
539
- fromAddress: runtime.derived.address.value,
540
- destinationAddress: destinationAddress.value,
541
- amount: amountOutput(amount),
450
+ ...(0, protocol_1.transactionOutput)(transaction, true, 'published'),
451
+ fromAddress: derived.value,
452
+ destinationAddress: destination.value,
453
+ amount: (0, protocol_1.amountOutput)(amount),
542
454
  };
543
455
  }
544
456
  if (operation === 'receivePending') {
545
- const timeoutMs = positiveTimeout(parameters.timeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS, 'Timeout');
546
- const receivable = parseInputReceivable(parameters);
547
- const requestedRepresentative = parseOptionalAddress(parameters.receiveRepresentativeAddress ?? parameters.representativeAddress, 'Representative Address');
548
- const runtime = await createWalletRuntime(parameters, attoCredentials);
549
- assertOptionalSameAddress(runtime.derived.address, parseOptionalAddress(parameters.receiveAddress, 'Address'), 'Address');
550
- assertSameAddress(runtime.derived.address, receivable.receiverAddress, 'Receivable Address');
551
- const representative = requestedRepresentative ?? runtime.derived.address;
552
- const transaction = await withTimeout(Promise.resolve(runtime.wallet.receive(receivable, representative, null)), timeoutMs, 'Receive transaction');
457
+ const receivable = await receivableFromInput(execution, value, parameters);
458
+ const receiver = (0, protocol_1.addressFromPublicKey)(text(receivable.receiverPublicKey));
459
+ if (receiver.value !== derived.value)
460
+ throw new Error('Receivable Address must match the address derived from the wallet secret');
461
+ const representative = optionalText(parameters.representativeAddress)
462
+ ? (0, protocol_1.parseAddress)(parameters.representativeAddress, 'Representative Address')
463
+ : derived;
464
+ const block = (0, protocol_1.createReceiveBlock)(account, receivable, representative, timestamp);
465
+ const transaction = await publishBlock(execution, value, block, derived.signer, timeoutMs);
553
466
  return {
554
- ...transactionOutput(transaction, 'received'),
555
- address: runtime.derived.address.value,
467
+ ...(0, protocol_1.transactionOutput)(transaction, true, 'received'),
468
+ address: derived.value,
556
469
  representativeAddress: representative.value,
557
- amount: amountOutput(receivable.amount),
558
- receivable: parseJsonObject((0, commons_node_1.receivableToJson)(receivable)),
470
+ amount: (0, protocol_1.amountOutput)(receivable.amount),
559
471
  };
560
472
  }
561
473
  if (operation === 'changeRepresentative') {
562
- const representativeAddress = parseAddress(parameters.representativeAddress, 'Representative Address');
563
- const runtime = await createWalletRuntime(parameters, attoCredentials);
564
- assertOptionalSameAddress(runtime.derived.address, parseOptionalAddress(parameters.changeAddress, 'Address'), 'Address');
565
- const transaction = await runtime.wallet.change((0, commons_core_1.toAttoIndex)(runtime.derived.keyIndex), representativeAddress, null);
474
+ if (!account)
475
+ throw new Error('The wallet account is not open yet');
476
+ const representative = (0, protocol_1.parseAddress)(parameters.representativeAddress, 'Representative Address');
477
+ const block = (0, protocol_1.createChangeBlock)(account, representative, timestamp);
478
+ const transaction = await publishBlock(execution, value, block, derived.signer, timeoutMs);
566
479
  return {
567
- ...transactionOutput(transaction, 'representative_changed'),
568
- address: runtime.derived.address.value,
569
- representativeAddress: representativeAddress.value,
480
+ ...(0, protocol_1.transactionOutput)(transaction, true, 'representative_changed'),
481
+ address: derived.value,
482
+ representativeAddress: representative.value,
570
483
  };
571
484
  }
572
485
  throw new Error(`Unsupported Atto operation: ${operation}`);
573
486
  }
574
- async function createAttoTriggerSubscription(event, parameters, credentials, emit, emitError) {
575
- const attoCredentials = normalizeCredentials(credentials);
576
- const node = createNodeClient(attoCredentials);
577
- let closing = false;
578
- const onCancel = (error) => {
579
- if (closing || !error)
580
- return;
581
- emitError(errorFromUnknown(error, 'Atto trigger stream stopped'));
582
- };
583
- const closeJob = (job) => {
584
- closing = true;
585
- cancelJob(job);
586
- };
487
+ async function pollAccounts(context, parameters, value) {
488
+ const addresses = await addressesForSource(parameters, value, true);
489
+ if (addresses?.length === 1) {
490
+ const account = await accountByAddress(context, value, addresses[0]);
491
+ return account ? [(0, protocol_1.accountOutput)(account, true)] : [];
492
+ }
493
+ return (await requestStream(context, value, {
494
+ method: addresses ? 'POST' : 'GET',
495
+ path: 'accounts/stream',
496
+ ...(addresses ? { body: { addresses: addresses.map((address) => address.value) } } : {}),
497
+ maxItems: POLL_MAX_ITEMS,
498
+ timeoutMs: POLL_TIMEOUT_MS,
499
+ })).map((item) => (0, protocol_1.accountOutput)(item, true));
500
+ }
501
+ function pollItemKey(event, item) {
502
+ if (event === 'account')
503
+ return `${String(item.address)}:${String(item.frontier)}:${String(item.balance?.raw ?? '')}`;
504
+ return String(item.hash ?? JSON.stringify(item));
505
+ }
506
+ function newPollItems(context, event, items) {
507
+ const staticData = context.getWorkflowStaticData('node');
508
+ const stateKey = `seen_${event}`;
509
+ const existing = Array.isArray(staticData[stateKey]) ? staticData[stateKey].map(String) : [];
510
+ const seen = new Set(existing);
511
+ const fresh = items.filter((item) => !seen.has(pollItemKey(event, item)));
512
+ const updated = [...existing, ...items.map((item) => pollItemKey(event, item))].slice(-MAX_SEEN_POLL_ITEMS);
513
+ staticData[stateKey] = updated;
514
+ if (existing.length === 0 && context.getMode() !== 'manual')
515
+ return [];
516
+ return fresh;
517
+ }
518
+ async function pollAttoEvent(context, event, parameters, credentialData) {
519
+ const value = credentials(credentialData);
520
+ let items;
587
521
  if (event === 'receivable') {
588
- const addresses = await addressesFromSource(parameters, attoCredentials);
589
- const minAmount = parseOptionalAmount(parameters.minAmount, parameters.minAmountUnit ?? 'RAW', 'Minimum Amount');
590
- const job = node.onReceivableByAddresses(addresses, minAmount, (receivable) => emit(receivableOutput(receivable)), onCancel);
591
- return { close: () => closeJob(job) };
522
+ items = (await requestStream(context, value, await receivableStreamRequest(parameters, value, POLL_MAX_ITEMS, POLL_TIMEOUT_MS))).map((item) => (0, protocol_1.receivableOutput)(item, true));
592
523
  }
593
- if (event === 'account') {
594
- const addresses = await addressesFromSource(parameters, attoCredentials, true);
595
- const job = addresses
596
- ? node.onAccountByAddresses(addresses, (account) => emit(accountOutput(account)), onCancel)
597
- : node.onAccountAll((account) => emit(accountOutput(account)), onCancel);
598
- return { close: () => closeJob(job) };
524
+ else if (event === 'account') {
525
+ items = await pollAccounts(context, parameters, value);
599
526
  }
600
- if (event === 'transaction') {
601
- const mode = parseQueryMode(parameters.queryMode);
602
- const hash = mode === 'hash' ? parseHash(parameters.hash, 'Hash') : undefined;
603
- const addresses = mode === 'credentials' ? [await addressForCredentials(attoCredentials)] : mode === 'manual' ? parseAddressList(parameters.addresses ?? parameters.address, 'Address') : undefined;
604
- const fromHeight = parseOptionalHeight(parameters.fromHeight, 'From Height');
605
- const toHeight = parseOptionalHeight(parameters.toHeight, 'To Height');
606
- const job = hash
607
- ? node.onTransactionByHash(hash, (transaction) => emit(transactionOutput(transaction)), onCancel)
608
- : subscribeToTransactionAddresses(node, addresses, fromHeight, toHeight, parameters, (transaction) => emit(transactionOutput(transaction)), onCancel);
609
- return { close: () => closeJob(job) };
527
+ else if (event === 'transaction') {
528
+ const mode = queryMode(parameters.queryMode);
529
+ if (mode === 'hash') {
530
+ const transaction = (await requestJson(context, value, requireNodeUrl(value), 'GET', `transactions/${(0, protocol_1.parseHash)(parameters.hash, 'Hash')}`));
531
+ items = [(0, protocol_1.transactionOutput)(transaction, true)];
532
+ }
533
+ else {
534
+ items = (await requestStream(context, value, await transactionStreamRequest(parameters, value, POLL_MAX_ITEMS, POLL_TIMEOUT_MS))).map((item) => (0, protocol_1.transactionOutput)(item, true));
535
+ }
610
536
  }
611
- if (event === 'accountEntry') {
612
- const mode = parseQueryMode(parameters.queryMode);
613
- const hash = mode === 'hash' ? parseHash(parameters.hash, 'Hash') : undefined;
614
- const addresses = mode === 'credentials' ? [await addressForCredentials(attoCredentials)] : mode === 'manual' ? parseAddressList(parameters.addresses ?? parameters.address, 'Address') : undefined;
615
- const fromHeight = parseOptionalHeight(parameters.fromHeight, 'From Height');
616
- const toHeight = parseOptionalHeight(parameters.toHeight, 'To Height');
617
- const job = hash
618
- ? node.onAccountEntryByHash(hash, (accountEntry) => emit(accountEntryOutput(accountEntry)), onCancel)
619
- : subscribeToAccountEntryAddresses(node, addresses, fromHeight, toHeight, parameters, (accountEntry) => emit(accountEntryOutput(accountEntry)), onCancel);
620
- return { close: () => closeJob(job) };
537
+ else {
538
+ items = (await requestStream(context, value, await accountEntryStreamRequest(parameters, value, POLL_MAX_ITEMS, POLL_TIMEOUT_MS))).map((item) => (0, protocol_1.accountEntryOutput)(item, true));
621
539
  }
622
- throw new Error(`Unsupported Atto trigger event: ${event}`);
540
+ return newPollItems(context, event, items);
623
541
  }
624
542
  //# sourceMappingURL=operations.js.map