@forgecharts/sdk 1.1.23
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/package.json +50 -0
- package/src/__tests__/backwardCompatibility.test.ts +191 -0
- package/src/__tests__/candleInvariant.test.ts +500 -0
- package/src/__tests__/public-api-surface.ts +76 -0
- package/src/__tests__/timeframeBoundary.test.ts +583 -0
- package/src/api/DrawingManager.ts +188 -0
- package/src/api/EventBus.ts +53 -0
- package/src/api/IndicatorDAG.ts +389 -0
- package/src/api/IndicatorRegistry.ts +47 -0
- package/src/api/LayoutManager.ts +72 -0
- package/src/api/PaneManager.ts +129 -0
- package/src/api/ReferenceAPI.ts +195 -0
- package/src/api/TChart.ts +881 -0
- package/src/api/createChart.ts +43 -0
- package/src/api/drawing tools/fib gann menu/fibRetracement.ts +27 -0
- package/src/api/drawing tools/lines menu/crossLine.ts +21 -0
- package/src/api/drawing tools/lines menu/disjointChannel.ts +74 -0
- package/src/api/drawing tools/lines menu/extendedLine.ts +22 -0
- package/src/api/drawing tools/lines menu/flatTopBottom.ts +45 -0
- package/src/api/drawing tools/lines menu/horizontal.ts +24 -0
- package/src/api/drawing tools/lines menu/horizontalRay.ts +25 -0
- package/src/api/drawing tools/lines menu/infoLine.ts +127 -0
- package/src/api/drawing tools/lines menu/insidePitchfork.ts +21 -0
- package/src/api/drawing tools/lines menu/modifiedSchiffPitchfork.ts +18 -0
- package/src/api/drawing tools/lines menu/parallelChannel.ts +47 -0
- package/src/api/drawing tools/lines menu/pitchfork.ts +15 -0
- package/src/api/drawing tools/lines menu/ray.ts +28 -0
- package/src/api/drawing tools/lines menu/regressionTrend.ts +157 -0
- package/src/api/drawing tools/lines menu/schiffPitchfork.ts +18 -0
- package/src/api/drawing tools/lines menu/trendAngle.ts +64 -0
- package/src/api/drawing tools/lines menu/trendline.ts +16 -0
- package/src/api/drawing tools/lines menu/vertical.ts +16 -0
- package/src/api/drawing tools/pointers menu/crosshair.ts +17 -0
- package/src/api/drawing tools/pointers menu/cursor.ts +16 -0
- package/src/api/drawing tools/pointers menu/demonstration.ts +35 -0
- package/src/api/drawing tools/pointers menu/dot.ts +26 -0
- package/src/api/drawing tools/shapes menu/rectangle.ts +24 -0
- package/src/api/drawing tools/shapes menu/text.ts +30 -0
- package/src/api/drawingUtils.ts +82 -0
- package/src/core/CanvasLayer.ts +77 -0
- package/src/core/Chart.ts +917 -0
- package/src/core/CoordTransform.ts +282 -0
- package/src/core/Crosshair.ts +207 -0
- package/src/core/IndicatorEngine.ts +216 -0
- package/src/core/InteractionManager.ts +899 -0
- package/src/core/PriceScale.ts +133 -0
- package/src/core/Series.ts +132 -0
- package/src/core/TimeScale.ts +175 -0
- package/src/datafeed/DatafeedConnector.ts +300 -0
- package/src/engine/CandleEngine.ts +458 -0
- package/src/engine/__tests__/CandleEngine.test.ts +402 -0
- package/src/engine/candleInvariants.ts +172 -0
- package/src/engine/mergeUtils.ts +93 -0
- package/src/engine/timeframeUtils.ts +118 -0
- package/src/index.ts +190 -0
- package/src/internal.ts +41 -0
- package/src/licensing/ChartRuntimeResolver.ts +380 -0
- package/src/licensing/LicenseManager.ts +131 -0
- package/src/licensing/__tests__/ChartRuntimeResolver.test.ts +207 -0
- package/src/licensing/__tests__/LicenseManager.test.ts +180 -0
- package/src/licensing/licenseTypes.ts +19 -0
- package/src/pine/PineCompiler.ts +68 -0
- package/src/pine/diagnostics.ts +30 -0
- package/src/pine/index.ts +7 -0
- package/src/pine/pine-ast.ts +163 -0
- package/src/pine/pine-lexer.ts +265 -0
- package/src/pine/pine-parser.ts +439 -0
- package/src/pine/pine-transpiler.ts +301 -0
- package/src/pixi/LayerName.ts +35 -0
- package/src/pixi/PixiCandlestickRenderer.ts +125 -0
- package/src/pixi/PixiChart.ts +425 -0
- package/src/pixi/PixiCrosshairRenderer.ts +134 -0
- package/src/pixi/PixiDrawingRenderer.ts +121 -0
- package/src/pixi/PixiGridRenderer.ts +136 -0
- package/src/pixi/PixiLayerManager.ts +102 -0
- package/src/renderers/CandlestickRenderer.ts +130 -0
- package/src/renderers/HistogramRenderer.ts +63 -0
- package/src/renderers/LineRenderer.ts +77 -0
- package/src/theme/colors.ts +21 -0
- package/src/tools/barDivergenceCheck.ts +305 -0
- package/src/trading/TradingOverlayStore.ts +161 -0
- package/src/trading/UnmanagedIngestion.ts +156 -0
- package/src/trading/__tests__/ManagedTradingController.test.ts +338 -0
- package/src/trading/__tests__/TradingOverlayStore.test.ts +323 -0
- package/src/trading/__tests__/UnmanagedIngestion.test.ts +205 -0
- package/src/trading/managed/ManagedTradingController.ts +292 -0
- package/src/trading/managed/managedCapabilities.ts +98 -0
- package/src/trading/managed/managedTypes.ts +151 -0
- package/src/trading/tradingTypes.ts +135 -0
- package/src/tscript/TScriptIndicator.ts +54 -0
- package/src/tscript/ast.ts +105 -0
- package/src/tscript/lexer.ts +190 -0
- package/src/tscript/parser.ts +334 -0
- package/src/tscript/runtime.ts +525 -0
- package/src/tscript/series.ts +84 -0
- package/src/types/IChart.ts +56 -0
- package/src/types/IRenderer.ts +16 -0
- package/src/types/ISeries.ts +30 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +15 -0
- package/vitest.config.ts +25 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@forgecharts/sdk",
|
|
3
|
+
"version": "1.1.23",
|
|
4
|
+
"description": "ForgeCharts SDK — canvas-based charting engine, no third-party chart libraries",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/forgecharts/ForgeCharts.git"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"registry": "https://registry.npmjs.org",
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./internal": {
|
|
23
|
+
"types": "./dist/internal.d.ts",
|
|
24
|
+
"import": "./dist/internal.js",
|
|
25
|
+
"require": "./dist/internal.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup --no-dts && tsc --build --force tsconfig.json",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"lint": "eslint src --ext .ts",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"clean": "rimraf dist tsconfig.tsbuildinfo"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@forgecharts/shared": "*",
|
|
40
|
+
"@forgecharts/types": "*",
|
|
41
|
+
"@forgecharts/utils": "*",
|
|
42
|
+
"pixi.js": "^8.16.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"rimraf": "^5.0.10",
|
|
46
|
+
"tsup": "^8.2.4",
|
|
47
|
+
"typescript": "*",
|
|
48
|
+
"vitest": "^2.0.5"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backward compatibility — unit tests
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the introduction of licensing, unmanaged mode, and managed
|
|
5
|
+
* foundations does NOT break the existing SDK contract:
|
|
6
|
+
*
|
|
7
|
+
* 1. All expected exports exist and have the correct shape/type
|
|
8
|
+
* 2. CandleEngine works unchanged (no side-effects from new code)
|
|
9
|
+
* 3. Default mode is unmanaged (safe default — nothing breaks by default)
|
|
10
|
+
* 4. The RapidAPI datafeed path is unaffected (DatafeedConnector still exports)
|
|
11
|
+
* 5. New modules export correctly from the package root (index.ts)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
15
|
+
import { LicenseManager } from '../licensing/LicenseManager';
|
|
16
|
+
import { CandleEngine } from '../engine/CandleEngine';
|
|
17
|
+
import type { RawOHLCV } from '../engine/CandleEngine';
|
|
18
|
+
|
|
19
|
+
// ─── License cleanup ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
LicenseManager.getInstance().clear();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ─── Existing exports remain intact ──────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
describe('Backward compatibility — SDK exports', () => {
|
|
28
|
+
it('CandleEngine is exported', async () => {
|
|
29
|
+
const mod = await import('../engine/CandleEngine');
|
|
30
|
+
expect(mod.CandleEngine).toBeDefined();
|
|
31
|
+
expect(typeof mod.CandleEngine).toBe('function');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('timeframeToMs and getBucketStart are exported', async () => {
|
|
35
|
+
const mod = await import('../engine/timeframeUtils');
|
|
36
|
+
expect(typeof mod.timeframeToMs).toBe('function');
|
|
37
|
+
expect(typeof mod.getBucketStart).toBe('function');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('LicenseManager is exported', async () => {
|
|
41
|
+
const mod = await import('../licensing/LicenseManager');
|
|
42
|
+
expect(mod.LicenseManager).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('ChartRuntimeResolver is exported', async () => {
|
|
46
|
+
const mod = await import('../licensing/ChartRuntimeResolver');
|
|
47
|
+
expect(mod.ChartRuntimeResolver).toBeDefined();
|
|
48
|
+
expect(typeof mod.isManagedMode).toBe('function');
|
|
49
|
+
expect(typeof mod.isUnmanagedMode).toBe('function');
|
|
50
|
+
expect(typeof mod.canRenderOrderEntry).toBe('function');
|
|
51
|
+
expect(typeof mod.canRenderManagedTradingControls).toBe('function');
|
|
52
|
+
expect(typeof mod.canRenderExternalOverlayOnlyMode).toBe('function');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('TradingOverlayStore is exported', async () => {
|
|
56
|
+
const mod = await import('../trading/TradingOverlayStore');
|
|
57
|
+
expect(mod.TradingOverlayStore).toBeDefined();
|
|
58
|
+
expect(typeof mod.TradingOverlayStore).toBe('function');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('UnmanagedIngestion is exported', async () => {
|
|
62
|
+
const mod = await import('../trading/UnmanagedIngestion');
|
|
63
|
+
expect(mod.UnmanagedIngestion).toBeDefined();
|
|
64
|
+
expect(typeof mod.UnmanagedIngestion).toBe('function');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('ManagedTradingController is exported', async () => {
|
|
68
|
+
const mod = await import('../trading/managed/ManagedTradingController');
|
|
69
|
+
expect(mod.ManagedTradingController).toBeDefined();
|
|
70
|
+
expect(typeof mod.ManagedTradingController).toBe('function');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('DatafeedConnector is exported (default RapidAPI path intact)', async () => {
|
|
74
|
+
const mod = await import('../datafeed/DatafeedConnector');
|
|
75
|
+
expect(mod.DatafeedConnector).toBeDefined();
|
|
76
|
+
expect(typeof mod.DatafeedConnector).toBe('function');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ─── CandleEngine unchanged ───────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
describe('Backward compatibility — CandleEngine still works with no license', () => {
|
|
83
|
+
// T0 = 2024-07-03T10:00:00Z in seconds
|
|
84
|
+
const T0 = 1_719_997_200;
|
|
85
|
+
const T1 = T0 + 60;
|
|
86
|
+
|
|
87
|
+
function raw(time: number, close: number): RawOHLCV {
|
|
88
|
+
return { time, open: 100, high: close + 5, low: close - 5, close, volume: 1 };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
it('can be instantiated and used without any license', () => {
|
|
92
|
+
const engine = new CandleEngine();
|
|
93
|
+
expect(() => engine.initialize([], '1m')).not.toThrow();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('basic OHLCV update works — no licensing side-effects', () => {
|
|
97
|
+
const engine = new CandleEngine();
|
|
98
|
+
engine.initialize([], '1m');
|
|
99
|
+
|
|
100
|
+
engine.applyLiveUpdate(raw(T0, 100));
|
|
101
|
+
engine.applyLiveUpdate(raw(T0, 105));
|
|
102
|
+
engine.applyLiveUpdate(raw(T1, 110));
|
|
103
|
+
|
|
104
|
+
expect(engine.getBars()).toHaveLength(2);
|
|
105
|
+
expect(engine.getBars()[0]!.close).toBe(105);
|
|
106
|
+
expect(engine.getBars()[1]!.close).toBe(110);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('initialize() resets state completely', () => {
|
|
110
|
+
const engine = new CandleEngine();
|
|
111
|
+
engine.initialize([], '1m');
|
|
112
|
+
engine.applyLiveUpdate(raw(T0, 100));
|
|
113
|
+
engine.initialize([], '5m');
|
|
114
|
+
expect(engine.getBars()).toHaveLength(0);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ─── Default license mode — safe unmanaged defaults ──────────────────────────
|
|
119
|
+
|
|
120
|
+
describe('Backward compatibility — default mode is unmanaged', () => {
|
|
121
|
+
it('LicenseManager.getMode() returns unmanaged with no license', () => {
|
|
122
|
+
const lm = LicenseManager.getInstance();
|
|
123
|
+
expect(lm.getMode()).toBe('unmanaged');
|
|
124
|
+
expect(lm.getLicense()).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('isUnmanagedMode() is true with no license', async () => {
|
|
128
|
+
const { isUnmanagedMode } = await import('../licensing/ChartRuntimeResolver');
|
|
129
|
+
expect(isUnmanagedMode()).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('isManagedMode() is false with no license', async () => {
|
|
133
|
+
const { isManagedMode } = await import('../licensing/ChartRuntimeResolver');
|
|
134
|
+
expect(isManagedMode()).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('canRenderCandles() is true with no license', async () => {
|
|
138
|
+
const { canRenderCandles } = await import('../licensing/ChartRuntimeResolver');
|
|
139
|
+
expect(canRenderCandles()).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('canRenderorderEntry() is false with no license', async () => {
|
|
143
|
+
const { canRenderOrderEntry } = await import('../licensing/ChartRuntimeResolver');
|
|
144
|
+
expect(canRenderOrderEntry()).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('canRenderManagedTradingControls() is false with no license', async () => {
|
|
148
|
+
const { canRenderManagedTradingControls } = await import('../licensing/ChartRuntimeResolver');
|
|
149
|
+
expect(canRenderManagedTradingControls()).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ─── Unmanaged ingestion is additive — does not replace the existing datafeed ─
|
|
154
|
+
|
|
155
|
+
describe('Backward compatibility — unmanaged ingestion is opt-in and additive', () => {
|
|
156
|
+
it('UnmanagedIngestion can be instantiated without affecting CandleEngine state', async () => {
|
|
157
|
+
const standaloneEngine = new CandleEngine();
|
|
158
|
+
standaloneEngine.initialize([], '1m');
|
|
159
|
+
|
|
160
|
+
// Creating an UnmanagedIngestion constructs its OWN internal CandleEngine
|
|
161
|
+
const { UnmanagedIngestion } = await import('../trading/UnmanagedIngestion');
|
|
162
|
+
const ing = new UnmanagedIngestion('1m');
|
|
163
|
+
|
|
164
|
+
// The standalone engine is still clean
|
|
165
|
+
expect(standaloneEngine.getBars()).toHaveLength(0);
|
|
166
|
+
// The ingestion engine also starts empty
|
|
167
|
+
expect(ing.getBars()).toHaveLength(0);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('DatafeedConnector can still be constructed independently', async () => {
|
|
171
|
+
const { DatafeedConnector } = await import('../datafeed/DatafeedConnector');
|
|
172
|
+
const mockDatafeed = {
|
|
173
|
+
getHistoricalBars: () => Promise.resolve({ bars: [], noData: true }),
|
|
174
|
+
subscribeBars: () => {},
|
|
175
|
+
unsubscribeBars: () => {},
|
|
176
|
+
};
|
|
177
|
+
const mockSeries = {
|
|
178
|
+
setData: () => {},
|
|
179
|
+
update: () => {},
|
|
180
|
+
data: () => [],
|
|
181
|
+
applyOptions: () => {},
|
|
182
|
+
options: () => ({} as never),
|
|
183
|
+
};
|
|
184
|
+
const mockCallbacks = {
|
|
185
|
+
onLoading: () => {},
|
|
186
|
+
onLoaded: () => {},
|
|
187
|
+
onError: () => {},
|
|
188
|
+
};
|
|
189
|
+
expect(() => new DatafeedConnector(mockDatafeed, mockSeries, mockCallbacks)).not.toThrow();
|
|
190
|
+
});
|
|
191
|
+
});
|