@forgecharts/sdk 1.2.5 → 1.2.6

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.
Files changed (33) hide show
  1. package/dist/index.d.ts +3 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +245 -50
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal.js +245 -50
  6. package/dist/internal.js.map +1 -1
  7. package/dist/licensing/ChartRuntimeResolver.d.ts +55 -24
  8. package/dist/licensing/ChartRuntimeResolver.d.ts.map +1 -1
  9. package/dist/licensing/LicenseManager.d.ts +17 -2
  10. package/dist/licensing/LicenseManager.d.ts.map +1 -1
  11. package/dist/licensing/__tests__/ChartRuntimeResolver.test.d.ts +3 -0
  12. package/dist/licensing/__tests__/ChartRuntimeResolver.test.d.ts.map +1 -1
  13. package/dist/licensing/__tests__/LicenseManager.test.d.ts +3 -0
  14. package/dist/licensing/__tests__/LicenseManager.test.d.ts.map +1 -1
  15. package/dist/licensing/__tests__/capabilityMatrix.test.d.ts +11 -0
  16. package/dist/licensing/__tests__/capabilityMatrix.test.d.ts.map +1 -0
  17. package/dist/licensing/capabilityMatrix.d.ts +77 -0
  18. package/dist/licensing/capabilityMatrix.d.ts.map +1 -0
  19. package/dist/licensing/licenseTypes.d.ts +29 -2
  20. package/dist/licensing/licenseTypes.d.ts.map +1 -1
  21. package/dist/node.d.ts +3 -1
  22. package/dist/node.d.ts.map +1 -1
  23. package/dist/react/index.js +234 -55
  24. package/dist/react/index.js.map +1 -1
  25. package/dist/react/internal.js +237 -57
  26. package/dist/react/internal.js.map +1 -1
  27. package/dist/react/shell/ManagedAppShell.d.ts.map +1 -1
  28. package/dist/trading/__tests__/managedCapabilities.test.d.ts +11 -0
  29. package/dist/trading/__tests__/managedCapabilities.test.d.ts.map +1 -0
  30. package/dist/trading/managed/ManagedTradingController.d.ts.map +1 -1
  31. package/dist/trading/managed/managedCapabilities.d.ts +45 -13
  32. package/dist/trading/managed/managedCapabilities.d.ts.map +1 -1
  33. package/package.json +1 -1
@@ -10606,8 +10606,126 @@ var TradingOverlayStore = class {
10606
10606
  }
10607
10607
  };
10608
10608
 
10609
+ // src/licensing/licenseTypes.ts
10610
+ var TRADING_FEATURE_KEYS = /* @__PURE__ */ new Set([
10611
+ "orderEntry",
10612
+ "managedTrading",
10613
+ "draggableOrders",
10614
+ "bracketOrders",
10615
+ "trailingStops",
10616
+ "positionsOverlay"
10617
+ ]);
10618
+
10619
+ // src/licensing/capabilityMatrix.ts
10620
+ var CURRENT_SCHEMA_VERSION = 2;
10621
+ var BASE_MATRIX = {
10622
+ core: {
10623
+ orderEntry: false,
10624
+ managedTrading: false,
10625
+ draggableOrders: false,
10626
+ bracketOrders: false,
10627
+ externalIngestion: true,
10628
+ symbolResolverRequired: true,
10629
+ tradingBridgeRequired: false,
10630
+ fullAppShell: false
10631
+ },
10632
+ trading: {
10633
+ orderEntry: true,
10634
+ managedTrading: false,
10635
+ draggableOrders: true,
10636
+ bracketOrders: true,
10637
+ externalIngestion: true,
10638
+ symbolResolverRequired: true,
10639
+ tradingBridgeRequired: true,
10640
+ fullAppShell: false
10641
+ },
10642
+ platform: {
10643
+ orderEntry: true,
10644
+ managedTrading: true,
10645
+ draggableOrders: true,
10646
+ bracketOrders: true,
10647
+ externalIngestion: false,
10648
+ symbolResolverRequired: false,
10649
+ tradingBridgeRequired: false,
10650
+ fullAppShell: true
10651
+ }
10652
+ };
10653
+ function getDefaultCapabilities(productTier, deploymentMode) {
10654
+ const base = BASE_MATRIX[productTier];
10655
+ if (deploymentMode === "unmanaged") {
10656
+ return { ...base, managedTrading: false, fullAppShell: false };
10657
+ }
10658
+ return { ...base };
10659
+ }
10660
+ function isValidCombination(deploymentMode, capabilities) {
10661
+ if (deploymentMode === "managed" && capabilities.fullAppShell === false) return false;
10662
+ return true;
10663
+ }
10664
+ function hasAnyTradingFeature(features) {
10665
+ if (!features) return false;
10666
+ for (const key of TRADING_FEATURE_KEYS) {
10667
+ if (features[key] === true) return true;
10668
+ }
10669
+ return false;
10670
+ }
10671
+ function inferProductTier(raw, mode) {
10672
+ if (mode === "managed") return "platform";
10673
+ if (raw.tier === "trading" || raw.tier === "dom") return "trading";
10674
+ if (hasAnyTradingFeature(raw.features)) return "trading";
10675
+ return "core";
10676
+ }
10677
+ function migrateLegacyPayload(raw) {
10678
+ const warnings = [];
10679
+ const mode = raw.deploymentMode ?? raw.mode ?? "unmanaged";
10680
+ let productTier = raw.productTier;
10681
+ if (!productTier) {
10682
+ productTier = inferProductTier(raw, mode);
10683
+ warnings.push(
10684
+ `License payload has no productTier \u2014 inferred '${productTier}' from mode='${mode}'` + (raw.tier ? ` and legacy tier='${raw.tier}'` : "") + "."
10685
+ );
10686
+ }
10687
+ let capabilities = raw.capabilities;
10688
+ if (!capabilities) {
10689
+ capabilities = getDefaultCapabilities(productTier, mode);
10690
+ warnings.push(`License payload has no capabilities \u2014 derived defaults for productTier='${productTier}', deploymentMode='${mode}'.`);
10691
+ }
10692
+ if (!raw.schemaVersion || raw.schemaVersion < CURRENT_SCHEMA_VERSION) {
10693
+ warnings.push(`License payload schemaVersion=${raw.schemaVersion ?? 1} is legacy \u2014 migrated to v${CURRENT_SCHEMA_VERSION}.`);
10694
+ }
10695
+ const payload = {
10696
+ licenseKey: raw.licenseKey ?? "",
10697
+ mode,
10698
+ deploymentMode: mode,
10699
+ productTier,
10700
+ capabilities,
10701
+ schemaVersion: CURRENT_SCHEMA_VERSION,
10702
+ ...raw.tier != null && { tier: raw.tier },
10703
+ ...raw.plan != null && { plan: raw.plan },
10704
+ ...raw.expires != null && { expires: raw.expires },
10705
+ ...raw.features != null && { features: raw.features }
10706
+ };
10707
+ return { payload, warnings };
10708
+ }
10709
+ function validateLicensePolicy(payload) {
10710
+ const warnings = [];
10711
+ const capabilities = payload.capabilities ?? getDefaultCapabilities(payload.productTier ?? "core", payload.mode);
10712
+ if (isValidCombination(payload.mode, capabilities)) {
10713
+ return { valid: true, normalized: { ...payload, capabilities }, warnings };
10714
+ }
10715
+ warnings.push(
10716
+ `Invalid license combination: deploymentMode='${payload.mode}' requires fullAppShell capability. Normalizing productTier to 'platform'.`
10717
+ );
10718
+ const normalizedCapabilities = getDefaultCapabilities("platform", payload.mode);
10719
+ return {
10720
+ valid: false,
10721
+ normalized: { ...payload, productTier: "platform", capabilities: normalizedCapabilities },
10722
+ warnings
10723
+ };
10724
+ }
10725
+
10609
10726
  // src/licensing/LicenseManager.ts
10610
10727
  var DEFAULT_MODE = "unmanaged";
10728
+ var DEFAULT_PRODUCT_TIER = "core";
10611
10729
  var DEFAULT_FEATURES = {};
10612
10730
  var LicenseManager = class _LicenseManager {
10613
10731
  static instance = null;
@@ -10670,22 +10788,42 @@ var LicenseManager = class _LicenseManager {
10670
10788
  if (!data.valid) {
10671
10789
  throw new Error(data.reason ?? "License invalid");
10672
10790
  }
10673
- const payload = {
10791
+ const raw = {
10674
10792
  licenseKey: data.licenseKey ?? key.trim().toUpperCase(),
10675
10793
  mode: data.mode ?? "unmanaged",
10794
+ ...data.tier != null && { tier: data.tier },
10795
+ ...data.productTier != null && { productTier: data.productTier },
10796
+ ...data.capabilities != null && { capabilities: data.capabilities },
10797
+ ...data.schemaVersion != null && { schemaVersion: data.schemaVersion },
10676
10798
  ...data.plan != null && { plan: data.plan },
10677
10799
  ...data.expiresAt != null && { expires: data.expiresAt },
10678
10800
  ...data.features != null && { features: data.features }
10679
10801
  };
10802
+ const payload = this.#applyPolicyPipeline(raw);
10680
10803
  this.payload = payload;
10681
10804
  this.notify();
10682
10805
  return payload;
10683
10806
  }
10684
- /** Load a pre-validated payload (e.g. from cache / localStorage). */
10807
+ /** Load a pre-validated payload (e.g. from cache / localStorage). Runs through the same migration + guardrail pipeline as `validateLicense`. */
10685
10808
  loadLicense(payload) {
10686
- this.payload = payload;
10809
+ this.payload = this.#applyPolicyPipeline(payload);
10687
10810
  this.notify();
10688
10811
  }
10812
+ /**
10813
+ * Runs a raw/legacy/partial payload through `migrateLegacyPayload` (fills
10814
+ * missing productTier/capabilities/schemaVersion, deterministically, with
10815
+ * warnings) then `validateLicensePolicy` (guardrail normalization).
10816
+ * Any warnings are surfaced via `console.warn` — callers never see a
10817
+ * payload without `capabilities`.
10818
+ */
10819
+ #applyPolicyPipeline(raw) {
10820
+ const { payload: migrated, warnings: migrationWarnings } = migrateLegacyPayload(raw);
10821
+ const { normalized, warnings: policyWarnings } = validateLicensePolicy(migrated);
10822
+ for (const warning of [...migrationWarnings, ...policyWarnings]) {
10823
+ console.warn(`[LicenseManager] ${warning}`);
10824
+ }
10825
+ return normalized;
10826
+ }
10689
10827
  /** Return the currently loaded payload, or null if none. */
10690
10828
  getLicense() {
10691
10829
  return this.payload;
@@ -10706,6 +10844,25 @@ var LicenseManager = class _LicenseManager {
10706
10844
  hasFeature(name) {
10707
10845
  return this.getFeatures()[name] === true;
10708
10846
  }
10847
+ /**
10848
+ * The entitlement tier driven by the active license.
10849
+ * Defaults to 'core' — the most restrictive tier — so no trading/managed
10850
+ * capability appears without a valid license.
10851
+ */
10852
+ getProductTier() {
10853
+ return this.payload?.productTier ?? DEFAULT_PRODUCT_TIER;
10854
+ }
10855
+ /**
10856
+ * The resolved capability set for the active license (or the safest
10857
+ * defaults — unmanaged/core — when no license is loaded yet).
10858
+ */
10859
+ getCapabilities() {
10860
+ return this.payload?.capabilities ?? getDefaultCapabilities(DEFAULT_PRODUCT_TIER, DEFAULT_MODE);
10861
+ }
10862
+ /** Check whether a specific capability is enabled on the active license. */
10863
+ hasCapability(name) {
10864
+ return this.getCapabilities()[name] === true;
10865
+ }
10709
10866
  /** Clear the current license state (returns to safe unmanaged defaults). */
10710
10867
  clear() {
10711
10868
  this.payload = null;
@@ -10713,16 +10870,6 @@ var LicenseManager = class _LicenseManager {
10713
10870
  }
10714
10871
  };
10715
10872
 
10716
- // src/licensing/licenseTypes.ts
10717
- var TRADING_FEATURE_KEYS = /* @__PURE__ */ new Set([
10718
- "orderEntry",
10719
- "managedTrading",
10720
- "draggableOrders",
10721
- "bracketOrders",
10722
- "trailingStops",
10723
- "positionsOverlay"
10724
- ]);
10725
-
10726
10873
  // src/licensing/ChartRuntimeResolver.ts
10727
10874
  var ChartRuntimeResolver = class _ChartRuntimeResolver {
10728
10875
  static instance = null;
@@ -10751,15 +10898,23 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
10751
10898
  /**
10752
10899
  * Check whether a specific platform feature is enabled.
10753
10900
  *
10754
- * Resolution:
10755
- * 1. If the feature is a trading feature and mode is unmanaged → false.
10901
+ * Resolution (capability-driven — never gated on deploymentMode alone):
10902
+ * 1. If the feature is a trading feature and productTier is 'core' → false.
10756
10903
  * 2. If the feature has an explicit flag in the license → use it.
10757
- * 3. Default: true for managed, true for non-trading unmanaged features.
10904
+ * 3. Default: true for platform/trading tier, true for non-trading features.
10758
10905
  */
10759
10906
  hasFeature(name) {
10760
- if (TRADING_FEATURE_KEYS.has(name) && this.isUnmanagedMode()) return false;
10907
+ if (TRADING_FEATURE_KEYS.has(name) && this.#isCoreTier()) return false;
10761
10908
  return this.#featureOrDefault(name, true);
10762
10909
  }
10910
+ /** The entitlement tier driven by the active license. */
10911
+ getProductTier() {
10912
+ return this.lm.getProductTier();
10913
+ }
10914
+ /** True when the active license's productTier is 'core' (no trading capabilities). */
10915
+ #isCoreTier() {
10916
+ return this.lm.getProductTier() === "core";
10917
+ }
10763
10918
  // ── Charting capability queries ─────────────────────────────────────────────
10764
10919
  /** Chart type selection (candlestick, bar, line, area, etc.). */
10765
10920
  canUseChartTypes() {
@@ -10813,50 +10968,77 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
10813
10968
  // ── Trading capability queries (managed-only by default) ────────────────────
10814
10969
  /**
10815
10970
  * Built-in order entry UI hooks.
10816
- * Requires managed mode unmanaged licenses cannot use order entry.
10971
+ * Ceiling driven by `productTier` (core = false, trading/platform = true) —
10972
+ * available in both Unmanaged Trading and Managed Platform. An explicit
10973
+ * `features.orderEntry` flag on the payload may only narrow below the
10974
+ * ceiling, never elevate above it.
10817
10975
  */
10818
10976
  canUseOrderEntry() {
10819
- if (this.isUnmanagedMode()) return false;
10820
- return this.#featureOrDefault("orderEntry", true);
10977
+ return this.#capabilityWithOverride("orderEntry");
10821
10978
  }
10822
10979
  /**
10823
10980
  * Managed trading service hooks (broker execution, position management).
10824
- * Requires managed mode.
10981
+ * Only true for Managed Platform — `managedTrading` is forced off under
10982
+ * unmanaged deployment regardless of tier (see capabilityMatrix).
10825
10983
  */
10826
10984
  canUseManagedTrading() {
10827
- if (this.isUnmanagedMode()) return false;
10828
- return this.#featureOrDefault("managedTrading", true);
10985
+ return this.#capabilityWithOverride("managedTrading");
10829
10986
  }
10830
10987
  /**
10831
10988
  * Drag-to-price order placement.
10832
- * Requires managed mode.
10989
+ * Ceiling driven by `productTier` — available in Unmanaged Trading and
10990
+ * Managed Platform.
10833
10991
  */
10834
10992
  canUseDraggableOrders() {
10835
- if (this.isUnmanagedMode()) return false;
10836
- return this.#featureOrDefault("draggableOrders", true);
10993
+ return this.#capabilityWithOverride("draggableOrders");
10837
10994
  }
10838
10995
  /**
10839
10996
  * Bracket / OCO order support.
10840
- * Requires managed mode.
10997
+ * Ceiling driven by `productTier` — available in Unmanaged Trading and
10998
+ * Managed Platform.
10841
10999
  */
10842
11000
  canUseBracketOrders() {
10843
- if (this.isUnmanagedMode()) return false;
10844
- return this.#featureOrDefault("bracketOrders", true);
11001
+ return this.#capabilityWithOverride("bracketOrders");
11002
+ }
11003
+ /**
11004
+ * Resolve one of the 4 override-able capability keys: the tier×mode matrix
11005
+ * sets the ceiling, an explicit `features[name]` flag may only narrow it.
11006
+ */
11007
+ #capabilityWithOverride(name) {
11008
+ const ceiling = this.lm.getCapabilities()[name];
11009
+ if (!ceiling) return false;
11010
+ const explicit = this.lm.getFeatures()[name];
11011
+ return explicit === void 0 ? true : explicit === true;
11012
+ }
11013
+ /**
11014
+ * Whether trading UI must route execution through host-supplied intent
11015
+ * handlers rather than a managed backend (Unmanaged Trading only).
11016
+ */
11017
+ requiresTradingBridge() {
11018
+ return this.lm.getCapabilities().tradingBridgeRequired;
11019
+ }
11020
+ /** Whether the host must supply an `ISymbolResolver` (unmanaged tiers). */
11021
+ requiresSymbolResolver() {
11022
+ return this.lm.getCapabilities().symbolResolverRequired;
11023
+ }
11024
+ /** Whether the full managed app shell should render (Managed Platform only). */
11025
+ hasFullAppShell() {
11026
+ return this.lm.getCapabilities().fullAppShell;
10845
11027
  }
10846
11028
  /**
10847
- * Trailing stop orders.
10848
- * Requires managed mode.
11029
+ * Trailing stop orders. Not part of the core capability set — gated on
11030
+ * productTier (not deploymentMode) to keep the two axes independent.
10849
11031
  */
10850
11032
  canUseTrailingStops() {
10851
- if (this.isUnmanagedMode()) return false;
11033
+ if (this.#isCoreTier()) return false;
10852
11034
  return this.#featureOrDefault("trailingStops", true);
10853
11035
  }
10854
11036
  /**
10855
- * Positions overlay on chart.
10856
- * Requires managed mode.
11037
+ * Positions overlay on chart. Not part of the core capability set — gated
11038
+ * on productTier (not deploymentMode) to keep the two axes independent.
10857
11039
  */
10858
11040
  canUsePositionsOverlay() {
10859
- if (this.isUnmanagedMode()) return false;
11041
+ if (this.#isCoreTier()) return false;
10860
11042
  return this.#featureOrDefault("positionsOverlay", true);
10861
11043
  }
10862
11044
  // ── Scripting capability queries ────────────────────────────────────────────
@@ -10896,11 +11078,11 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
10896
11078
  // ── Legacy / backward-compat ────────────────────────────────────────────────
10897
11079
  /**
10898
11080
  * External data ingestion APIs (e.g. pushing tick data from an external broker).
10899
- * Available in unmanaged mode only — in managed mode the built-in feed owns data.
11081
+ * Driven by `productTier`/`deploymentMode` via `capabilities.externalIngestion`
11082
+ * — true for core/trading, false for Managed Platform (the managed feed owns data).
10900
11083
  */
10901
11084
  canUseExternalIngestion() {
10902
- if (this.isUnmanagedMode()) return true;
10903
- return this.#featureOrDefault("unmanagedIngestion", false);
11085
+ return this.lm.getCapabilities().externalIngestion;
10904
11086
  }
10905
11087
  // ── UI render capability checks ──────────────────────────────────────────────
10906
11088
  // Component-level gates derived from the feature-level checks above.
@@ -10961,8 +11143,13 @@ var ChartRuntimeResolver = class _ChartRuntimeResolver {
10961
11143
  */
10962
11144
  getCapabilities() {
10963
11145
  return {
10964
- // ── Mode ────────────────────────────────────────────────────────────
11146
+ // ── Mode / tier ─────────────────────────────────────────────────────
10965
11147
  mode: this.lm.getMode(),
11148
+ productTier: this.lm.getProductTier(),
11149
+ // ── Capabilities (the 3-model matrix) ──────────────────────────────
11150
+ symbolResolverRequired: this.requiresSymbolResolver(),
11151
+ tradingBridgeRequired: this.requiresTradingBridge(),
11152
+ fullAppShell: this.hasFullAppShell(),
10966
11153
  // ── Charting features ───────────────────────────────────────────────
10967
11154
  chartTypes: this.canUseChartTypes(),
10968
11155
  drawingTools: this.canUseDrawingTools(),
@@ -11114,19 +11301,11 @@ function assertManagedMode() {
11114
11301
  );
11115
11302
  }
11116
11303
  }
11117
- function assertCanPlaceOrders() {
11118
- assertManagedMode();
11119
- if (!resolver2().canUseOrderEntry()) {
11120
- throw new Error(
11121
- "[ForgeCharts] Order entry is not enabled on the active license. Contact your license provider to enable the orderEntry feature."
11122
- );
11123
- }
11124
- }
11125
- function assertCanUseBrackets() {
11126
- assertCanPlaceOrders();
11304
+ function assertCanUseManagedBrackets() {
11305
+ assertCanUseManagedTrading();
11127
11306
  if (!resolver2().canUseBracketOrders()) {
11128
11307
  throw new Error(
11129
- "[ForgeCharts] Bracket orders are not enabled on the active license. Contact your license provider to enable the bracketOrders feature."
11308
+ "[ForgeCharts] Bracket orders are not enabled on the active license. Contact your license provider to enable the bracketOrders capability."
11130
11309
  );
11131
11310
  }
11132
11311
  }
@@ -11134,7 +11313,7 @@ function assertCanUseManagedTrading() {
11134
11313
  assertManagedMode();
11135
11314
  if (!resolver2().canUseManagedTrading()) {
11136
11315
  throw new Error(
11137
- "[ForgeCharts] Managed trading is not enabled on the active license. Contact your license provider to enable the managedTrading feature."
11316
+ "[ForgeCharts] Managed trading is not enabled on the active license. Contact your license provider to enable the managedTrading capability (requires productTier=platform)."
11138
11317
  );
11139
11318
  }
11140
11319
  }
@@ -11193,7 +11372,7 @@ var ManagedTradingController = class {
11193
11372
  * (provider event stream) and call _store.upsertOrder() as status changes.
11194
11373
  */
11195
11374
  async placeOrder(input) {
11196
- assertCanPlaceOrders();
11375
+ assertCanUseManagedTrading();
11197
11376
  if (!this._provider) notImplemented("placeOrder");
11198
11377
  const clientId = makeId();
11199
11378
  const overlayOrder = {
@@ -11231,7 +11410,7 @@ var ManagedTradingController = class {
11231
11410
  * visually fades out rather than disappearing instantly).
11232
11411
  */
11233
11412
  async cancelOrder(orderId) {
11234
- assertCanPlaceOrders();
11413
+ assertCanUseManagedTrading();
11235
11414
  if (!this._provider) notImplemented("cancelOrder");
11236
11415
  await this._provider.cancelOrder(orderId);
11237
11416
  const existing = this._store.getOrders().find((o) => o.id === orderId);
@@ -11251,7 +11430,7 @@ var ManagedTradingController = class {
11251
11430
  * before forwarding to the provider.
11252
11431
  */
11253
11432
  async modifyOrder(orderId, updates) {
11254
- assertCanPlaceOrders();
11433
+ assertCanUseManagedTrading();
11255
11434
  if (!this._provider) notImplemented("modifyOrder");
11256
11435
  const ack = await this._provider.modifyOrder(orderId, updates);
11257
11436
  const existing = this._store.getOrders().find((o) => o.id === orderId);
@@ -11278,7 +11457,7 @@ var ManagedTradingController = class {
11278
11457
  * users can adjust prices before confirming.
11279
11458
  */
11280
11459
  async placeBracketOrder(input) {
11281
- assertCanUseBrackets();
11460
+ assertCanUseManagedBrackets();
11282
11461
  if (!this._provider) notImplemented("placeBracketOrder");
11283
11462
  const groupId = input.entry.groupId ?? makeId();
11284
11463
  const ack = await this._provider.placeBracketOrder({