gloomberb 0.12.0 → 0.13.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/README.md +2 -0
- package/package.json +1 -2
- package/src/api-client/auth.ts +2 -1
- package/src/api-client/index.test.ts +34 -0
- package/src/api-client/index.ts +10 -2
- package/src/api-client/request.ts +4 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +2 -1
- package/src/cli/commands/plugins.ts +4 -3
- package/src/cli/desktop-pane-shot.ts +79 -10
- package/src/cli/pane-functions/capabilities.ts +76 -23
- package/src/cli/pane-functions/cloud-session.test.ts +54 -0
- package/src/cli/pane-functions/cloud-session.ts +38 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/index.test.ts +13 -2
- package/src/cli/pane-functions/index.ts +9 -5
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +68 -7
- package/src/cli/pane-functions/screenshot.ts +195 -42
- package/src/cli/restore-plugins.ts +10 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -1
- package/src/components/chart/composite/composite-chart.tsx +87 -21
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +92 -16
- package/src/components/chart/composite/text-renderer.ts +13 -0
- package/src/components/chart/composite/time-scale.ts +30 -10
- package/src/components/chart/composite/types.ts +26 -0
- package/src/components/command-bar/assist/runtime.ts +38 -30
- package/src/components/command-bar/surface/assist.test.tsx +63 -9
- package/src/components/command-bar/surface/test-harness.tsx +23 -4
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/data/config/store/index.test.ts +23 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +4 -0
- package/src/news/types.ts +1 -0
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chat/controller/channels.ts +2 -1
- package/src/plugins/builtin/chat/controller/index.ts +13 -5
- package/src/plugins/builtin/chat/controller/realtime.ts +24 -0
- package/src/plugins/builtin/chat/controller/session-runtime.ts +76 -8
- package/src/plugins/builtin/chat/controller/state.ts +1 -0
- package/src/plugins/builtin/chat/controller.test.ts +260 -4
- package/src/plugins/builtin/cloud/askg/executor.test.ts +160 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +429 -0
- package/src/plugins/builtin/cloud/askg/manifest.test.ts +153 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +314 -0
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +190 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +286 -0
- package/src/plugins/builtin/cloud/askg/undo.test.ts +44 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +277 -0
- package/src/plugins/builtin/cloud/askg/verify.ts +130 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +7 -1
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/congress-trades/client.ts +11 -0
- package/src/plugins/builtin/congress-trades/detail.tsx +3 -3
- package/src/plugins/builtin/congress-trades/headless.test.ts +98 -0
- package/src/plugins/builtin/congress-trades/headless.ts +145 -0
- package/src/plugins/builtin/congress-trades/pane.tsx +3 -3
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +4 -0
- package/src/plugins/builtin/dividend-yield/headless.test.ts +88 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +135 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +12 -8
- package/src/plugins/builtin/dividend-yield/model.ts +4 -22
- package/src/plugins/builtin/dividend-yield/view.ts +17 -0
- package/src/plugins/builtin/earnings/headless.test.ts +82 -0
- package/src/plugins/builtin/earnings/headless.ts +162 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/data.ts +29 -8
- package/src/plugins/builtin/earnings-calls/headless.test.ts +192 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +300 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +19 -9
- package/src/plugins/builtin/earnings-calls/model.ts +238 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +4 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +13 -11
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/holders/client.ts +29 -0
- package/src/plugins/builtin/holders/headless.test.ts +58 -0
- package/src/plugins/builtin/holders/headless.ts +162 -0
- package/src/plugins/builtin/holders/index.tsx +14 -8
- package/src/plugins/builtin/holders/pane.tsx +7 -1
- package/src/plugins/builtin/insider/client.ts +50 -0
- package/src/plugins/builtin/insider/headless.test.ts +122 -0
- package/src/plugins/builtin/insider/headless.ts +117 -0
- package/src/plugins/builtin/insider/index.tsx +21 -50
- package/src/plugins/builtin/insider/insider-data.ts +1 -1
- package/src/plugins/builtin/insider/model.ts +72 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +15 -0
- package/src/plugins/builtin/ipo-calendar/headless.test.ts +89 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +127 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +2 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +3 -15
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/news/headless.test.ts +138 -0
- package/src/plugins/builtin/news/headless.ts +190 -0
- package/src/plugins/builtin/news/index.tsx +13 -9
- package/src/plugins/builtin/news/wire/index.tsx +2 -1
- package/src/plugins/builtin/research/analyst-headless.test.ts +60 -0
- package/src/plugins/builtin/research/analyst-headless.ts +172 -0
- package/src/plugins/builtin/research/analyst-model.ts +219 -0
- package/src/plugins/builtin/research/analyst-pane.tsx +31 -221
- package/src/plugins/builtin/research/client.ts +12 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/headless.test.ts +93 -0
- package/src/plugins/builtin/research/headless.ts +161 -0
- package/src/plugins/builtin/research/index.tsx +32 -18
- package/src/plugins/builtin/research-search/pane.tsx +4 -0
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/client.ts +16 -0
- package/src/plugins/builtin/sec/headless.test.ts +72 -0
- package/src/plugins/builtin/sec/headless.ts +69 -0
- package/src/plugins/builtin/sec/index.tsx +20 -75
- package/src/plugins/builtin/sec/model.ts +89 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/short-interest/client.ts +5 -2
- package/src/plugins/builtin/short-interest/headless.test.ts +96 -0
- package/src/plugins/builtin/short-interest/headless.ts +111 -0
- package/src/plugins/builtin/short-interest/index.tsx +15 -9
- package/src/plugins/builtin/thirteenf/data.ts +13 -11
- package/src/plugins/builtin/thirteenf/headless.test.ts +112 -0
- package/src/plugins/builtin/thirteenf/headless.ts +286 -0
- package/src/plugins/builtin/thirteenf/index.tsx +4 -0
- package/src/plugins/builtin/thirteenf/model.ts +13 -0
- package/src/plugins/builtin/thirteenf/table.ts +3 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +4 -0
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +6 -8
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +58 -1
- package/src/plugins/bundle.ts +4 -2
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +0 -13
- package/src/plugins/catalog-browser.ts +4 -0
- package/src/plugins/catalog-manifest.test.ts +15 -0
- package/src/plugins/catalog-manifest.ts +12 -5
- package/src/plugins/catalog-ui.ts +0 -6
- package/src/plugins/host-contract.ts +9 -0
- package/src/plugins/host-modules.ts +12 -0
- package/src/plugins/loader.ts +63 -15
- package/src/plugins/prediction-markets/headless.test.ts +119 -0
- package/src/plugins/prediction-markets/headless.ts +255 -0
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/seed.ts +3 -0
- package/src/public/broker.ts +6 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +322 -39
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +3 -1
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +20 -4
- package/src/renderers/electrobun/view/build-assets.ts +0 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +63 -10
- package/src/sources/gloomberb-cloud/news.ts +1 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +14 -16
- package/src/types/config.ts +2 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { Buffer } from "node:buffer";
|
|
2
|
-
import { lookup } from "node:dns/promises";
|
|
3
|
-
import { isIP } from "node:net";
|
|
4
|
-
import { PRESERVED_PASSWORD_HINT } from "../../brokers/profile-form";
|
|
5
|
-
import type { BrokerAdapter } from "../../types/broker";
|
|
6
|
-
import type { BrokerInstanceConfig } from "../../types/config";
|
|
7
|
-
import type { GloomPlugin } from "../../types/plugin";
|
|
8
|
-
import { normalizeSimpleFinSnapshot, type BrokerPortfolioSnapshot } from "./normalize";
|
|
9
|
-
|
|
10
|
-
type FetchLike = typeof fetch;
|
|
11
|
-
|
|
12
|
-
const pendingAccessUrls = new Map<string, string>();
|
|
13
|
-
|
|
14
|
-
function isPrivateAddress(address: string): boolean {
|
|
15
|
-
const normalized = address.toLowerCase();
|
|
16
|
-
if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") return true;
|
|
17
|
-
if (/^(?:fc|fd|fe[89ab])/.test(normalized)) return true;
|
|
18
|
-
const parts = normalized.split(".").map(Number);
|
|
19
|
-
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part))) return false;
|
|
20
|
-
return parts[0] === 0
|
|
21
|
-
|| parts[0] === 10
|
|
22
|
-
|| parts[0] === 127
|
|
23
|
-
|| (parts[0] === 169 && parts[1] === 254)
|
|
24
|
-
|| (parts[0] === 172 && parts[1]! >= 16 && parts[1]! <= 31)
|
|
25
|
-
|| (parts[0] === 192 && parts[1] === 168)
|
|
26
|
-
|| (parts[0] === 100 && parts[1]! >= 64 && parts[1]! <= 127)
|
|
27
|
-
|| parts[0]! >= 224;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function decodeSimpleFinSetupToken(value: string): string {
|
|
31
|
-
const token = value.replace(/\s+/g, "");
|
|
32
|
-
if (!token || token.length > 12_000 || !/^[A-Za-z0-9+/_=-]+$/.test(token)) {
|
|
33
|
-
throw new Error("The SimpleFIN setup token is not valid.");
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
const decoded = Buffer.from(token.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8");
|
|
37
|
-
if (!decoded.startsWith("https://")) throw new Error("Invalid protocol");
|
|
38
|
-
return decoded;
|
|
39
|
-
} catch {
|
|
40
|
-
throw new Error("The SimpleFIN setup token is not valid.");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function validateSimpleFinUrl(value: string, requireCredentials: boolean): Promise<URL> {
|
|
45
|
-
let url: URL;
|
|
46
|
-
try {
|
|
47
|
-
url = new URL(value);
|
|
48
|
-
} catch {
|
|
49
|
-
throw new Error("SimpleFIN returned an invalid URL.");
|
|
50
|
-
}
|
|
51
|
-
if (url.protocol !== "https:" || (url.port && url.port !== "443") || url.hash) {
|
|
52
|
-
throw new Error("SimpleFIN returned an unsafe URL.");
|
|
53
|
-
}
|
|
54
|
-
if (requireCredentials && (!url.username || !url.password)) {
|
|
55
|
-
throw new Error("SimpleFIN returned an access URL without credentials.");
|
|
56
|
-
}
|
|
57
|
-
if (isIP(url.hostname) && isPrivateAddress(url.hostname)) {
|
|
58
|
-
throw new Error("SimpleFIN returned a private network URL.");
|
|
59
|
-
}
|
|
60
|
-
let addresses: Array<{ address: string; family: number }>;
|
|
61
|
-
try {
|
|
62
|
-
addresses = await lookup(url.hostname, { all: true, verbatim: true });
|
|
63
|
-
} catch {
|
|
64
|
-
throw new Error("The SimpleFIN server address is not valid.");
|
|
65
|
-
}
|
|
66
|
-
if (!addresses.length || addresses.some(({ address }) => isPrivateAddress(address))) {
|
|
67
|
-
throw new Error("SimpleFIN returned a private network URL.");
|
|
68
|
-
}
|
|
69
|
-
return url;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function timedFetch(fetchImpl: FetchLike, url: URL, init: RequestInit, timeoutMs: number): Promise<Response> {
|
|
73
|
-
const controller = new AbortController();
|
|
74
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
75
|
-
try {
|
|
76
|
-
return await fetchImpl(url, { ...init, redirect: "error", signal: controller.signal });
|
|
77
|
-
} catch (error) {
|
|
78
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
79
|
-
throw new Error("SimpleFIN took too long to respond.");
|
|
80
|
-
}
|
|
81
|
-
throw new Error(`Gloomberb could not reach SimpleFIN. ${error instanceof Error ? error.message : ""}`.trim());
|
|
82
|
-
} finally {
|
|
83
|
-
clearTimeout(timer);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function claimAccessUrl(setupToken: string, fetchImpl: FetchLike): Promise<string> {
|
|
88
|
-
const claimUrl = await validateSimpleFinUrl(decodeSimpleFinSetupToken(setupToken), false);
|
|
89
|
-
const response = await timedFetch(fetchImpl, claimUrl, {
|
|
90
|
-
method: "POST",
|
|
91
|
-
headers: { "Content-Length": "0" },
|
|
92
|
-
}, 20_000);
|
|
93
|
-
if (response.status === 403) {
|
|
94
|
-
throw new Error("The SimpleFIN setup token was already used or is no longer valid.");
|
|
95
|
-
}
|
|
96
|
-
if (!response.ok) throw new Error(`SimpleFIN could not claim the setup token. Status: ${response.status}.`);
|
|
97
|
-
const accessUrl = (await response.text()).trim();
|
|
98
|
-
if (accessUrl.length > 8_000) throw new Error("SimpleFIN returned an invalid access URL.");
|
|
99
|
-
await validateSimpleFinUrl(accessUrl, true);
|
|
100
|
-
return accessUrl;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function loadAccounts(accessUrl: string, fetchImpl: FetchLike): Promise<unknown> {
|
|
104
|
-
const url = await validateSimpleFinUrl(accessUrl, true);
|
|
105
|
-
const username = decodeURIComponent(url.username);
|
|
106
|
-
const password = decodeURIComponent(url.password);
|
|
107
|
-
url.username = "";
|
|
108
|
-
url.password = "";
|
|
109
|
-
url.pathname = `${url.pathname.replace(/\/$/, "")}/accounts`;
|
|
110
|
-
url.searchParams.set("balances-only", "1");
|
|
111
|
-
url.searchParams.set("version", "2");
|
|
112
|
-
const response = await timedFetch(fetchImpl, url, {
|
|
113
|
-
headers: {
|
|
114
|
-
Accept: "application/json",
|
|
115
|
-
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`,
|
|
116
|
-
},
|
|
117
|
-
}, 30_000);
|
|
118
|
-
if (response.status === 402) throw new Error("SimpleFIN Bridge needs attention before this profile can sync.");
|
|
119
|
-
if (response.status === 403) throw new Error("The SimpleFIN connection was revoked.");
|
|
120
|
-
if (!response.ok) throw new Error(`SimpleFIN could not load accounts. Status: ${response.status}.`);
|
|
121
|
-
try {
|
|
122
|
-
return await response.json();
|
|
123
|
-
} catch {
|
|
124
|
-
throw new Error("SimpleFIN returned an invalid accounts response.");
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function loadSimpleFinPortfolio(
|
|
129
|
-
instance: BrokerInstanceConfig,
|
|
130
|
-
fetchImpl: FetchLike = fetch,
|
|
131
|
-
): Promise<BrokerPortfolioSnapshot> {
|
|
132
|
-
let accessUrl = pendingAccessUrls.get(instance.id)
|
|
133
|
-
?? (typeof instance.config.accessUrl === "string" ? instance.config.accessUrl : "");
|
|
134
|
-
if (!accessUrl) {
|
|
135
|
-
const setupToken = typeof instance.config.setupToken === "string" ? instance.config.setupToken : "";
|
|
136
|
-
accessUrl = await claimAccessUrl(setupToken, fetchImpl);
|
|
137
|
-
pendingAccessUrls.set(instance.id, accessUrl);
|
|
138
|
-
}
|
|
139
|
-
return normalizeSimpleFinSnapshot(await loadAccounts(accessUrl, fetchImpl));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export const simpleFinBroker: BrokerAdapter = {
|
|
143
|
-
id: "simplefin",
|
|
144
|
-
name: "SimpleFIN",
|
|
145
|
-
configSchema: [{
|
|
146
|
-
key: "setupToken",
|
|
147
|
-
label: "Setup Token",
|
|
148
|
-
type: "password",
|
|
149
|
-
required: true,
|
|
150
|
-
placeholder: "One-time token from SimpleFIN Bridge",
|
|
151
|
-
}],
|
|
152
|
-
|
|
153
|
-
async validate(instance) {
|
|
154
|
-
return [instance.config.setupToken, instance.config.accessUrl]
|
|
155
|
-
.some((value) => typeof value === "string" && value.trim().length > 0);
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
async importPositions(instance) {
|
|
159
|
-
return (await loadSimpleFinPortfolio(instance)).positions;
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
async importPortfolioSnapshot(instance) {
|
|
163
|
-
return loadSimpleFinPortfolio(instance);
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
async listAccounts(instance) {
|
|
167
|
-
return (await loadSimpleFinPortfolio(instance)).accounts;
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
getPersistedConfigUpdate(instance) {
|
|
171
|
-
const accessUrl = pendingAccessUrls.get(instance.id);
|
|
172
|
-
if (!accessUrl) return null;
|
|
173
|
-
pendingAccessUrls.delete(instance.id);
|
|
174
|
-
return { setupToken: "", accessUrl };
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
toConfigValues(instance) {
|
|
178
|
-
return {
|
|
179
|
-
setupToken: typeof instance.config.accessUrl === "string"
|
|
180
|
-
? PRESERVED_PASSWORD_HINT
|
|
181
|
-
: instance.config.setupToken,
|
|
182
|
-
};
|
|
183
|
-
},
|
|
184
|
-
|
|
185
|
-
fromConfigValues(values, previous) {
|
|
186
|
-
const setupToken = typeof values.setupToken === "string" ? values.setupToken.trim() : "";
|
|
187
|
-
const accessUrl = previous && typeof previous.config.accessUrl === "string" ? previous.config.accessUrl : "";
|
|
188
|
-
if (setupToken === PRESERVED_PASSWORD_HINT && accessUrl) return { setupToken: "", accessUrl };
|
|
189
|
-
if (setupToken) return { setupToken };
|
|
190
|
-
return accessUrl ? { setupToken: "", accessUrl } : { setupToken: "" };
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
export const simpleFinPlugin: GloomPlugin = {
|
|
195
|
-
id: "simplefin",
|
|
196
|
-
name: "SimpleFIN",
|
|
197
|
-
version: "1.0.0",
|
|
198
|
-
description: "Read-only investment position sync through SimpleFIN Bridge.",
|
|
199
|
-
toggleable: true,
|
|
200
|
-
broker: simpleFinBroker,
|
|
201
|
-
};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { PRESERVED_PASSWORD_HINT } from "../../brokers/profile-form";
|
|
2
|
-
import type { BrokerAdapter } from "../../types/broker";
|
|
3
|
-
import type { BrokerInstanceConfig } from "../../types/config";
|
|
4
|
-
import type { GloomPlugin } from "../../types/plugin";
|
|
5
|
-
import type { BrokerPortfolioSnapshot } from "./normalize";
|
|
6
|
-
import { loadSimpleFinNativeModule } from "./native-loader";
|
|
7
|
-
|
|
8
|
-
async function loadSimpleFinPortfolio(instance: BrokerInstanceConfig): Promise<BrokerPortfolioSnapshot> {
|
|
9
|
-
const module = await loadSimpleFinNativeModule();
|
|
10
|
-
return module.loadSimpleFinPortfolio(instance);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const simpleFinBroker: BrokerAdapter = {
|
|
14
|
-
id: "simplefin",
|
|
15
|
-
name: "SimpleFIN",
|
|
16
|
-
configSchema: [{
|
|
17
|
-
key: "setupToken",
|
|
18
|
-
label: "Setup Token",
|
|
19
|
-
type: "password",
|
|
20
|
-
required: true,
|
|
21
|
-
placeholder: "One-time token from SimpleFIN Bridge",
|
|
22
|
-
}],
|
|
23
|
-
|
|
24
|
-
async validate(instance) {
|
|
25
|
-
return [instance.config.setupToken, instance.config.accessUrl]
|
|
26
|
-
.some((value) => typeof value === "string" && value.trim().length > 0);
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
async importPositions(instance) {
|
|
30
|
-
return (await loadSimpleFinPortfolio(instance)).positions;
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
async importPortfolioSnapshot(instance) {
|
|
34
|
-
return loadSimpleFinPortfolio(instance);
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
async listAccounts(instance) {
|
|
38
|
-
return (await loadSimpleFinPortfolio(instance)).accounts;
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
async getPersistedConfigUpdate(instance) {
|
|
42
|
-
const module = await loadSimpleFinNativeModule();
|
|
43
|
-
return module.simpleFinBroker.getPersistedConfigUpdate?.(instance) ?? null;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
toConfigValues(instance) {
|
|
47
|
-
return {
|
|
48
|
-
setupToken: typeof instance.config.accessUrl === "string"
|
|
49
|
-
? PRESERVED_PASSWORD_HINT
|
|
50
|
-
: instance.config.setupToken,
|
|
51
|
-
};
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
fromConfigValues(values, previous) {
|
|
55
|
-
const setupToken = typeof values.setupToken === "string" ? values.setupToken.trim() : "";
|
|
56
|
-
const accessUrl = previous && typeof previous.config.accessUrl === "string" ? previous.config.accessUrl : "";
|
|
57
|
-
if (setupToken === PRESERVED_PASSWORD_HINT && accessUrl) return { setupToken: "", accessUrl };
|
|
58
|
-
if (setupToken) return { setupToken };
|
|
59
|
-
return accessUrl ? { setupToken: "", accessUrl } : { setupToken: "" };
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const simpleFinPlugin: GloomPlugin = {
|
|
64
|
-
id: "simplefin",
|
|
65
|
-
name: "SimpleFIN",
|
|
66
|
-
version: "1.0.0",
|
|
67
|
-
description: "Read-only investment position sync through SimpleFIN Bridge.",
|
|
68
|
-
toggleable: true,
|
|
69
|
-
broker: simpleFinBroker,
|
|
70
|
-
};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export function loadRobinhoodNativeModule(): Promise<never> {
|
|
2
|
-
return Promise.reject(new Error("Robinhood native sync is unavailable in the desktop web renderer."));
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function loadSimpleFinNativeModule(): Promise<never> {
|
|
6
|
-
return Promise.reject(new Error("SimpleFIN native sync is unavailable in the desktop web renderer."));
|
|
7
|
-
}
|