@metamask-previews/advanced-chart-core 0.0.0-preview-0898196 → 0.0.0-preview-d518059
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/dist/core/bootstrap.d.ts +10 -0
- package/dist/core/bootstrap.d.ts.map +1 -0
- package/dist/core/bootstrap.js +183 -0
- package/dist/core/bootstrap.js.map +1 -0
- package/dist/core/bridge.d.ts +37 -0
- package/dist/core/bridge.d.ts.map +1 -0
- package/dist/core/bridge.js +92 -0
- package/dist/core/bridge.js.map +1 -0
- package/dist/core/dataLifecycle.d.ts +8 -0
- package/dist/core/dataLifecycle.d.ts.map +1 -0
- package/dist/core/dataLifecycle.js +46 -0
- package/dist/core/dataLifecycle.js.map +1 -0
- package/dist/core/host.d.ts +39 -0
- package/dist/core/host.d.ts.map +1 -0
- package/dist/core/host.js +39 -0
- package/dist/core/host.js.map +1 -0
- package/dist/core/loadLibrary.d.ts +16 -0
- package/dist/core/loadLibrary.d.ts.map +1 -0
- package/dist/core/loadLibrary.js +58 -0
- package/dist/core/loadLibrary.js.map +1 -0
- package/dist/core/resolution.d.ts +15 -0
- package/dist/core/resolution.d.ts.map +1 -0
- package/dist/core/resolution.js +54 -0
- package/dist/core/resolution.js.map +1 -0
- package/dist/core/state.d.ts +82 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +309 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/timeUtils.d.ts +32 -0
- package/dist/core/timeUtils.d.ts.map +1 -0
- package/dist/core/timeUtils.js +66 -0
- package/dist/core/timeUtils.js.map +1 -0
- package/dist/core/timezone.d.ts +16 -0
- package/dist/core/timezone.d.ts.map +1 -0
- package/dist/core/timezone.js +131 -0
- package/dist/core/timezone.js.map +1 -0
- package/dist/core/types.d.ts +323 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +17 -0
- package/dist/core/types.js.map +1 -0
- package/dist/features/indicators/index.d.ts +6 -0
- package/dist/features/indicators/index.d.ts.map +1 -0
- package/dist/features/indicators/index.js +177 -0
- package/dist/features/indicators/index.js.map +1 -0
- package/dist/features/indicators/legend.d.ts +40 -0
- package/dist/features/indicators/legend.d.ts.map +1 -0
- package/dist/features/indicators/legend.js +564 -0
- package/dist/features/indicators/legend.js.map +1 -0
- package/dist/features/indicators/resize.d.ts +2 -0
- package/dist/features/indicators/resize.d.ts.map +1 -0
- package/dist/features/indicators/resize.js +30 -0
- package/dist/features/indicators/resize.js.map +1 -0
- package/dist/features/indicators/studies.d.ts +36 -0
- package/dist/features/indicators/studies.d.ts.map +1 -0
- package/dist/features/indicators/studies.js +140 -0
- package/dist/features/indicators/studies.js.map +1 -0
- package/dist/features/indicators/subPane.d.ts +6 -0
- package/dist/features/indicators/subPane.d.ts.map +1 -0
- package/dist/features/indicators/subPane.js +72 -0
- package/dist/features/indicators/subPane.js.map +1 -0
- package/dist/features/volume/index.d.ts +4 -0
- package/dist/features/volume/index.d.ts.map +1 -0
- package/dist/features/volume/index.js +140 -0
- package/dist/features/volume/index.js.map +1 -0
- package/dist/host-entry.d.ts +5 -0
- package/dist/host-entry.d.ts.map +1 -0
- package/dist/host-entry.js +17 -0
- package/dist/host-entry.js.map +1 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -8
- package/dist/index.js.map +1 -1
- package/dist/interaction/crosshair.d.ts +21 -0
- package/dist/interaction/crosshair.d.ts.map +1 -0
- package/dist/interaction/crosshair.js +141 -0
- package/dist/interaction/crosshair.js.map +1 -0
- package/dist/interaction/visibleRange.d.ts +11 -0
- package/dist/interaction/visibleRange.d.ts.map +1 -0
- package/dist/interaction/visibleRange.js +97 -0
- package/dist/interaction/visibleRange.js.map +1 -0
- package/dist/messages/contract.d.ts +262 -0
- package/dist/messages/contract.d.ts.map +1 -0
- package/dist/messages/contract.js +26 -0
- package/dist/messages/contract.js.map +1 -0
- package/dist/messages/handler.d.ts +25 -0
- package/dist/messages/handler.d.ts.map +1 -0
- package/dist/messages/handler.js +48 -0
- package/dist/messages/handler.js.map +1 -0
- package/dist/overlays/focusTime/index.d.ts +6 -0
- package/dist/overlays/focusTime/index.d.ts.map +1 -0
- package/dist/overlays/focusTime/index.js +155 -0
- package/dist/overlays/focusTime/index.js.map +1 -0
- package/dist/overlays/positionLines/index.d.ts +4 -0
- package/dist/overlays/positionLines/index.d.ts.map +1 -0
- package/dist/overlays/positionLines/index.js +183 -0
- package/dist/overlays/positionLines/index.js.map +1 -0
- package/dist/overlays/positionLines/state.d.ts +8 -0
- package/dist/overlays/positionLines/state.d.ts.map +1 -0
- package/dist/overlays/positionLines/state.js +26 -0
- package/dist/overlays/positionLines/state.js.map +1 -0
- package/dist/overlays/socialLeaderboard/index.d.ts +48 -0
- package/dist/overlays/socialLeaderboard/index.d.ts.map +1 -0
- package/dist/overlays/socialLeaderboard/index.js +222 -0
- package/dist/overlays/socialLeaderboard/index.js.map +1 -0
- package/dist/overlays/tradeMarkers/animation.d.ts +5 -0
- package/dist/overlays/tradeMarkers/animation.d.ts.map +1 -0
- package/dist/overlays/tradeMarkers/animation.js +118 -0
- package/dist/overlays/tradeMarkers/animation.js.map +1 -0
- package/dist/overlays/tradeMarkers/index.d.ts +35 -0
- package/dist/overlays/tradeMarkers/index.d.ts.map +1 -0
- package/dist/overlays/tradeMarkers/index.js +324 -0
- package/dist/overlays/tradeMarkers/index.js.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.ts +15 -0
- package/dist/overlays/tradeMarkers/markerHitTest.d.ts.map +1 -0
- package/dist/overlays/tradeMarkers/markerHitTest.js +331 -0
- package/dist/overlays/tradeMarkers/markerHitTest.js.map +1 -0
- package/dist/overlays/tradeMarkers/state.d.ts +20 -0
- package/dist/overlays/tradeMarkers/state.d.ts.map +1 -0
- package/dist/overlays/tradeMarkers/state.js +60 -0
- package/dist/overlays/tradeMarkers/state.js.map +1 -0
- package/dist/pagination/priceApi.d.ts +43 -0
- package/dist/pagination/priceApi.d.ts.map +1 -0
- package/dist/pagination/priceApi.js +97 -0
- package/dist/pagination/priceApi.js.map +1 -0
- package/dist/pagination/rnBacked.d.ts +15 -0
- package/dist/pagination/rnBacked.d.ts.map +1 -0
- package/dist/pagination/rnBacked.js +100 -0
- package/dist/pagination/rnBacked.js.map +1 -0
- package/dist/platform/iframeHost.d.ts +35 -0
- package/dist/platform/iframeHost.d.ts.map +1 -0
- package/dist/platform/iframeHost.js +73 -0
- package/dist/platform/iframeHost.js.map +1 -0
- package/dist/platform/reactNativeHost.d.ts +8 -0
- package/dist/platform/reactNativeHost.d.ts.map +1 -0
- package/dist/platform/reactNativeHost.js +51 -0
- package/dist/platform/reactNativeHost.js.map +1 -0
- package/dist/widget/chartType.d.ts +4 -0
- package/dist/widget/chartType.d.ts.map +1 -0
- package/dist/widget/chartType.js +34 -0
- package/dist/widget/chartType.js.map +1 -0
- package/dist/widget/datafeed.d.ts +22 -0
- package/dist/widget/datafeed.d.ts.map +1 -0
- package/dist/widget/datafeed.js +223 -0
- package/dist/widget/datafeed.js.map +1 -0
- package/dist/widget/externalLinkBridge.d.ts +14 -0
- package/dist/widget/externalLinkBridge.d.ts.map +1 -0
- package/dist/widget/externalLinkBridge.js +127 -0
- package/dist/widget/externalLinkBridge.js.map +1 -0
- package/dist/widget/initChart.d.ts +53 -0
- package/dist/widget/initChart.d.ts.map +1 -0
- package/dist/widget/initChart.js +235 -0
- package/dist/widget/initChart.js.map +1 -0
- package/dist/widget/ohlcvIngestion.d.ts +16 -0
- package/dist/widget/ohlcvIngestion.d.ts.map +1 -0
- package/dist/widget/ohlcvIngestion.js +233 -0
- package/dist/widget/ohlcvIngestion.js.map +1 -0
- package/dist/widget/priceFormatter.d.ts +38 -0
- package/dist/widget/priceFormatter.d.ts.map +1 -0
- package/dist/widget/priceFormatter.js +135 -0
- package/dist/widget/priceFormatter.js.map +1 -0
- package/dist/widget/scaleLayout.d.ts +9 -0
- package/dist/widget/scaleLayout.d.ts.map +1 -0
- package/dist/widget/scaleLayout.js +85 -0
- package/dist/widget/scaleLayout.js.map +1 -0
- package/dist/widget/theme.d.ts +99 -0
- package/dist/widget/theme.d.ts.map +1 -0
- package/dist/widget/theme.js +270 -0
- package/dist/widget/theme.js.map +1 -0
- package/dist/widget/tvDomHelpers.d.ts +19 -0
- package/dist/widget/tvDomHelpers.d.ts.map +1 -0
- package/dist/widget/tvDomHelpers.js +60 -0
- package/dist/widget/tvDomHelpers.js.map +1 -0
- package/dist/widget/visualOverrides.d.ts +27 -0
- package/dist/widget/visualOverrides.d.ts.map +1 -0
- package/dist/widget/visualOverrides.js +58 -0
- package/dist/widget/visualOverrides.js.map +1 -0
- package/package.json +6 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChartConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Phase 1 + 2 bootstrap. Returns the resolved CONFIG so callers (and tests)
|
|
4
|
+
* can inspect what booted. Idempotent on its inbound subscription — the
|
|
5
|
+
* WebView is not expected to bootstrap twice.
|
|
6
|
+
*
|
|
7
|
+
* @returns The resolved chart configuration read from `window.CONFIG`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bootstrap(): ChartConfig;
|
|
10
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/core/bootstrap.ts"],"names":[],"mappings":"AA4EA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAuC9C;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,WAAW,CA2GvC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Entry orchestration. Called once from src/index.ts when the IIFE evaluates
|
|
2
|
+
// inside the WebView.
|
|
3
|
+
//
|
|
4
|
+
// Responsibilities (Phase 1 + 2):
|
|
5
|
+
// 1. Read window.CONFIG (must be inlined by AdvancedChartTemplate before this
|
|
6
|
+
// script runs).
|
|
7
|
+
// 2. Seed core state with the symbol / resolution / theme from CONFIG.
|
|
8
|
+
// 3. Wire the RN→WV bridge to the message dispatcher.
|
|
9
|
+
// 4. Register Phase 1 + 2 message handlers:
|
|
10
|
+
// SET_THEME_COLORS (Phase 1), SET_OHLCV_DATA, REALTIME_UPDATE,
|
|
11
|
+
// SET_CHART_TYPE (Phase 2).
|
|
12
|
+
// 5. Begin loading the TradingView library so it's ready when the first
|
|
13
|
+
// SET_OHLCV_DATA arrives.
|
|
14
|
+
// 6. On first SET_OHLCV_DATA: createChartWidget with the default datafeed,
|
|
15
|
+
// apply visual overrides, attach crosshair + visible-range listeners.
|
|
16
|
+
import { handleAddIndicator, handleRemoveIndicator, handleSetMAVisibility, } from '../features/indicators/index.js';
|
|
17
|
+
import { attachLegendResizeListener, setupLegendOverlay, } from '../features/indicators/legend.js';
|
|
18
|
+
import { handleSetSubPaneLayout } from '../features/indicators/subPane.js';
|
|
19
|
+
import { handleToggleVolume, registerVolumeThemeSync, } from '../features/volume/index.js';
|
|
20
|
+
import { attachCrosshairListener, attachTapDismiss, } from '../interaction/crosshair.js';
|
|
21
|
+
import { attachVisibleRangeListeners } from '../interaction/visibleRange.js';
|
|
22
|
+
import { dispatchInboundMessage, registerHandler, } from '../messages/handler.js';
|
|
23
|
+
import { registerFocusTimeOverlay } from '../overlays/focusTime/index.js';
|
|
24
|
+
import { registerPositionLinesOverlay } from '../overlays/positionLines/index.js';
|
|
25
|
+
import { slbScheduleInitialCentering } from '../overlays/socialLeaderboard/index.js';
|
|
26
|
+
import { registerTradeMarkerPulseHandler } from '../overlays/tradeMarkers/animation.js';
|
|
27
|
+
import { registerTradeMarkerOverlay } from '../overlays/tradeMarkers/index.js';
|
|
28
|
+
import { attachMarkerHitTest } from '../overlays/tradeMarkers/markerHitTest.js';
|
|
29
|
+
import { registerRnBackedPaginationHandler } from '../pagination/rnBacked.js';
|
|
30
|
+
import { handleSetChartType } from '../widget/chartType.js';
|
|
31
|
+
import { customDatafeed } from '../widget/datafeed.js';
|
|
32
|
+
import { createChartWidget, scheduleChartLayoutSettledNotify, } from '../widget/initChart.js';
|
|
33
|
+
import { handleRealtimeUpdate, handleSetOHLCVData, onFirstOhlcvData, } from '../widget/ohlcvIngestion.js';
|
|
34
|
+
import { advancedChartPriceFormatterFactory } from '../widget/priceFormatter.js';
|
|
35
|
+
import { applyScaleLayout } from '../widget/scaleLayout.js';
|
|
36
|
+
import { applyThemeColors, flushPendingTheme, initThemeFromConfig, } from '../widget/theme.js';
|
|
37
|
+
import { applyVisualOverrides } from '../widget/visualOverrides.js';
|
|
38
|
+
import { onFromRN, postToRN, reportErrorToRN } from './bridge.js';
|
|
39
|
+
import { getHostTransport } from './host.js';
|
|
40
|
+
import { loadTradingViewLibrary } from './loadLibrary.js';
|
|
41
|
+
import { getOhlcvData, getVisibleFromMs, getVisibleToMs, setConfig, setSubPaneHeightRatio, } from './state.js';
|
|
42
|
+
import { getApproxBarDurationSec } from './timeUtils.js';
|
|
43
|
+
/**
|
|
44
|
+
* When RN passes an explicit visible-range start (e.g. a specific period like
|
|
45
|
+
* 1D/1W/1M), build a `{ type: 'time-range', from, to }` timeframe so the
|
|
46
|
+
* initial view snaps to that window instead of defaulting to `Date.now()`.
|
|
47
|
+
* Padded by 2 bar durations so the last bar isn't glued to the right edge.
|
|
48
|
+
* Ported from chartLogic.js initChart's `tfOption` computation (~line 5284).
|
|
49
|
+
*
|
|
50
|
+
* @returns A `time-range` timeframe pinned to the requested window, or
|
|
51
|
+
* `undefined` when RN did not pass an explicit visible-range start.
|
|
52
|
+
*/
|
|
53
|
+
function buildInitialTimeframe() {
|
|
54
|
+
const visibleFromMs = getVisibleFromMs();
|
|
55
|
+
if (visibleFromMs === null) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const visibleToMs = getVisibleToMs() ?? Date.now();
|
|
59
|
+
const initBarPadSec = getApproxBarDurationSec(getOhlcvData()) * 2;
|
|
60
|
+
return {
|
|
61
|
+
type: 'time-range',
|
|
62
|
+
from: Math.floor(visibleFromMs / 1000),
|
|
63
|
+
to: Math.ceil(visibleToMs / 1000) + initBarPadSec,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function readConfig() {
|
|
67
|
+
const config = getHostTransport().getConfig();
|
|
68
|
+
if (!config) {
|
|
69
|
+
throw new Error('window.CONFIG is missing — AdvancedChartTemplate must inline ' +
|
|
70
|
+
'CONFIG before chartLogic runs.');
|
|
71
|
+
}
|
|
72
|
+
return config;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Phase 1 + 2 bootstrap. Returns the resolved CONFIG so callers (and tests)
|
|
76
|
+
* can inspect what booted. Idempotent on its inbound subscription — the
|
|
77
|
+
* WebView is not expected to bootstrap twice.
|
|
78
|
+
*
|
|
79
|
+
* @returns The resolved chart configuration read from `window.CONFIG`.
|
|
80
|
+
*/
|
|
81
|
+
export function bootstrap() {
|
|
82
|
+
const config = readConfig();
|
|
83
|
+
setConfig(config);
|
|
84
|
+
initThemeFromConfig(config.theme);
|
|
85
|
+
if (typeof config.subPaneHeightRatio === 'number') {
|
|
86
|
+
setSubPaneHeightRatio(config.subPaneHeightRatio);
|
|
87
|
+
}
|
|
88
|
+
registerHandler('SET_THEME_COLORS', (payload) => {
|
|
89
|
+
applyThemeColors(payload);
|
|
90
|
+
});
|
|
91
|
+
registerHandler('SET_OHLCV_DATA', (payload) => {
|
|
92
|
+
handleSetOHLCVData(payload);
|
|
93
|
+
});
|
|
94
|
+
registerHandler('REALTIME_UPDATE', (payload) => {
|
|
95
|
+
handleRealtimeUpdate(payload);
|
|
96
|
+
});
|
|
97
|
+
registerHandler('SET_CHART_TYPE', (payload) => {
|
|
98
|
+
handleSetChartType(payload);
|
|
99
|
+
});
|
|
100
|
+
registerHandler('ADD_INDICATOR', (payload) => {
|
|
101
|
+
handleAddIndicator(payload, config);
|
|
102
|
+
});
|
|
103
|
+
registerHandler('REMOVE_INDICATOR', (payload) => {
|
|
104
|
+
handleRemoveIndicator(payload);
|
|
105
|
+
});
|
|
106
|
+
registerHandler('SET_MA_VISIBILITY', (payload) => {
|
|
107
|
+
handleSetMAVisibility(payload, config);
|
|
108
|
+
});
|
|
109
|
+
registerHandler('TOGGLE_VOLUME', (payload) => {
|
|
110
|
+
handleToggleVolume(payload);
|
|
111
|
+
});
|
|
112
|
+
registerHandler('SET_SUB_PANE_LAYOUT', (payload) => {
|
|
113
|
+
handleSetSubPaneLayout(payload);
|
|
114
|
+
});
|
|
115
|
+
registerTradeMarkerOverlay();
|
|
116
|
+
registerTradeMarkerPulseHandler();
|
|
117
|
+
registerFocusTimeOverlay();
|
|
118
|
+
registerPositionLinesOverlay();
|
|
119
|
+
registerRnBackedPaginationHandler();
|
|
120
|
+
registerVolumeThemeSync();
|
|
121
|
+
onFromRN((message) => {
|
|
122
|
+
dispatchInboundMessage(message);
|
|
123
|
+
});
|
|
124
|
+
// Library load is fire-and-forget; the first-data handler awaits readiness
|
|
125
|
+
// again before constructing the widget, so this is purely a head-start.
|
|
126
|
+
loadTradingViewLibrary(config.libraryUrl).catch((error) => {
|
|
127
|
+
reportErrorToRN(error);
|
|
128
|
+
});
|
|
129
|
+
onFirstOhlcvData(() => {
|
|
130
|
+
loadTradingViewLibrary(config.libraryUrl)
|
|
131
|
+
.then(() => {
|
|
132
|
+
return createChartWidget(config, {
|
|
133
|
+
datafeed: customDatafeed,
|
|
134
|
+
customFormatters: {
|
|
135
|
+
priceFormatterFactory: advancedChartPriceFormatterFactory,
|
|
136
|
+
},
|
|
137
|
+
timeframe: buildInitialTimeframe(),
|
|
138
|
+
onReady: (widget) => {
|
|
139
|
+
try {
|
|
140
|
+
flushPendingTheme();
|
|
141
|
+
applyScaleLayout();
|
|
142
|
+
applyVisualOverrides(config.visualOverrides);
|
|
143
|
+
setupLegendOverlay(config.legendOverlay);
|
|
144
|
+
const chart = widget.activeChart();
|
|
145
|
+
// Match legacy onChartReady: when no explicit visible range
|
|
146
|
+
// was passed, pin a 2-bar gap on the right. TV's default is
|
|
147
|
+
// wider, leaving the chart visibly offset left.
|
|
148
|
+
if (getVisibleFromMs() === null) {
|
|
149
|
+
try {
|
|
150
|
+
chart.getTimeScale().setRightOffset(2);
|
|
151
|
+
}
|
|
152
|
+
catch (rightOffsetError) {
|
|
153
|
+
reportErrorToRN(rightOffsetError);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
attachCrosshairListener(chart);
|
|
157
|
+
attachTapDismiss(widget);
|
|
158
|
+
attachMarkerHitTest(widget, chart);
|
|
159
|
+
attachVisibleRangeListeners(chart);
|
|
160
|
+
chart
|
|
161
|
+
.selection()
|
|
162
|
+
.onChanged()
|
|
163
|
+
.subscribe(null, () => {
|
|
164
|
+
chart.selection().clear();
|
|
165
|
+
});
|
|
166
|
+
attachLegendResizeListener(widget);
|
|
167
|
+
slbScheduleInitialCentering();
|
|
168
|
+
scheduleChartLayoutSettledNotify();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
reportErrorToRN(error);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
})
|
|
176
|
+
.catch((error) => {
|
|
177
|
+
reportErrorToRN(error);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
postToRN('DEBUG', { message: 'modular-bootstrap-ready' });
|
|
181
|
+
return config;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/core/bootstrap.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,sBAAsB;AACtB,EAAE;AACF,kCAAkC;AAClC,8EAA8E;AAC9E,mBAAmB;AACnB,uEAAuE;AACvE,sDAAsD;AACtD,4CAA4C;AAC5C,oEAAoE;AACpE,iCAAiC;AACjC,wEAAwE;AACxE,6BAA6B;AAC7B,2EAA2E;AAC3E,yEAAyE;AAEzE,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EACL,sBAAsB,EACtB,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,EAAE,+BAA+B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,gCAAgC,GACjC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kCAAkC,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,SAAS,EACT,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAGzD;;;;;;;;;GASG;AACH,SAAS,qBAAqB;IAG5B,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;IACzC,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,WAAW,GAAG,cAAc,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACnD,MAAM,aAAa,GAAG,uBAAuB,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC;IAClE,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;QACtC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,aAAa;KAClD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC,SAAS,EAAE,CAAC;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,+DAA+D;YAC7D,gCAAgC,CACnC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,SAAS,CAAC,MAAM,CAAC,CAAC;IAElB,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,OAAO,MAAM,CAAC,kBAAkB,KAAK,QAAQ,EAAE,CAAC;QAClD,qBAAqB,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IACnD,CAAC;IAED,eAAe,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC9C,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC5C,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC7C,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC5C,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE;QAC3C,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC9C,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,EAAE;QAC/C,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE;QAC3C,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IACH,eAAe,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,EAAE;QACjD,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,0BAA0B,EAAE,CAAC;IAC7B,+BAA+B,EAAE,CAAC;IAClC,wBAAwB,EAAE,CAAC;IAC3B,4BAA4B,EAAE,CAAC;IAC/B,iCAAiC,EAAE,CAAC;IACpC,uBAAuB,EAAE,CAAC;IAE1B,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE;QACnB,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,wEAAwE;IACxE,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACxD,eAAe,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,gBAAgB,CAAC,GAAG,EAAE;QACpB,sBAAsB,CAAC,MAAM,CAAC,UAAU,CAAC;aACtC,IAAI,CAAC,GAAG,EAAE;YACT,OAAO,iBAAiB,CAAC,MAAM,EAAE;gBAC/B,QAAQ,EAAE,cAAc;gBACxB,gBAAgB,EAAE;oBAChB,qBAAqB,EAAE,kCAAkC;iBAC1D;gBACD,SAAS,EAAE,qBAAqB,EAAE;gBAClC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;oBAClB,IAAI,CAAC;wBACH,iBAAiB,EAAE,CAAC;wBACpB,gBAAgB,EAAE,CAAC;wBACnB,oBAAoB,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;wBAC7C,kBAAkB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;wBACzC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;wBACnC,4DAA4D;wBAC5D,4DAA4D;wBAC5D,gDAAgD;wBAChD,IAAI,gBAAgB,EAAE,KAAK,IAAI,EAAE,CAAC;4BAChC,IAAI,CAAC;gCACH,KAAK,CAAC,YAAY,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;4BACzC,CAAC;4BAAC,OAAO,gBAAgB,EAAE,CAAC;gCAC1B,eAAe,CAAC,gBAAgB,CAAC,CAAC;4BACpC,CAAC;wBACH,CAAC;wBACD,uBAAuB,CAAC,KAAK,CAAC,CAAC;wBAC/B,gBAAgB,CAAC,MAAM,CAAC,CAAC;wBACzB,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;wBACnC,2BAA2B,CAAC,KAAK,CAAC,CAAC;wBACnC,KAAK;6BACF,SAAS,EAAE;6BACX,SAAS,EAAE;6BACX,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE;4BACpB,KAAK,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC;wBAC5B,CAAC,CAAC,CAAC;wBACL,0BAA0B,CAAC,MAAM,CAAC,CAAC;wBACnC,2BAA2B,EAAE,CAAC;wBAC9B,gCAAgC,EAAE,CAAC;oBACrC,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,eAAe,CAAC,KAAK,CAAC,CAAC;oBACzB,CAAC;gBACH,CAAC;aACF,CAAC,CAAC;QACL,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAC;IAE1D,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["// Entry orchestration. Called once from src/index.ts when the IIFE evaluates\n// inside the WebView.\n//\n// Responsibilities (Phase 1 + 2):\n// 1. Read window.CONFIG (must be inlined by AdvancedChartTemplate before this\n// script runs).\n// 2. Seed core state with the symbol / resolution / theme from CONFIG.\n// 3. Wire the RN→WV bridge to the message dispatcher.\n// 4. Register Phase 1 + 2 message handlers:\n// SET_THEME_COLORS (Phase 1), SET_OHLCV_DATA, REALTIME_UPDATE,\n// SET_CHART_TYPE (Phase 2).\n// 5. Begin loading the TradingView library so it's ready when the first\n// SET_OHLCV_DATA arrives.\n// 6. On first SET_OHLCV_DATA: createChartWidget with the default datafeed,\n// apply visual overrides, attach crosshair + visible-range listeners.\n\nimport {\n handleAddIndicator,\n handleRemoveIndicator,\n handleSetMAVisibility,\n} from '../features/indicators/index.js';\nimport {\n attachLegendResizeListener,\n setupLegendOverlay,\n} from '../features/indicators/legend.js';\nimport { handleSetSubPaneLayout } from '../features/indicators/subPane.js';\nimport {\n handleToggleVolume,\n registerVolumeThemeSync,\n} from '../features/volume/index.js';\nimport {\n attachCrosshairListener,\n attachTapDismiss,\n} from '../interaction/crosshair.js';\nimport { attachVisibleRangeListeners } from '../interaction/visibleRange.js';\nimport {\n dispatchInboundMessage,\n registerHandler,\n} from '../messages/handler.js';\nimport { registerFocusTimeOverlay } from '../overlays/focusTime/index.js';\nimport { registerPositionLinesOverlay } from '../overlays/positionLines/index.js';\nimport { slbScheduleInitialCentering } from '../overlays/socialLeaderboard/index.js';\nimport { registerTradeMarkerPulseHandler } from '../overlays/tradeMarkers/animation.js';\nimport { registerTradeMarkerOverlay } from '../overlays/tradeMarkers/index.js';\nimport { attachMarkerHitTest } from '../overlays/tradeMarkers/markerHitTest.js';\nimport { registerRnBackedPaginationHandler } from '../pagination/rnBacked.js';\nimport { handleSetChartType } from '../widget/chartType.js';\nimport { customDatafeed } from '../widget/datafeed.js';\nimport {\n createChartWidget,\n scheduleChartLayoutSettledNotify,\n} from '../widget/initChart.js';\nimport {\n handleRealtimeUpdate,\n handleSetOHLCVData,\n onFirstOhlcvData,\n} from '../widget/ohlcvIngestion.js';\nimport { advancedChartPriceFormatterFactory } from '../widget/priceFormatter.js';\nimport { applyScaleLayout } from '../widget/scaleLayout.js';\nimport {\n applyThemeColors,\n flushPendingTheme,\n initThemeFromConfig,\n} from '../widget/theme.js';\nimport { applyVisualOverrides } from '../widget/visualOverrides.js';\nimport { onFromRN, postToRN, reportErrorToRN } from './bridge.js';\nimport { getHostTransport } from './host.js';\nimport { loadTradingViewLibrary } from './loadLibrary.js';\nimport {\n getOhlcvData,\n getVisibleFromMs,\n getVisibleToMs,\n setConfig,\n setSubPaneHeightRatio,\n} from './state.js';\nimport { getApproxBarDurationSec } from './timeUtils.js';\nimport type { ChartConfig } from './types.js';\n\n/**\n * When RN passes an explicit visible-range start (e.g. a specific period like\n * 1D/1W/1M), build a `{ type: 'time-range', from, to }` timeframe so the\n * initial view snaps to that window instead of defaulting to `Date.now()`.\n * Padded by 2 bar durations so the last bar isn't glued to the right edge.\n * Ported from chartLogic.js initChart's `tfOption` computation (~line 5284).\n *\n * @returns A `time-range` timeframe pinned to the requested window, or\n * `undefined` when RN did not pass an explicit visible-range start.\n */\nfunction buildInitialTimeframe():\n | { type: 'time-range'; from: number; to: number }\n | undefined {\n const visibleFromMs = getVisibleFromMs();\n if (visibleFromMs === null) {\n return undefined;\n }\n const visibleToMs = getVisibleToMs() ?? Date.now();\n const initBarPadSec = getApproxBarDurationSec(getOhlcvData()) * 2;\n return {\n type: 'time-range',\n from: Math.floor(visibleFromMs / 1000),\n to: Math.ceil(visibleToMs / 1000) + initBarPadSec,\n };\n}\n\nfunction readConfig(): ChartConfig {\n const config = getHostTransport().getConfig();\n if (!config) {\n throw new Error(\n 'window.CONFIG is missing — AdvancedChartTemplate must inline ' +\n 'CONFIG before chartLogic runs.',\n );\n }\n return config;\n}\n\n/**\n * Phase 1 + 2 bootstrap. Returns the resolved CONFIG so callers (and tests)\n * can inspect what booted. Idempotent on its inbound subscription — the\n * WebView is not expected to bootstrap twice.\n *\n * @returns The resolved chart configuration read from `window.CONFIG`.\n */\nexport function bootstrap(): ChartConfig {\n const config = readConfig();\n setConfig(config);\n\n initThemeFromConfig(config.theme);\n if (typeof config.subPaneHeightRatio === 'number') {\n setSubPaneHeightRatio(config.subPaneHeightRatio);\n }\n\n registerHandler('SET_THEME_COLORS', (payload) => {\n applyThemeColors(payload);\n });\n registerHandler('SET_OHLCV_DATA', (payload) => {\n handleSetOHLCVData(payload);\n });\n registerHandler('REALTIME_UPDATE', (payload) => {\n handleRealtimeUpdate(payload);\n });\n registerHandler('SET_CHART_TYPE', (payload) => {\n handleSetChartType(payload);\n });\n registerHandler('ADD_INDICATOR', (payload) => {\n handleAddIndicator(payload, config);\n });\n registerHandler('REMOVE_INDICATOR', (payload) => {\n handleRemoveIndicator(payload);\n });\n registerHandler('SET_MA_VISIBILITY', (payload) => {\n handleSetMAVisibility(payload, config);\n });\n registerHandler('TOGGLE_VOLUME', (payload) => {\n handleToggleVolume(payload);\n });\n registerHandler('SET_SUB_PANE_LAYOUT', (payload) => {\n handleSetSubPaneLayout(payload);\n });\n\n registerTradeMarkerOverlay();\n registerTradeMarkerPulseHandler();\n registerFocusTimeOverlay();\n registerPositionLinesOverlay();\n registerRnBackedPaginationHandler();\n registerVolumeThemeSync();\n\n onFromRN((message) => {\n dispatchInboundMessage(message);\n });\n\n // Library load is fire-and-forget; the first-data handler awaits readiness\n // again before constructing the widget, so this is purely a head-start.\n loadTradingViewLibrary(config.libraryUrl).catch((error) => {\n reportErrorToRN(error);\n });\n\n onFirstOhlcvData(() => {\n loadTradingViewLibrary(config.libraryUrl)\n .then(() => {\n return createChartWidget(config, {\n datafeed: customDatafeed,\n customFormatters: {\n priceFormatterFactory: advancedChartPriceFormatterFactory,\n },\n timeframe: buildInitialTimeframe(),\n onReady: (widget) => {\n try {\n flushPendingTheme();\n applyScaleLayout();\n applyVisualOverrides(config.visualOverrides);\n setupLegendOverlay(config.legendOverlay);\n const chart = widget.activeChart();\n // Match legacy onChartReady: when no explicit visible range\n // was passed, pin a 2-bar gap on the right. TV's default is\n // wider, leaving the chart visibly offset left.\n if (getVisibleFromMs() === null) {\n try {\n chart.getTimeScale().setRightOffset(2);\n } catch (rightOffsetError) {\n reportErrorToRN(rightOffsetError);\n }\n }\n attachCrosshairListener(chart);\n attachTapDismiss(widget);\n attachMarkerHitTest(widget, chart);\n attachVisibleRangeListeners(chart);\n chart\n .selection()\n .onChanged()\n .subscribe(null, () => {\n chart.selection().clear();\n });\n attachLegendResizeListener(widget);\n slbScheduleInitialCentering();\n scheduleChartLayoutSettledNotify();\n } catch (error) {\n reportErrorToRN(error);\n }\n },\n });\n })\n .catch((error) => {\n reportErrorToRN(error);\n });\n });\n\n postToRN('DEBUG', { message: 'modular-bootstrap-ready' });\n\n return config;\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { InboundMessage, OutboundMessageType, OutboundPayloads } from '../messages/contract.js';
|
|
2
|
+
export declare function safeStringify(value: unknown): string;
|
|
3
|
+
/**
|
|
4
|
+
* Posts a typed message to the host via the active transport. Equivalent to
|
|
5
|
+
* legacy `sendToReactNative(type, payload)` at chartLogic.js line ~98.
|
|
6
|
+
* Silently no-ops when the host transport is unavailable (e.g. during unit
|
|
7
|
+
* tests in a jsdom environment without the RN bridge stub).
|
|
8
|
+
*
|
|
9
|
+
* @param type - The outbound message type tag.
|
|
10
|
+
* @param payload - The payload associated with the message type.
|
|
11
|
+
*/
|
|
12
|
+
export declare function postToRN<Type extends OutboundMessageType>(type: Type, payload: OutboundPayloads[Type]): void;
|
|
13
|
+
/**
|
|
14
|
+
* Reports a runtime error to React Native via the ERROR channel. Matches the
|
|
15
|
+
* legacy `sendToReactNative('ERROR', { message })` pattern used throughout
|
|
16
|
+
* chartLogic.js.
|
|
17
|
+
*
|
|
18
|
+
* @param error - The error (or arbitrary thrown value) to report.
|
|
19
|
+
*/
|
|
20
|
+
export declare function reportErrorToRN(error: unknown): void;
|
|
21
|
+
export type InboundMessageHandler = (message: InboundMessage) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Registers a single inbound listener. The host posts JSON strings; the
|
|
24
|
+
* active transport delivers each raw payload (after any transport-level
|
|
25
|
+
* origin filtering) and this function parses + validates it before forwarding
|
|
26
|
+
* a well-formed message to the handler.
|
|
27
|
+
*
|
|
28
|
+
* The returned function unsubscribes — useful for tests; the real bundle
|
|
29
|
+
* subscribes once at bootstrap and never unsubscribes.
|
|
30
|
+
*
|
|
31
|
+
* @param handler - Callback invoked with each well-formed inbound message.
|
|
32
|
+
* @returns A function that removes the registered listeners.
|
|
33
|
+
*/
|
|
34
|
+
export declare function onFromRN(handler: InboundMessageHandler): () => void;
|
|
35
|
+
/** Re-export for callers that want the type tag union. */
|
|
36
|
+
export type { InboundMessageType } from '../messages/contract.js';
|
|
37
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/core/bridge.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAGjC,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,SAAS,mBAAmB,EACvD,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAC9B,IAAI,CAON;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAUpD;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAEtE;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAsBnE;AAED,0DAA0D;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Typed bridge between the WebView IIFE and its host.
|
|
2
|
+
//
|
|
3
|
+
// Owns the platform-agnostic parts of host communication — JSON framing,
|
|
4
|
+
// inbound parsing, and message validation — and delegates the actual
|
|
5
|
+
// transport (outbound delivery, inbound subscription, origin filtering) to
|
|
6
|
+
// the injected host adapter (see core/host.ts). Preserves the same
|
|
7
|
+
// window.ReactNativeWebView.postMessage(...) call shape via the default RN
|
|
8
|
+
// adapter, so the RN-side parseWebViewMessage decodes messages unchanged.
|
|
9
|
+
import { getHostTransport } from './host.js';
|
|
10
|
+
export function safeStringify(value) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.stringify(value) ?? 'Unknown error';
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return String(value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Posts a typed message to the host via the active transport. Equivalent to
|
|
20
|
+
* legacy `sendToReactNative(type, payload)` at chartLogic.js line ~98.
|
|
21
|
+
* Silently no-ops when the host transport is unavailable (e.g. during unit
|
|
22
|
+
* tests in a jsdom environment without the RN bridge stub).
|
|
23
|
+
*
|
|
24
|
+
* @param type - The outbound message type tag.
|
|
25
|
+
* @param payload - The payload associated with the message type.
|
|
26
|
+
*/
|
|
27
|
+
export function postToRN(type, payload) {
|
|
28
|
+
try {
|
|
29
|
+
getHostTransport().postMessage(JSON.stringify({ type, payload }));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// postMessage / JSON.stringify failure: nothing to do — the WebView
|
|
33
|
+
// cannot inform the host of its own bridge failure.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reports a runtime error to React Native via the ERROR channel. Matches the
|
|
38
|
+
* legacy `sendToReactNative('ERROR', { message })` pattern used throughout
|
|
39
|
+
* chartLogic.js.
|
|
40
|
+
*
|
|
41
|
+
* @param error - The error (or arbitrary thrown value) to report.
|
|
42
|
+
*/
|
|
43
|
+
export function reportErrorToRN(error) {
|
|
44
|
+
let message;
|
|
45
|
+
if (error instanceof Error) {
|
|
46
|
+
message = error.message;
|
|
47
|
+
}
|
|
48
|
+
else if (typeof error === 'string') {
|
|
49
|
+
message = error;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
message = safeStringify(error);
|
|
53
|
+
}
|
|
54
|
+
postToRN('ERROR', { message });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Registers a single inbound listener. The host posts JSON strings; the
|
|
58
|
+
* active transport delivers each raw payload (after any transport-level
|
|
59
|
+
* origin filtering) and this function parses + validates it before forwarding
|
|
60
|
+
* a well-formed message to the handler.
|
|
61
|
+
*
|
|
62
|
+
* The returned function unsubscribes — useful for tests; the real bundle
|
|
63
|
+
* subscribes once at bootstrap and never unsubscribes.
|
|
64
|
+
*
|
|
65
|
+
* @param handler - Callback invoked with each well-formed inbound message.
|
|
66
|
+
* @returns A function that removes the registered listeners.
|
|
67
|
+
*/
|
|
68
|
+
export function onFromRN(handler) {
|
|
69
|
+
return getHostTransport().subscribe((raw) => {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
73
|
+
}
|
|
74
|
+
catch (parseError) {
|
|
75
|
+
reportErrorToRN(parseError);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const candidate = parsed;
|
|
82
|
+
if (typeof candidate.type !== 'string') {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Trusting the type narrowing here is fine because messages/handler.ts
|
|
86
|
+
// re-validates via a switch on candidate.type before dispatching to a
|
|
87
|
+
// typed handler. Phase 1 only routes SET_THEME_COLORS, but the listener
|
|
88
|
+
// forwards every well-formed message so the handler can decide.
|
|
89
|
+
handler(parsed);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/core/bridge.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,yEAAyE;AACzE,qEAAqE;AACrE,2EAA2E;AAC3E,mEAAmE;AACnE,2EAA2E;AAC3E,0EAA0E;AAO1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,eAAe,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAU,EACV,OAA+B;IAE/B,IAAI,CAAC;QACH,gBAAgB,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;QACpE,oDAAoD;IACtD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,OAAe,CAAC;IACpB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,CAAC;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IACD,QAAQ,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AACjC,CAAC;AAID;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,QAAQ,CAAC,OAA8B;IACrD,OAAO,gBAAgB,EAAE,CAAC,SAAS,CAAC,CAAC,GAAY,EAAQ,EAAE;QACzD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3D,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,eAAe,CAAC,UAAU,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,MAAM,SAAS,GAAG,MAA4B,CAAC;QAC/C,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,OAAO;QACT,CAAC;QACD,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,gEAAgE;QAChE,OAAO,CAAC,MAAwB,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["// Typed bridge between the WebView IIFE and its host.\n//\n// Owns the platform-agnostic parts of host communication — JSON framing,\n// inbound parsing, and message validation — and delegates the actual\n// transport (outbound delivery, inbound subscription, origin filtering) to\n// the injected host adapter (see core/host.ts). Preserves the same\n// window.ReactNativeWebView.postMessage(...) call shape via the default RN\n// adapter, so the RN-side parseWebViewMessage decodes messages unchanged.\n\nimport type {\n InboundMessage,\n OutboundMessageType,\n OutboundPayloads,\n} from '../messages/contract.js';\nimport { getHostTransport } from './host.js';\n\nexport function safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? 'Unknown error';\n } catch {\n return String(value);\n }\n}\n\n/**\n * Posts a typed message to the host via the active transport. Equivalent to\n * legacy `sendToReactNative(type, payload)` at chartLogic.js line ~98.\n * Silently no-ops when the host transport is unavailable (e.g. during unit\n * tests in a jsdom environment without the RN bridge stub).\n *\n * @param type - The outbound message type tag.\n * @param payload - The payload associated with the message type.\n */\nexport function postToRN<Type extends OutboundMessageType>(\n type: Type,\n payload: OutboundPayloads[Type],\n): void {\n try {\n getHostTransport().postMessage(JSON.stringify({ type, payload }));\n } catch {\n // postMessage / JSON.stringify failure: nothing to do — the WebView\n // cannot inform the host of its own bridge failure.\n }\n}\n\n/**\n * Reports a runtime error to React Native via the ERROR channel. Matches the\n * legacy `sendToReactNative('ERROR', { message })` pattern used throughout\n * chartLogic.js.\n *\n * @param error - The error (or arbitrary thrown value) to report.\n */\nexport function reportErrorToRN(error: unknown): void {\n let message: string;\n if (error instanceof Error) {\n message = error.message;\n } else if (typeof error === 'string') {\n message = error;\n } else {\n message = safeStringify(error);\n }\n postToRN('ERROR', { message });\n}\n\nexport type InboundMessageHandler = (message: InboundMessage) => void;\n\n/**\n * Registers a single inbound listener. The host posts JSON strings; the\n * active transport delivers each raw payload (after any transport-level\n * origin filtering) and this function parses + validates it before forwarding\n * a well-formed message to the handler.\n *\n * The returned function unsubscribes — useful for tests; the real bundle\n * subscribes once at bootstrap and never unsubscribes.\n *\n * @param handler - Callback invoked with each well-formed inbound message.\n * @returns A function that removes the registered listeners.\n */\nexport function onFromRN(handler: InboundMessageHandler): () => void {\n return getHostTransport().subscribe((raw: unknown): void => {\n let parsed: unknown;\n try {\n parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;\n } catch (parseError) {\n reportErrorToRN(parseError);\n return;\n }\n if (!parsed || typeof parsed !== 'object') {\n return;\n }\n const candidate = parsed as { type?: unknown };\n if (typeof candidate.type !== 'string') {\n return;\n }\n // Trusting the type narrowing here is fine because messages/handler.ts\n // re-validates via a switch on candidate.type before dispatching to a\n // typed handler. Phase 1 only routes SET_THEME_COLORS, but the listener\n // forwards every well-formed message so the handler can decide.\n handler(parsed as InboundMessage);\n });\n}\n\n/** Re-export for callers that want the type tag union. */\nexport type { InboundMessageType } from '../messages/contract.js';\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type DataLifecycleEvent = 'ohlcvReset' | 'ohlcvPrepended' | 'visibleRangeChanged';
|
|
2
|
+
type Listener = () => void;
|
|
3
|
+
export declare function onDataLifecycle(event: DataLifecycleEvent, listener: Listener): () => void;
|
|
4
|
+
export declare function notifyDataLifecycle(event: DataLifecycleEvent): void;
|
|
5
|
+
/** Test-only: clear every listener across every event. */
|
|
6
|
+
export declare function _resetDataLifecycleForTests(): void;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=dataLifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataLifecycle.d.ts","sourceRoot":"","sources":["../../src/core/dataLifecycle.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,gBAAgB,GAChB,qBAAqB,CAAC;AAE1B,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAC;AAQ3B,wBAAgB,eAAe,CAC7B,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,QAAQ,GACjB,MAAM,IAAI,CASZ;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CASnE;AAED,0DAA0D;AAC1D,wBAAgB,2BAA2B,IAAI,IAAI,CAIlD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Lightweight event bus for data lifecycle events overlays need to react to.
|
|
2
|
+
//
|
|
3
|
+
// The widget modules (ohlcvIngestion, pagination, visibleRange) publish
|
|
4
|
+
// events after they mutate the OHLCV series or the visible range;
|
|
5
|
+
// overlay modules (tradeMarkers, positionLines) subscribe and re-place
|
|
6
|
+
// their shapes. Keeps overlays decoupled from widget/* — overlays never
|
|
7
|
+
// import from widget/*, matching the ESLint `no-restricted-paths`
|
|
8
|
+
// direction described in the plan.
|
|
9
|
+
//
|
|
10
|
+
// The events are intentionally void — subscribers read whatever state
|
|
11
|
+
// they need from core/state.ts. Errors inside a subscriber are logged
|
|
12
|
+
// to RN so a broken overlay doesn't take the widget down with it.
|
|
13
|
+
import { reportErrorToRN } from './bridge.js';
|
|
14
|
+
const listeners = {
|
|
15
|
+
ohlcvReset: [],
|
|
16
|
+
ohlcvPrepended: [],
|
|
17
|
+
visibleRangeChanged: [],
|
|
18
|
+
};
|
|
19
|
+
export function onDataLifecycle(event, listener) {
|
|
20
|
+
listeners[event].push(listener);
|
|
21
|
+
return () => {
|
|
22
|
+
const bucket = listeners[event];
|
|
23
|
+
const idx = bucket.indexOf(listener);
|
|
24
|
+
if (idx !== -1) {
|
|
25
|
+
bucket.splice(idx, 1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function notifyDataLifecycle(event) {
|
|
30
|
+
const bucket = listeners[event];
|
|
31
|
+
for (const listener of bucket) {
|
|
32
|
+
try {
|
|
33
|
+
listener();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
reportErrorToRN(error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Test-only: clear every listener across every event. */
|
|
41
|
+
export function _resetDataLifecycleForTests() {
|
|
42
|
+
listeners.ohlcvReset = [];
|
|
43
|
+
listeners.ohlcvPrepended = [];
|
|
44
|
+
listeners.visibleRangeChanged = [];
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=dataLifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataLifecycle.js","sourceRoot":"","sources":["../../src/core/dataLifecycle.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,wEAAwE;AACxE,kEAAkE;AAClE,mCAAmC;AACnC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,kEAAkE;AAElE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAS9C,MAAM,SAAS,GAA2C;IACxD,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,EAAE;IAClB,mBAAmB,EAAE,EAAE;CACxB,CAAC;AAEF,MAAM,UAAU,eAAe,CAC7B,KAAyB,EACzB,QAAkB;IAElB,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,OAAO,GAAG,EAAE;QACV,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAyB;IAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAChC,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,QAAQ,EAAE,CAAC;QACb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,2BAA2B;IACzC,SAAS,CAAC,UAAU,GAAG,EAAE,CAAC;IAC1B,SAAS,CAAC,cAAc,GAAG,EAAE,CAAC;IAC9B,SAAS,CAAC,mBAAmB,GAAG,EAAE,CAAC;AACrC,CAAC","sourcesContent":["// Lightweight event bus for data lifecycle events overlays need to react to.\n//\n// The widget modules (ohlcvIngestion, pagination, visibleRange) publish\n// events after they mutate the OHLCV series or the visible range;\n// overlay modules (tradeMarkers, positionLines) subscribe and re-place\n// their shapes. Keeps overlays decoupled from widget/* — overlays never\n// import from widget/*, matching the ESLint `no-restricted-paths`\n// direction described in the plan.\n//\n// The events are intentionally void — subscribers read whatever state\n// they need from core/state.ts. Errors inside a subscriber are logged\n// to RN so a broken overlay doesn't take the widget down with it.\n\nimport { reportErrorToRN } from './bridge.js';\n\nexport type DataLifecycleEvent =\n | 'ohlcvReset'\n | 'ohlcvPrepended'\n | 'visibleRangeChanged';\n\ntype Listener = () => void;\n\nconst listeners: Record<DataLifecycleEvent, Listener[]> = {\n ohlcvReset: [],\n ohlcvPrepended: [],\n visibleRangeChanged: [],\n};\n\nexport function onDataLifecycle(\n event: DataLifecycleEvent,\n listener: Listener,\n): () => void {\n listeners[event].push(listener);\n return () => {\n const bucket = listeners[event];\n const idx = bucket.indexOf(listener);\n if (idx !== -1) {\n bucket.splice(idx, 1);\n }\n };\n}\n\nexport function notifyDataLifecycle(event: DataLifecycleEvent): void {\n const bucket = listeners[event];\n for (const listener of bucket) {\n try {\n listener();\n } catch (error) {\n reportErrorToRN(error);\n }\n }\n}\n\n/** Test-only: clear every listener across every event. */\nexport function _resetDataLifecycleForTests(): void {\n listeners.ohlcvReset = [];\n listeners.ohlcvPrepended = [];\n listeners.visibleRangeChanged = [];\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ChartConfig } from './types.js';
|
|
2
|
+
/** Callback invoked with each raw inbound payload delivered by the host. */
|
|
3
|
+
export type InboundTransportListener = (raw: unknown) => void;
|
|
4
|
+
/**
|
|
5
|
+
* The platform transport contract. An adapter owns the three things that
|
|
6
|
+
* differ between hosts: how outbound messages are delivered, how inbound
|
|
7
|
+
* messages are received (including any origin filtering), and where the
|
|
8
|
+
* inlined chart CONFIG comes from.
|
|
9
|
+
*/
|
|
10
|
+
export type ChartHostTransport = {
|
|
11
|
+
/** Deliver a serialized outbound message to the host. */
|
|
12
|
+
postMessage(serialized: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Subscribe to raw inbound messages from the host. The adapter is
|
|
15
|
+
* responsible for any transport-level filtering (e.g. origin checks). The
|
|
16
|
+
* returned function unsubscribes.
|
|
17
|
+
*/
|
|
18
|
+
subscribe(onMessage: InboundTransportListener): () => void;
|
|
19
|
+
/** Return the inlined chart configuration, or `undefined` when absent. */
|
|
20
|
+
getConfig(): ChartConfig | undefined;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Injects the host transport the engine should use. The mobile IIFE and the
|
|
24
|
+
* extension entry call this before bootstrap; tests may call it to provide a
|
|
25
|
+
* fake transport.
|
|
26
|
+
*
|
|
27
|
+
* @param host - The transport adapter to activate.
|
|
28
|
+
*/
|
|
29
|
+
export declare function setHostTransport(host: ChartHostTransport): void;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the active host transport, lazily defaulting to the React Native
|
|
32
|
+
* adapter so untouched consumers keep their current behavior.
|
|
33
|
+
*
|
|
34
|
+
* @returns The active host transport.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getHostTransport(): ChartHostTransport;
|
|
37
|
+
/** Test-only: clear the injected host so the next access re-defaults. */
|
|
38
|
+
export declare function _resetHostTransportForTests(): void;
|
|
39
|
+
//# sourceMappingURL=host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/core/host.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,4EAA4E;AAC5E,MAAM,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yDAAyD;IACzD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,wBAAwB,GAAG,MAAM,IAAI,CAAC;IAC3D,0EAA0E;IAC1E,SAAS,IAAI,WAAW,GAAG,SAAS,CAAC;CACtC,CAAC;AAIF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAGrD;AAED,yEAAyE;AACzE,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Platform host transport layer.
|
|
2
|
+
//
|
|
3
|
+
// Abstracts the *platform-specific* transport (how the WebView talks to its
|
|
4
|
+
// host and where it reads its inlined CONFIG) behind a single injectable
|
|
5
|
+
// interface. The engine's message bridge (core/bridge.ts) and config accessor
|
|
6
|
+
// (core/state.ts getConfig) depend on this interface instead of naming a
|
|
7
|
+
// concrete global, so the same engine can run inside a React Native WebView
|
|
8
|
+
// or a browser extension iframe.
|
|
9
|
+
//
|
|
10
|
+
// Adapters live in src/platform/*. The React Native adapter is the default so
|
|
11
|
+
// that existing consumers (and the mobile IIFE) keep their exact behavior
|
|
12
|
+
// without any wiring.
|
|
13
|
+
import { createReactNativeHost } from '../platform/reactNativeHost.js';
|
|
14
|
+
let activeHost = null;
|
|
15
|
+
/**
|
|
16
|
+
* Injects the host transport the engine should use. The mobile IIFE and the
|
|
17
|
+
* extension entry call this before bootstrap; tests may call it to provide a
|
|
18
|
+
* fake transport.
|
|
19
|
+
*
|
|
20
|
+
* @param host - The transport adapter to activate.
|
|
21
|
+
*/
|
|
22
|
+
export function setHostTransport(host) {
|
|
23
|
+
activeHost = host;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns the active host transport, lazily defaulting to the React Native
|
|
27
|
+
* adapter so untouched consumers keep their current behavior.
|
|
28
|
+
*
|
|
29
|
+
* @returns The active host transport.
|
|
30
|
+
*/
|
|
31
|
+
export function getHostTransport() {
|
|
32
|
+
activeHost ??= createReactNativeHost();
|
|
33
|
+
return activeHost;
|
|
34
|
+
}
|
|
35
|
+
/** Test-only: clear the injected host so the next access re-defaults. */
|
|
36
|
+
export function _resetHostTransportForTests() {
|
|
37
|
+
activeHost = null;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../src/core/host.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,8EAA8E;AAC9E,yEAAyE;AACzE,4EAA4E;AAC5E,iCAAiC;AACjC,EAAE;AACF,8EAA8E;AAC9E,0EAA0E;AAC1E,sBAAsB;AAEtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAyBvE,IAAI,UAAU,GAA8B,IAAI,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAwB;IACvD,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,UAAU,KAAK,qBAAqB,EAAE,CAAC;IACvC,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,2BAA2B;IACzC,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC","sourcesContent":["// Platform host transport layer.\n//\n// Abstracts the *platform-specific* transport (how the WebView talks to its\n// host and where it reads its inlined CONFIG) behind a single injectable\n// interface. The engine's message bridge (core/bridge.ts) and config accessor\n// (core/state.ts getConfig) depend on this interface instead of naming a\n// concrete global, so the same engine can run inside a React Native WebView\n// or a browser extension iframe.\n//\n// Adapters live in src/platform/*. The React Native adapter is the default so\n// that existing consumers (and the mobile IIFE) keep their exact behavior\n// without any wiring.\n\nimport { createReactNativeHost } from '../platform/reactNativeHost.js';\nimport type { ChartConfig } from './types.js';\n\n/** Callback invoked with each raw inbound payload delivered by the host. */\nexport type InboundTransportListener = (raw: unknown) => void;\n\n/**\n * The platform transport contract. An adapter owns the three things that\n * differ between hosts: how outbound messages are delivered, how inbound\n * messages are received (including any origin filtering), and where the\n * inlined chart CONFIG comes from.\n */\nexport type ChartHostTransport = {\n /** Deliver a serialized outbound message to the host. */\n postMessage(serialized: string): void;\n /**\n * Subscribe to raw inbound messages from the host. The adapter is\n * responsible for any transport-level filtering (e.g. origin checks). The\n * returned function unsubscribes.\n */\n subscribe(onMessage: InboundTransportListener): () => void;\n /** Return the inlined chart configuration, or `undefined` when absent. */\n getConfig(): ChartConfig | undefined;\n};\n\nlet activeHost: ChartHostTransport | null = null;\n\n/**\n * Injects the host transport the engine should use. The mobile IIFE and the\n * extension entry call this before bootstrap; tests may call it to provide a\n * fake transport.\n *\n * @param host - The transport adapter to activate.\n */\nexport function setHostTransport(host: ChartHostTransport): void {\n activeHost = host;\n}\n\n/**\n * Returns the active host transport, lazily defaulting to the React Native\n * adapter so untouched consumers keep their current behavior.\n *\n * @returns The active host transport.\n */\nexport function getHostTransport(): ChartHostTransport {\n activeHost ??= createReactNativeHost();\n return activeHost;\n}\n\n/** Test-only: clear the injected host so the next access re-defaults. */\nexport function _resetHostTransportForTests(): void {\n activeHost = null;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads the TradingView library script. Subsequent calls resolve immediately
|
|
3
|
+
* if the library is already loaded; rejected if a previous load failed.
|
|
4
|
+
* Concurrent calls while the script is still loading share the same promise.
|
|
5
|
+
*
|
|
6
|
+
* @param libraryUrl - Base URL that the `charting_library.js` file is appended to.
|
|
7
|
+
* @returns A promise that resolves once the library script has loaded.
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadTradingViewLibrary(libraryUrl: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Resets the cached in-flight library-load promise.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export declare function _resetLoadLibraryForTests(): void;
|
|
16
|
+
//# sourceMappingURL=loadLibrary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadLibrary.d.ts","sourceRoot":"","sources":["../../src/core/loadLibrary.ts"],"names":[],"mappings":"AAkBA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCxE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Loads the TradingView Advanced Charts library from window.CONFIG.libraryUrl
|
|
2
|
+
// by injecting a <script> tag into the document head.
|
|
3
|
+
//
|
|
4
|
+
// Mirrors legacy chartLogic.js `loadLibrary()` (lines ~5211-5237) but returns
|
|
5
|
+
// a Promise so callers can `await` library readiness in bootstrap.ts.
|
|
6
|
+
import { reportErrorToRN } from './bridge.js';
|
|
7
|
+
import { setLibraryLoaded, setLibraryError, isLibraryLoaded, getLibraryError, } from './state.js';
|
|
8
|
+
const CHARTING_LIBRARY_FILE = 'charting_library.js';
|
|
9
|
+
let inflightPromise = null;
|
|
10
|
+
/**
|
|
11
|
+
* Loads the TradingView library script. Subsequent calls resolve immediately
|
|
12
|
+
* if the library is already loaded; rejected if a previous load failed.
|
|
13
|
+
* Concurrent calls while the script is still loading share the same promise.
|
|
14
|
+
*
|
|
15
|
+
* @param libraryUrl - Base URL that the `charting_library.js` file is appended to.
|
|
16
|
+
* @returns A promise that resolves once the library script has loaded.
|
|
17
|
+
*/
|
|
18
|
+
export function loadTradingViewLibrary(libraryUrl) {
|
|
19
|
+
if (isLibraryLoaded()) {
|
|
20
|
+
return Promise.resolve();
|
|
21
|
+
}
|
|
22
|
+
const existingError = getLibraryError();
|
|
23
|
+
if (existingError) {
|
|
24
|
+
return Promise.reject(new Error(existingError));
|
|
25
|
+
}
|
|
26
|
+
if (inflightPromise) {
|
|
27
|
+
return inflightPromise;
|
|
28
|
+
}
|
|
29
|
+
inflightPromise = new Promise((resolve, reject) => {
|
|
30
|
+
const scriptUrl = libraryUrl + CHARTING_LIBRARY_FILE;
|
|
31
|
+
const script = document.createElement('script');
|
|
32
|
+
script.type = 'text/javascript';
|
|
33
|
+
script.src = scriptUrl;
|
|
34
|
+
script.onload = () => {
|
|
35
|
+
setLibraryLoaded(true);
|
|
36
|
+
inflightPromise = null;
|
|
37
|
+
resolve();
|
|
38
|
+
};
|
|
39
|
+
script.onerror = () => {
|
|
40
|
+
const message = `Failed to load TradingView library. URL: ${scriptUrl}`;
|
|
41
|
+
setLibraryError(message);
|
|
42
|
+
inflightPromise = null;
|
|
43
|
+
reportErrorToRN(message);
|
|
44
|
+
reject(new Error(message));
|
|
45
|
+
};
|
|
46
|
+
document.head.appendChild(script);
|
|
47
|
+
});
|
|
48
|
+
return inflightPromise;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resets the cached in-flight library-load promise.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export function _resetLoadLibraryForTests() {
|
|
56
|
+
inflightPromise = null;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=loadLibrary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadLibrary.js","sourceRoot":"","sources":["../../src/core/loadLibrary.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,sDAAsD;AACtD,EAAE;AACF,8EAA8E;AAC9E,sEAAsE;AAEtE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,qBAAqB,GAAG,qBAAqB,CAAC;AAEpD,IAAI,eAAe,GAAyB,IAAI,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,IAAI,eAAe,EAAE,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,aAAa,GAAG,eAAe,EAAE,CAAC;IACxC,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,eAAe,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtD,MAAM,SAAS,GAAG,UAAU,GAAG,qBAAqB,CAAC;QACrD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAChC,MAAM,CAAC,GAAG,GAAG,SAAS,CAAC;QACvB,MAAM,CAAC,MAAM,GAAG,GAAS,EAAE;YACzB,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACvB,eAAe,GAAG,IAAI,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,CAAC,OAAO,GAAG,GAAS,EAAE;YAC1B,MAAM,OAAO,GAAG,4CAA4C,SAAS,EAAE,CAAC;YACxE,eAAe,CAAC,OAAO,CAAC,CAAC;YACzB,eAAe,GAAG,IAAI,CAAC;YACvB,eAAe,CAAC,OAAO,CAAC,CAAC;YACzB,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,CAAC,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IACH,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,eAAe,GAAG,IAAI,CAAC;AACzB,CAAC","sourcesContent":["// Loads the TradingView Advanced Charts library from window.CONFIG.libraryUrl\n// by injecting a <script> tag into the document head.\n//\n// Mirrors legacy chartLogic.js `loadLibrary()` (lines ~5211-5237) but returns\n// a Promise so callers can `await` library readiness in bootstrap.ts.\n\nimport { reportErrorToRN } from './bridge.js';\nimport {\n setLibraryLoaded,\n setLibraryError,\n isLibraryLoaded,\n getLibraryError,\n} from './state.js';\n\nconst CHARTING_LIBRARY_FILE = 'charting_library.js';\n\nlet inflightPromise: Promise<void> | null = null;\n\n/**\n * Loads the TradingView library script. Subsequent calls resolve immediately\n * if the library is already loaded; rejected if a previous load failed.\n * Concurrent calls while the script is still loading share the same promise.\n *\n * @param libraryUrl - Base URL that the `charting_library.js` file is appended to.\n * @returns A promise that resolves once the library script has loaded.\n */\nexport function loadTradingViewLibrary(libraryUrl: string): Promise<void> {\n if (isLibraryLoaded()) {\n return Promise.resolve();\n }\n const existingError = getLibraryError();\n if (existingError) {\n return Promise.reject(new Error(existingError));\n }\n if (inflightPromise) {\n return inflightPromise;\n }\n\n inflightPromise = new Promise<void>((resolve, reject) => {\n const scriptUrl = libraryUrl + CHARTING_LIBRARY_FILE;\n const script = document.createElement('script');\n script.type = 'text/javascript';\n script.src = scriptUrl;\n script.onload = (): void => {\n setLibraryLoaded(true);\n inflightPromise = null;\n resolve();\n };\n script.onerror = (): void => {\n const message = `Failed to load TradingView library. URL: ${scriptUrl}`;\n setLibraryError(message);\n inflightPromise = null;\n reportErrorToRN(message);\n reject(new Error(message));\n };\n document.head.appendChild(script);\n });\n return inflightPromise;\n}\n\n/**\n * Resets the cached in-flight library-load promise.\n *\n * @internal\n */\nexport function _resetLoadLibraryForTests(): void {\n inflightPromise = null;\n}\n"]}
|