@mfxhub/sdk 0.1.1 → 0.1.2
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.
- package/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/dist/execution.d.ts +8 -0
- package/dist/execution.js +30 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/results.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,28 @@ Every release of `@mfxhub/sdk`, newest first. The format follows
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.2
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- An order sent again with the same idempotency key, after the first had already finished, came
|
|
14
|
+
back with `ok` false and no price, tickets or broker comment, because the platform answers a
|
|
15
|
+
repeat with the command's state only. A filled order read as not filled. The SDK now reads the
|
|
16
|
+
command once and returns the original result in full, marked `duplicate`. A repeat still in
|
|
17
|
+
flight is returned as it is, with no extra read.
|
|
18
|
+
- If that read fails, `placeOrder` still resolves with what the platform said (the state, `ok`
|
|
19
|
+
true for a success, `duplicate`) with the price and tickets empty, and `detail` names the
|
|
20
|
+
`getCommand` call that reads the rest. It never rejects.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `execution.getCommand(commandId)`: one command and its result, as the platform has it now. Like
|
|
25
|
+
every execution method it resolves to an `ExecutionResult` and never rejects.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- The SDK passes the shared SDK test suite 0.3.0, all 74 cases.
|
|
30
|
+
|
|
9
31
|
## 0.1.1
|
|
10
32
|
|
|
11
33
|
No change to what the SDK does: the same API and behaviour as 0.1.0, and all 72 cases of the
|
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ logged in to.
|
|
|
62
62
|
| Signing | every request and the stream's upgrade, Ed25519 |
|
|
63
63
|
| Errors | one typed error per error code the API names |
|
|
64
64
|
| Terminals | `get`, `page`, `list`, `create` |
|
|
65
|
-
| Execution | `placeOrder`, with `ExecutionResult` for every outcome
|
|
65
|
+
| Execution | `placeOrder` and `getCommand`, with `ExecutionResult` for every outcome |
|
|
66
66
|
| Catalogue | `quote` |
|
|
67
67
|
| Stream | `client.stream()`: events, results, account, ticks, candles, book and expert channels |
|
|
68
68
|
|
package/dist/execution.d.ts
CHANGED
|
@@ -44,5 +44,13 @@ export declare class Execution {
|
|
|
44
44
|
* belongs, a missing idempotency key, a comment over 31 characters.
|
|
45
45
|
*/
|
|
46
46
|
placeOrder(terminalId: string, order: PlaceOrder): Promise<ExecutionResult>;
|
|
47
|
+
/**
|
|
48
|
+
* One command and its result, as the platform has it now. A command of another tenant is not
|
|
49
|
+
* found, never forbidden. Like every method here it resolves to a result and never rejects:
|
|
50
|
+
* a caller checking what happened to an order must not have to catch to learn it cannot tell.
|
|
51
|
+
*
|
|
52
|
+
* `idempotencyKey` is only carried into the result, so a retry can reuse it; a read sends none.
|
|
53
|
+
*/
|
|
54
|
+
getCommand(commandId: string, idempotencyKey?: string): Promise<ExecutionResult>;
|
|
47
55
|
}
|
|
48
56
|
export {};
|
package/dist/execution.js
CHANGED
|
@@ -76,8 +76,19 @@ export class Execution {
|
|
|
76
76
|
const body = orderBody(order);
|
|
77
77
|
return this.#command({ method: "POST", path: `/terminals/${id}/orders`, body, idempotencyKey: key }, key);
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* One command and its result, as the platform has it now. A command of another tenant is not
|
|
81
|
+
* found, never forbidden. Like every method here it resolves to a result and never rejects:
|
|
82
|
+
* a caller checking what happened to an order must not have to catch to learn it cannot tell.
|
|
83
|
+
*
|
|
84
|
+
* `idempotencyKey` is only carried into the result, so a retry can reuse it; a read sends none.
|
|
85
|
+
*/
|
|
86
|
+
async getCommand(commandId, idempotencyKey = "") {
|
|
87
|
+
const id = uuid("commandId", commandId);
|
|
88
|
+
return this.#command({ method: "GET", path: `/commands/${id}` }, idempotencyKey, false);
|
|
89
|
+
}
|
|
79
90
|
/** Sends one execution call, once, and turns everything it can do into a result. */
|
|
80
|
-
async #command(spec, key) {
|
|
91
|
+
async #command(spec, key, write = true) {
|
|
81
92
|
const started = performance.now();
|
|
82
93
|
const elapsed = () => performance.now() - started;
|
|
83
94
|
const what = `${spec.method} ${spec.path}`;
|
|
@@ -114,6 +125,23 @@ export class Execution {
|
|
|
114
125
|
// throwFor throws only the API's typed errors: every other answer is a refusal.
|
|
115
126
|
return concluded("refused", key, elapsed(), `${what}: ${error.message}`);
|
|
116
127
|
}
|
|
117
|
-
|
|
128
|
+
const result = fromCommand(answer.body, key, elapsed());
|
|
129
|
+
if (!(write && result.duplicate && result.final && result.commandId !== null))
|
|
130
|
+
return result;
|
|
131
|
+
// A repeat of a command that is already final is answered with its state only: read the
|
|
132
|
+
// command once, so the caller gets the original result - price, tickets, reason - and not an
|
|
133
|
+
// empty one. One extra read, only on a final repeat.
|
|
134
|
+
const original = await this.getCommand(result.commandId, key);
|
|
135
|
+
if (original.commandId !== null && original.state !== null) {
|
|
136
|
+
return { ...original, duplicate: true, clientRttMs: elapsed() };
|
|
137
|
+
}
|
|
138
|
+
// DECISION: the read failed. Report what the platform did say - the state, ok
|
|
139
|
+
// from it, duplicate - and how to read the rest. Never UNKNOWN: the outcome is known.
|
|
140
|
+
return {
|
|
141
|
+
...result,
|
|
142
|
+
clientRttMs: elapsed(),
|
|
143
|
+
detail: `the original result could not be read (${original.detail ?? "no answer"}); ` +
|
|
144
|
+
`execution.getCommand('${result.commandId}') reads it`,
|
|
145
|
+
};
|
|
118
146
|
}
|
|
119
147
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,4 +23,4 @@ export { RequestSigner, SigningKey, signingMessage } from "./signing.js";
|
|
|
23
23
|
export { KEY_HEADER, NONCE_HEADER, SIGNATURE_HEADER, TIMESTAMP_HEADER } from "./signing.js";
|
|
24
24
|
export type { components, operations, paths } from "./generated/api.js";
|
|
25
25
|
/** This package's version. */
|
|
26
|
-
export declare const VERSION = "0.1.
|
|
26
|
+
export declare const VERSION = "0.1.2";
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,4 @@ export { Terminals, isUsable } from "./terminals.js";
|
|
|
16
16
|
export { RequestSigner, SigningKey, signingMessage } from "./signing.js";
|
|
17
17
|
export { KEY_HEADER, NONCE_HEADER, SIGNATURE_HEADER, TIMESTAMP_HEADER } from "./signing.js";
|
|
18
18
|
/** This package's version. */
|
|
19
|
-
export const VERSION = "0.1.
|
|
19
|
+
export const VERSION = "0.1.2";
|
package/dist/results.js
CHANGED
|
@@ -60,10 +60,13 @@ export function fromCommand(body, idempotencyKey, clientRttMs) {
|
|
|
60
60
|
const raw = record(body);
|
|
61
61
|
const state = typeof raw.state === "string" && STATES.has(raw.state) ? raw.state : null;
|
|
62
62
|
const result = record(raw.result);
|
|
63
|
+
const hasResult = result === raw.result; // record() returns the object itself only for an object
|
|
63
64
|
const reasonText = text(result.reason);
|
|
64
65
|
const tickets = record(result.tickets);
|
|
65
66
|
return {
|
|
66
|
-
|
|
67
|
+
// The result says, once there is one. A CommandAccepted has none: a repeat of a command that
|
|
68
|
+
// already succeeded says so by its state alone.
|
|
69
|
+
ok: hasResult ? result.ok === true : state === "SUCCEEDED",
|
|
67
70
|
idempotencyKey,
|
|
68
71
|
clientRttMs,
|
|
69
72
|
commandId: text(raw.command_id),
|
package/package.json
CHANGED