@itwin/ecschema-rpcinterface-tests 5.6.0-dev.8 → 5.7.0-dev.1

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.
@@ -44957,7 +44957,7 @@ class TextStyleSettings {
44957
44957
  listMarker;
44958
44958
  /** The frame settings of the [[TextAnnotation]]. */
44959
44959
  frame;
44960
- /** The margins to surround the document content. */
44960
+ /** Multiplier used to calculate the margins to surround the document content. */
44961
44961
  margins;
44962
44962
  /** The alignment of the text content. */
44963
44963
  justification;
@@ -63738,6 +63738,18 @@ class JsonParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_2__.AbstractPa
63738
63738
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'uomSeparator' attribute. It should be of type 'string'.`);
63739
63739
  if (undefined !== jsonObj.scientificType && typeof (jsonObj.scientificType) !== "string")
63740
63740
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'scientificType' attribute. It should be of type 'string'.`);
63741
+ if (undefined !== jsonObj.ratioType && typeof (jsonObj.ratioType) !== "string")
63742
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioType' attribute. It should be of type 'string'.`);
63743
+ if (undefined !== jsonObj.ratioSeparator && typeof (jsonObj.ratioSeparator) !== "string")
63744
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioSeparator' attribute. It should be of type 'string'.`);
63745
+ if (undefined !== jsonObj.ratioFormatType && typeof (jsonObj.ratioFormatType) !== "string")
63746
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'ratioFormatType' attribute. It should be of type 'string'.`);
63747
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
63748
+ if (jsonObj.ratioType !== undefined || jsonObj.ratioSeparator !== undefined || jsonObj.ratioFormatType !== undefined) {
63749
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
63750
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has ratio properties that require EC version 3.3 or newer.`);
63751
+ }
63752
+ }
63741
63753
  if (undefined !== jsonObj.stationOffsetSize && typeof (jsonObj.stationOffsetSize) !== "number")
63742
63754
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_0__.ECSchemaStatus.InvalidECJson, `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be of type 'number'.`);
63743
63755
  if (undefined !== jsonObj.stationSeparator && typeof (jsonObj.stationSeparator) !== "string")
@@ -64451,6 +64463,15 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
64451
64463
  const thousandSeparator = this.getOptionalAttribute(xmlElement, "thousandSeparator");
64452
64464
  const uomSeparator = this.getOptionalAttribute(xmlElement, "uomSeparator");
64453
64465
  const scientificType = this.getOptionalAttribute(xmlElement, "scientificType");
64466
+ const ratioType = this.getOptionalAttribute(xmlElement, "ratioType");
64467
+ const ratioSeparator = this.getOptionalAttribute(xmlElement, "ratioSeparator");
64468
+ const ratioFormatType = this.getOptionalAttribute(xmlElement, "ratioFormatType");
64469
+ // Validate EC version if ratio properties exist - they require EC version 3.3+
64470
+ if (ratioType !== undefined || ratioSeparator !== undefined || ratioFormatType !== undefined) {
64471
+ if (this._ecSpecVersion === undefined || this._ecSpecVersion.readVersion < 3 || (this._ecSpecVersion.readVersion === 3 && this._ecSpecVersion.writeVersion < 3)) {
64472
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidSchemaXML, `The Format ${this._currentItemFullName} has ratio properties that require EC version 3.3 or newer.`);
64473
+ }
64474
+ }
64454
64475
  const stationOffsetSize = this.getOptionalIntAttribute(xmlElement, "stationOffsetSize", `The Format ${this._currentItemFullName} has an invalid 'stationOffsetSize' attribute. It should be a numeric value.`);
64455
64476
  const stationSeparator = this.getOptionalAttribute(xmlElement, "stationSeparator");
64456
64477
  let composite;
@@ -64493,6 +64514,9 @@ class XmlParser extends _AbstractParser__WEBPACK_IMPORTED_MODULE_5__.AbstractPar
64493
64514
  thousandSeparator,
64494
64515
  uomSeparator,
64495
64516
  scientificType,
64517
+ ratioType,
64518
+ ratioSeparator,
64519
+ ratioFormatType,
64496
64520
  stationOffsetSize,
64497
64521
  stationSeparator,
64498
64522
  composite,
@@ -66236,7 +66260,7 @@ class SchemaFormatsProvider {
66236
66260
  // If no matching presentation format was found, use persistence unit format if it matches unit system.
66237
66261
  const persistenceUnit = await kindOfQuantity.persistenceUnit;
66238
66262
  const persistenceUnitSystem = await persistenceUnit?.unitSystem;
66239
- if (persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
66263
+ if (persistenceUnit && persistenceUnitSystem && unitSystemMatchers.some((matcher) => matcher(persistenceUnitSystem))) {
66240
66264
  this._formatsRetrieved.add(itemKey.fullName);
66241
66265
  const props = getPersistenceUnitFormatProps(persistenceUnit);
66242
66266
  return this.convertToFormatDefinition(props, kindOfQuantity);
@@ -68833,6 +68857,7 @@ __webpack_require__.r(__webpack_exports__);
68833
68857
 
68834
68858
 
68835
68859
 
68860
+ const loggingCategory = "ECClass";
68836
68861
  /**
68837
68862
  * A common abstract class for all of the ECClass types.
68838
68863
  * @public @preview
@@ -68887,7 +68912,13 @@ class ECClass extends _SchemaItem__WEBPACK_IMPORTED_MODULE_9__.SchemaItem {
68887
68912
  async getDerivedClasses() {
68888
68913
  const derivedClasses = [];
68889
68914
  for (const derivedClassKey of this.schema.context.classHierarchy.getDerivedClassKeys(this.key)) {
68890
- const derivedClass = await this.schema.context.getSchemaItem(derivedClassKey, ECClass);
68915
+ let derivedClass = await this.schema.getItem(derivedClassKey.name, ECClass); // if the derived class is in the same schema this will get it without going to the context
68916
+ if (derivedClass) {
68917
+ derivedClasses.push(derivedClass);
68918
+ continue;
68919
+ }
68920
+ _itwin_core_bentley__WEBPACK_IMPORTED_MODULE_0__.Logger.logInfo(loggingCategory, `Derived class ${derivedClassKey.name} not found in schema ${this.schema.name}, looking in schema context.`);
68921
+ derivedClass = await this.schema.context.getSchemaItem(derivedClassKey, ECClass);
68891
68922
  if (derivedClass)
68892
68923
  derivedClasses.push(derivedClass);
68893
68924
  }
@@ -69275,18 +69306,56 @@ class ECClass extends _SchemaItem__WEBPACK_IMPORTED_MODULE_9__.SchemaItem {
69275
69306
  */
69276
69307
  async *getAllBaseClasses() {
69277
69308
  for (const baseClassKey of this.schema.context.classHierarchy.getBaseClassKeys(this.key)) {
69278
- const baseClass = await this.schema.lookupItem(baseClassKey, ECClass);
69309
+ const baseClass = await this.getClassFromReferencesRecursively(baseClassKey); // Search in schema ref tree all the way to the top
69279
69310
  if (baseClass)
69280
69311
  yield baseClass;
69281
69312
  }
69282
69313
  }
69314
+ /**
69315
+ * gets a class from this schema or its references recursively using the item key
69316
+ * @param itemKey
69317
+ * @returns ECClass if it could be found, undefined otherwise
69318
+ * @internal
69319
+ */
69320
+ async getClassFromReferencesRecursively(itemKey) {
69321
+ const schemaList = [this.schema];
69322
+ while (schemaList.length > 0) {
69323
+ const currentSchema = schemaList.shift();
69324
+ if (currentSchema.schemaKey.compareByName(itemKey.schemaKey)) {
69325
+ const baseClass = await currentSchema.getItem(itemKey.name, ECClass);
69326
+ schemaList.splice(0); // clear the list
69327
+ return baseClass;
69328
+ }
69329
+ schemaList.push(...currentSchema.references);
69330
+ }
69331
+ return undefined;
69332
+ }
69283
69333
  *getAllBaseClassesSync() {
69284
69334
  for (const baseClassKey of this.schema.context.classHierarchy.getBaseClassKeys(this.key)) {
69285
- const baseClass = this.schema.lookupItemSync(baseClassKey, ECClass);
69335
+ const baseClass = this.getClassFromReferencesRecursivelySync(baseClassKey); // Search in schema ref tree all the way to the top
69286
69336
  if (baseClass)
69287
69337
  yield baseClass;
69288
69338
  }
69289
69339
  }
69340
+ /**
69341
+ * gets a class from this schema or its references recursively using the item key synchronously
69342
+ * @param itemKey
69343
+ * @returns ECClass if it could be found, undefined otherwise
69344
+ * @internal
69345
+ */
69346
+ getClassFromReferencesRecursivelySync(itemKey) {
69347
+ const schemaList = [this.schema];
69348
+ while (schemaList.length > 0) {
69349
+ const currentSchema = schemaList.shift();
69350
+ if (currentSchema.schemaKey.compareByName(itemKey.schemaKey)) {
69351
+ const baseClass = currentSchema.getItemSync(itemKey.name, ECClass);
69352
+ schemaList.splice(0); // clear the list
69353
+ return baseClass;
69354
+ }
69355
+ schemaList.push(...currentSchema.references);
69356
+ }
69357
+ return undefined;
69358
+ }
69290
69359
  /**
69291
69360
  *
69292
69361
  * @param cache
@@ -70422,6 +70491,9 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70422
70491
  get stationSeparator() { return this._base.stationSeparator; }
70423
70492
  get stationOffsetSize() { return this._base.stationOffsetSize; }
70424
70493
  get stationBaseFactor() { return this._base.stationBaseFactor; }
70494
+ get ratioType() { return this._base.ratioType; }
70495
+ get ratioSeparator() { return this._base.ratioSeparator; }
70496
+ get ratioFormatType() { return this._base.ratioFormatType; }
70425
70497
  get formatTraits() { return this._base.formatTraits; }
70426
70498
  get spacer() { return this._base.spacer; }
70427
70499
  get includeZero() { return this._base.includeZero; }
@@ -70443,10 +70515,13 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70443
70515
  addUnit(unit, label) {
70444
70516
  if (undefined === this._units)
70445
70517
  this._units = [];
70446
- else { // Validate that a duplicate is not added.
70447
- for (const existingUnit of this._units) {
70448
- if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
70449
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has duplicate units, '${unit.fullName}'.`); // TODO: Validation - this should be a validation error not a hard failure.
70518
+ else {
70519
+ const isDuplicateAllowed = this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio;
70520
+ if (!isDuplicateAllowed) {
70521
+ for (const existingUnit of this._units) {
70522
+ if (unit.fullName.toLowerCase() === existingUnit[0].fullName.toLowerCase())
70523
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has duplicate units, '${unit.fullName}'.`); // TODO: Validation - this should be a validation error not a hard failure.
70524
+ }
70450
70525
  }
70451
70526
  }
70452
70527
  this._units.push([unit, label]);
@@ -70471,37 +70546,87 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70471
70546
  if (formatProps.composite.units.length <= 0 || formatProps.composite.units.length > 4)
70472
70547
  throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has an invalid 'Composite' attribute. It should have 1-4 units.`);
70473
70548
  }
70549
+ // For Ratio formats: validate that composite is provided
70550
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
70551
+ const hasComposite = undefined !== formatProps.composite && formatProps.composite.units.length > 0;
70552
+ if (!hasComposite) {
70553
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} is 'Ratio' type and must have 'composite' units.`);
70554
+ }
70555
+ }
70474
70556
  }
70475
70557
  fromJSONSync(formatProps) {
70476
70558
  super.fromJSONSync(formatProps);
70477
70559
  this.typecheck(formatProps);
70478
- if (undefined === formatProps.composite)
70479
- return;
70480
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
70481
- for (const unit of formatProps.composite.units) {
70482
- const newUnit = this.schema.lookupItemSync(unit.name);
70483
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70484
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70485
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
70486
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70487
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
70488
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70560
+ // Process composite units
70561
+ if (undefined !== formatProps.composite) {
70562
+ for (const unit of formatProps.composite.units) {
70563
+ const newUnit = this.schema.lookupItemSync(unit.name);
70564
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70565
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70566
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
70567
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
70568
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
70569
+ this.addUnit(lazyUnit, unit.label);
70570
+ }
70571
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
70572
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
70573
+ const unit1Item = this.schema.lookupItemSync(this._units[0][0].fullName);
70574
+ const unit2Item = this.schema.lookupItemSync(this._units[1][0].fullName);
70575
+ if (!unit1Item || !unit2Item || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit1Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit1Item)) || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit2Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit2Item)))
70576
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
70577
+ const getPhenomenon = (unitItem) => {
70578
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
70579
+ return unitItem.phenomenon;
70580
+ }
70581
+ const invertsUnit = unitItem.invertsUnit;
70582
+ if (invertsUnit) {
70583
+ const resolvedUnit = this.schema.lookupItemSync(invertsUnit.fullName);
70584
+ return resolvedUnit && _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(resolvedUnit) ? resolvedUnit.phenomenon : undefined;
70585
+ }
70586
+ return undefined;
70587
+ };
70588
+ const phenomenon1 = getPhenomenon(unit1Item);
70589
+ const phenomenon2 = getPhenomenon(unit2Item);
70590
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
70591
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has 2-unit composite with different phenomena. Both units must have the same phenomenon.`);
70592
+ }
70593
+ }
70489
70594
  }
70490
70595
  }
70491
70596
  async fromJSON(formatProps) {
70492
70597
  await super.fromJSON(formatProps);
70493
70598
  this.typecheck(formatProps);
70494
- if (undefined === formatProps.composite)
70495
- return;
70496
- // Units are separated from the rest of the deserialization because of the need to have separate sync and async implementation
70497
- for (const unit of formatProps.composite.units) {
70498
- const newUnit = await this.schema.lookupItem(unit.name);
70499
- if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70500
- throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70501
- if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit))
70502
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70503
- else if (_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit))
70504
- this.addUnit(new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit), unit.label);
70599
+ // Process composite units
70600
+ if (undefined !== formatProps.composite) {
70601
+ for (const unit of formatProps.composite.units) {
70602
+ const newUnit = await this.schema.lookupItem(unit.name);
70603
+ if (undefined === newUnit || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(newUnit)))
70604
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, ``);
70605
+ const lazyUnit = _Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(newUnit)
70606
+ ? new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit)
70607
+ : new _DelayedPromise__WEBPACK_IMPORTED_MODULE_7__.DelayedPromiseWithProps(newUnit.key, async () => newUnit);
70608
+ this.addUnit(lazyUnit, unit.label);
70609
+ }
70610
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
70611
+ if (this.type === _itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && this._units && this._units.length === 2) {
70612
+ const unit1Item = await this.schema.lookupItem(this._units[0][0].fullName);
70613
+ const unit2Item = await this.schema.lookupItem(this._units[1][0].fullName);
70614
+ if (!unit1Item || !unit2Item || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit1Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit1Item)) || (!_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unit2Item) && !_InvertedUnit__WEBPACK_IMPORTED_MODULE_4__.InvertedUnit.isInvertedUnit(unit2Item)))
70615
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has invalid units.`);
70616
+ // Helper to extract phenomenon from Unit or InvertedUnit
70617
+ const getPhenomenon = async (unitItem) => {
70618
+ if (_Unit__WEBPACK_IMPORTED_MODULE_6__.Unit.isUnit(unitItem)) {
70619
+ return unitItem.phenomenon;
70620
+ }
70621
+ const invertsUnit = await unitItem.invertsUnit;
70622
+ return invertsUnit ? invertsUnit.phenomenon : undefined;
70623
+ };
70624
+ const phenomenon1 = await getPhenomenon(unit1Item);
70625
+ const phenomenon2 = await getPhenomenon(unit2Item);
70626
+ if (phenomenon1?.fullName !== phenomenon2?.fullName) {
70627
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_2__.ECSchemaStatus.InvalidECJson, `The Format ${this.fullName} has 2-unit composite with different phenomena. Both units must have the same phenomenon.`);
70628
+ }
70629
+ }
70505
70630
  }
70506
70631
  }
70507
70632
  /**
@@ -70538,6 +70663,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70538
70663
  if (" " !== this.stationSeparator)
70539
70664
  schemaJson.stationSeparator = this.stationSeparator;
70540
70665
  }
70666
+ // Only include ratio properties for EC version 3.3+
70667
+ if (_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio === this.type && this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
70668
+ if (undefined !== this.ratioType)
70669
+ schemaJson.ratioType = this.ratioType;
70670
+ if (undefined !== this.ratioSeparator)
70671
+ schemaJson.ratioSeparator = this.ratioSeparator;
70672
+ if (undefined !== this.ratioFormatType)
70673
+ schemaJson.ratioFormatType = this.ratioFormatType;
70674
+ }
70541
70675
  if (undefined === this.units)
70542
70676
  return schemaJson;
70543
70677
  schemaJson.composite = {};
@@ -70569,6 +70703,15 @@ class Format extends _SchemaItem__WEBPACK_IMPORTED_MODULE_5__.SchemaItem {
70569
70703
  itemElement.setAttribute("minWidth", this.minWidth.toString());
70570
70704
  if (undefined !== this.scientificType)
70571
70705
  itemElement.setAttribute("scientificType", this.scientificType);
70706
+ // Only include ratio properties for EC version 3.3+
70707
+ if (this.schema.originalECSpecMajorVersion === 3 && this.schema.originalECSpecMinorVersion !== undefined && this.schema.originalECSpecMinorVersion >= 3) {
70708
+ if (undefined !== this.ratioType)
70709
+ itemElement.setAttribute("ratioType", this.ratioType);
70710
+ if (undefined !== this.ratioSeparator)
70711
+ itemElement.setAttribute("ratioSeparator", this.ratioSeparator);
70712
+ if (undefined !== this.ratioFormatType)
70713
+ itemElement.setAttribute("ratioFormatType", this.ratioFormatType);
70714
+ }
70572
70715
  if (undefined !== this.stationOffsetSize)
70573
70716
  itemElement.setAttribute("stationOffsetSize", this.stationOffsetSize.toString());
70574
70717
  const formatTraits = (0,_itwin_core_quantity__WEBPACK_IMPORTED_MODULE_3__.formatTraitsToArray)(this.formatTraits);
@@ -71315,6 +71458,9 @@ class OverrideFormat {
71315
71458
  get type() { return this.parent.type; }
71316
71459
  get minWidth() { return this.parent.minWidth; }
71317
71460
  get scientificType() { return this.parent.scientificType; }
71461
+ get ratioType() { return this.parent.ratioType; }
71462
+ get ratioSeparator() { return this.parent.ratioSeparator; }
71463
+ get ratioFormatType() { return this.parent.ratioFormatType; }
71318
71464
  get showSignOption() { return this.parent.showSignOption; }
71319
71465
  get decimalSeparator() { return this.parent.decimalSeparator; }
71320
71466
  get thousandSeparator() { return this.parent.thousandSeparator; }
@@ -71339,7 +71485,7 @@ class OverrideFormat {
71339
71485
  for (const [unit, unitLabel] of this._units) {
71340
71486
  const unitSchema = koqSchema.context.getSchemaSync(unit.schemaKey);
71341
71487
  if (unitSchema === undefined)
71342
- throw new _Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaStatus.InvalidECJson, `The unit schema ${unit.schemaKey} is not found in the context.`);
71488
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaError(_Exception__WEBPACK_IMPORTED_MODULE_3__.ECSchemaStatus.InvalidECJson, `The unit schema ${unit.schemaKey.toString()} is not found in the context.`);
71343
71489
  fullName += "[";
71344
71490
  fullName += _Deserialization_XmlSerializationUtils__WEBPACK_IMPORTED_MODULE_0__.XmlSerializationUtils.createXmlTypedName(koqSchema, unitSchema, unit.name);
71345
71491
  if (unitLabel !== undefined)
@@ -108185,7 +108331,9 @@ function createMeshArgs(mesh) {
108185
108331
  if (!mesh.triangles || mesh.triangles.isEmpty || mesh.points.length === 0)
108186
108332
  return undefined;
108187
108333
  const texture = mesh.displayParams.textureMapping?.texture;
108188
- const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams } : undefined;
108334
+ const useConstantLod = mesh.displayParams.textureMapping?.params?.useConstantLod;
108335
+ const constantLodParams = mesh.displayParams.textureMapping?.params?.constantLodParams;
108336
+ const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams, useConstantLod, constantLodParams } : undefined;
108189
108337
  const colors = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorIndex();
108190
108338
  mesh.colorMap.toColorIndex(colors, mesh.colors);
108191
108339
  const features = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FeatureIndex();
@@ -149964,7 +150112,7 @@ function readPnts(stream, dataOffset, pnts) {
149964
150112
  }
149965
150113
  async function decodeDracoPointCloud(buf) {
149966
150114
  try {
149967
- const dracoLoader = (await __webpack_require__.e(/*! import() */ "vendors-common_temp_node_modules_pnpm_loaders_gl_draco_4_3_4_node_modules_loaders_gl_draco_di-40e622").then(__webpack_require__.bind(__webpack_require__, /*! @loaders.gl/draco */ "../../common/temp/node_modules/.pnpm/@loaders.gl+draco@4.3.4/node_modules/@loaders.gl/draco/dist/index.js"))).DracoLoader;
150115
+ const dracoLoader = (await __webpack_require__.e(/*! import() */ "vendors-common_temp_node_modules_pnpm_loaders_gl_draco_4_3_4__loaders_gl_core_4_3_4_node_modu-4c1fc9").then(__webpack_require__.bind(__webpack_require__, /*! @loaders.gl/draco */ "../../common/temp/node_modules/.pnpm/@loaders.gl+draco@4.3.4_@loaders.gl+core@4.3.4/node_modules/@loaders.gl/draco/dist/index.js"))).DracoLoader;
149968
150116
  const mesh = await dracoLoader.parse(buf, {});
149969
150117
  if (mesh.topology !== "point-list")
149970
150118
  return undefined;
@@ -161403,14 +161551,47 @@ class GltfReader {
161403
161551
  }
161404
161552
  }
161405
161553
  createDisplayParams(material, hasBakedLighting, isPointPrimitive = false) {
161554
+ let constantLodParamProps;
161555
+ let normalMapUseConstantLod = false;
161556
+ if (!(0,_common_gltf_GltfSchema__WEBPACK_IMPORTED_MODULE_12__.isGltf1Material)(material)) {
161557
+ // NOTE: EXT_textureInfo_constant_lod is not supported for occlusionTexture and metallicRoughnessTexture
161558
+ // Use the same texture fallback logic as extractTextureId
161559
+ const textureInfo = material.pbrMetallicRoughness?.baseColorTexture ?? material.emissiveTexture;
161560
+ const extConstantLod = textureInfo?.extensions?.EXT_textureInfo_constant_lod;
161561
+ const offset = extConstantLod?.offset;
161562
+ extConstantLod ? constantLodParamProps = {
161563
+ repetitions: extConstantLod?.repetitions,
161564
+ offset: offset ? { x: offset[0], y: offset[1] } : undefined,
161565
+ minDistClamp: extConstantLod?.minClampDistance,
161566
+ maxDistClamp: extConstantLod?.maxClampDistance,
161567
+ } : undefined;
161568
+ // Normal map only uses constant LOD if both the base texture and normal texture have the extension
161569
+ normalMapUseConstantLod = extConstantLod !== undefined && material.normalTexture?.extensions?.EXT_textureInfo_constant_lod !== undefined;
161570
+ }
161406
161571
  const isTransparent = this.isMaterialTransparent(material);
161407
161572
  const textureId = this.extractTextureId(material);
161408
161573
  const normalMapId = this.extractNormalMapId(material);
161409
- let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId) : undefined;
161574
+ let textureMapping = (undefined !== textureId || undefined !== normalMapId) ? this.findTextureMapping(textureId, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod) : undefined;
161410
161575
  const color = colorFromMaterial(material, isTransparent);
161411
161576
  let renderMaterial;
161412
- if (undefined !== textureMapping && undefined !== textureMapping.normalMapParams) {
161413
- const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping };
161577
+ if (undefined !== textureMapping) {
161578
+ // Convert result of findTextureMapping (TextureMapping object) to MaterialTextureMappingProps interface
161579
+ const textureMappingProps = {
161580
+ texture: textureMapping.texture,
161581
+ normalMapParams: textureMapping.normalMapParams,
161582
+ mode: textureMapping.params.mode,
161583
+ transform: textureMapping.params.textureMatrix,
161584
+ weight: textureMapping.params.weight,
161585
+ worldMapping: textureMapping.params.worldMapping,
161586
+ useConstantLod: textureMapping.params.useConstantLod,
161587
+ constantLodProps: textureMapping.params.useConstantLod ? {
161588
+ repetitions: textureMapping.params.constantLodParams.repetitions,
161589
+ offset: textureMapping.params.constantLodParams.offset,
161590
+ minDistClamp: textureMapping.params.constantLodParams.minDistClamp,
161591
+ maxDistClamp: textureMapping.params.constantLodParams.maxDistClamp,
161592
+ } : undefined,
161593
+ };
161594
+ const args = { diffuse: { color }, specular: { color: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.ColorDef.white }, textureMapping: textureMappingProps };
161414
161595
  renderMaterial = _IModelApp__WEBPACK_IMPORTED_MODULE_3__.IModelApp.renderSystem.createRenderMaterial(args);
161415
161596
  // DisplayParams doesn't want a separate texture mapping if the material already has one.
161416
161597
  textureMapping = undefined;
@@ -161422,7 +161603,30 @@ class GltfReader {
161422
161603
  width = pointStyle.diameter;
161423
161604
  }
161424
161605
  }
161425
- return new _common_internal_render_DisplayParams__WEBPACK_IMPORTED_MODULE_8__.DisplayParams(_common_internal_render_DisplayParams__WEBPACK_IMPORTED_MODULE_8__.DisplayParams.Type.Mesh, color, color, width, _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.LinePixels.Solid, _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.None, renderMaterial, undefined, hasBakedLighting, textureMapping);
161606
+ // Process BENTLEY_materials_planar_fill extension
161607
+ let fillFlags = _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.None;
161608
+ if (!(0,_common_gltf_GltfSchema__WEBPACK_IMPORTED_MODULE_12__.isGltf1Material)(material)) {
161609
+ const planarFill = material.extensions?.BENTLEY_materials_planar_fill;
161610
+ if (planarFill) {
161611
+ // Map wireframeFill: 0=NONE (no fill flags), 1=ALWAYS (Always flag), 2=TOGGLE (ByView flag)
161612
+ const wireframeFill = planarFill.wireframeFill ?? 0;
161613
+ if (wireframeFill === 1) {
161614
+ fillFlags |= _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.Always;
161615
+ }
161616
+ else if (wireframeFill === 2) {
161617
+ fillFlags |= _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.ByView;
161618
+ }
161619
+ // Map backgroundFill to Background flag
161620
+ if (planarFill.backgroundFill === true) {
161621
+ fillFlags |= _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.Background;
161622
+ }
161623
+ // Map behind to Behind flag
161624
+ if (planarFill.behind === true) {
161625
+ fillFlags |= _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.FillFlags.Behind;
161626
+ }
161627
+ }
161628
+ }
161629
+ return new _common_internal_render_DisplayParams__WEBPACK_IMPORTED_MODULE_8__.DisplayParams(_common_internal_render_DisplayParams__WEBPACK_IMPORTED_MODULE_8__.DisplayParams.Type.Mesh, color, color, width, _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.LinePixels.Solid, fillFlags, renderMaterial, undefined, hasBakedLighting, textureMapping);
161426
161630
  }
161427
161631
  readMeshPrimitives(node, featureTable, thisTransform, thisBias, instances) {
161428
161632
  const meshes = [];
@@ -162224,7 +162428,7 @@ class GltfReader {
162224
162428
  if (_itwin_core_bentley__WEBPACK_IMPORTED_MODULE_0__.ProcessDetector.isIEBrowser) {
162225
162429
  throw new Error("Unsupported browser for Draco decoding");
162226
162430
  }
162227
- const dracoLoader = (await __webpack_require__.e(/*! import() */ "vendors-common_temp_node_modules_pnpm_loaders_gl_draco_4_3_4_node_modules_loaders_gl_draco_di-40e622").then(__webpack_require__.bind(__webpack_require__, /*! @loaders.gl/draco */ "../../common/temp/node_modules/.pnpm/@loaders.gl+draco@4.3.4/node_modules/@loaders.gl/draco/dist/index.js"))).DracoLoader;
162431
+ const dracoLoader = (await __webpack_require__.e(/*! import() */ "vendors-common_temp_node_modules_pnpm_loaders_gl_draco_4_3_4__loaders_gl_core_4_3_4_node_modu-4c1fc9").then(__webpack_require__.bind(__webpack_require__, /*! @loaders.gl/draco */ "../../common/temp/node_modules/.pnpm/@loaders.gl+draco@4.3.4_@loaders.gl+core@4.3.4/node_modules/@loaders.gl/draco/dist/index.js"))).DracoLoader;
162228
162432
  await Promise.all(dracoMeshes.map(async (x) => this.decodeDracoMesh(x, dracoLoader)));
162229
162433
  }
162230
162434
  catch (err) {
@@ -162368,7 +162572,7 @@ class GltfReader {
162368
162572
  });
162369
162573
  return renderTexture ?? false;
162370
162574
  }
162371
- findTextureMapping(id, isTransparent, normalMapId) {
162575
+ findTextureMapping(id, isTransparent, normalMapId, constantLodParamProps, normalMapUseConstantLod = false) {
162372
162576
  if (undefined === id && undefined === normalMapId)
162373
162577
  return undefined;
162374
162578
  let texture;
@@ -162390,16 +162594,18 @@ class GltfReader {
162390
162594
  nMap = {
162391
162595
  normalMap,
162392
162596
  greenUp,
162597
+ useConstantLod: normalMapUseConstantLod,
162393
162598
  };
162394
162599
  }
162395
162600
  else {
162396
162601
  texture = normalMap;
162397
- nMap = { greenUp };
162602
+ nMap = { greenUp, useConstantLod: normalMapUseConstantLod };
162398
162603
  }
162399
162604
  }
162400
162605
  if (!texture)
162401
162606
  return undefined;
162402
- const textureMapping = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping(texture, new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping.Params());
162607
+ const useConstantLod = constantLodParamProps !== undefined;
162608
+ const textureMapping = new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping(texture, new _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.TextureMapping.Params({ useConstantLod, constantLodProps: constantLodParamProps }));
162403
162609
  textureMapping.normalMapParams = nMap;
162404
162610
  return textureMapping;
162405
162611
  }
@@ -162436,7 +162642,7 @@ async function readGltfTemplate(args) {
162436
162642
  }
162437
162643
  /** Produce a [[RenderGraphic]] from a [glTF](https://www.khronos.org/gltf/) asset suitable for use in [view decorations]($docs/learning/frontend/ViewDecorations).
162438
162644
  * @returns a graphic produced from the glTF asset's default scene, or `undefined` if a graphic could not be produced from the asset.
162439
- * The returned graphic also includes the bounding boxes of the glTF model in world and local coordiantes.
162645
+ * The returned graphic also includes the bounding boxes of the glTF model in world and local coordinates.
162440
162646
  * @note Support for the full [glTF 2.0 specification](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html) is currently a work in progress.
162441
162647
  * If a particular glTF asset fails to load and/or display properly, please
162442
162648
  * [submit an issue](https://github.com/iTwin/itwinjs-core/issues).
@@ -162464,6 +162670,7 @@ class GltfGraphicsReader extends GltfReader {
162464
162670
  _contentRange;
162465
162671
  _transform;
162466
162672
  _isLeaf;
162673
+ _useViewportRenderMode;
162467
162674
  binaryData; // strictly for tests
162468
162675
  meshes; // strictly for tests
162469
162676
  constructor(props, args) {
@@ -162477,6 +162684,7 @@ class GltfGraphicsReader extends GltfReader {
162477
162684
  this._contentRange = args.contentRange;
162478
162685
  this._transform = args.transform;
162479
162686
  this._isLeaf = true !== args.hasChildren;
162687
+ this._useViewportRenderMode = args.useViewportRenderMode ?? false;
162480
162688
  this.binaryData = props.binaryData;
162481
162689
  const pickableId = args.pickableOptions?.id;
162482
162690
  if (pickableId) {
@@ -162487,7 +162695,8 @@ class GltfGraphicsReader extends GltfReader {
162487
162695
  get viewFlagOverrides() {
162488
162696
  return {
162489
162697
  whiteOnWhiteReversal: false,
162490
- renderMode: _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.RenderMode.SmoothShade,
162698
+ // Don't override renderMode if using viewport's render mode - let the viewport control it.
162699
+ renderMode: this._useViewportRenderMode ? undefined : _itwin_core_common__WEBPACK_IMPORTED_MODULE_2__.RenderMode.SmoothShade,
162491
162700
  };
162492
162701
  }
162493
162702
  get meshElementIdToFeatureIndex() {
@@ -304095,6 +304304,8 @@ class BaseFormat {
304095
304304
  _stationOffsetSize; // required when type is station; positive integer > 0
304096
304305
  _stationBaseFactor; // optional positive integer base factor for station formatting; default is 1
304097
304306
  _ratioType; // required if type is ratio; options: oneToN, NToOne, ValueBased, useGreatestCommonDivisor
304307
+ _ratioFormatType; // defaults to Decimal if not specified
304308
+ _ratioSeparator; // default is ":"; separator character used in ratio formatting
304098
304309
  _azimuthBase; // value always clockwise from north
304099
304310
  _azimuthBaseUnit; // unit for azimuthBase value
304100
304311
  _azimuthCounterClockwise; // if set to true, azimuth values are returned counter-clockwise from base
@@ -304116,6 +304327,10 @@ class BaseFormat {
304116
304327
  set scientificType(scientificType) { this._scientificType = scientificType; }
304117
304328
  get ratioType() { return this._ratioType; }
304118
304329
  set ratioType(ratioType) { this._ratioType = ratioType; }
304330
+ get ratioFormatType() { return this._ratioFormatType; }
304331
+ set ratioFormatType(ratioFormatType) { this._ratioFormatType = ratioFormatType; }
304332
+ get ratioSeparator() { return this._ratioSeparator; }
304333
+ set ratioSeparator(ratioSeparator) { this._ratioSeparator = ratioSeparator; }
304119
304334
  get showSignOption() { return this._showSignOption; }
304120
304335
  set showSignOption(showSignOption) { this._showSignOption = showSignOption; }
304121
304336
  get decimalSeparator() { return this._decimalSeparator; }
@@ -304184,6 +304399,22 @@ class BaseFormat {
304184
304399
  if (undefined === formatProps.ratioType)
304185
304400
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} is 'Ratio' type therefore the attribute 'ratioType' is required.`);
304186
304401
  this._ratioType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioType)(formatProps.ratioType, this.name);
304402
+ if (undefined !== formatProps.ratioSeparator) {
304403
+ if (typeof (formatProps.ratioSeparator) !== "string")
304404
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has an invalid 'ratioSeparator' attribute. It should be of type 'string'.`);
304405
+ if (formatProps.ratioSeparator.length !== 1)
304406
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has an invalid 'ratioSeparator' attribute. It should be a one character string.`);
304407
+ this._ratioSeparator = formatProps.ratioSeparator;
304408
+ }
304409
+ else {
304410
+ this._ratioSeparator = ":"; // Apply default
304411
+ }
304412
+ if (undefined !== formatProps.ratioFormatType) {
304413
+ this._ratioFormatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseRatioFormatType)(formatProps.ratioFormatType, this.name);
304414
+ }
304415
+ else {
304416
+ this._ratioFormatType = _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioFormatType.Decimal; // Apply default
304417
+ }
304187
304418
  }
304188
304419
  if (undefined !== formatProps.roundFactor) { // optional; default is 0.0
304189
304420
  if (typeof (formatProps.roundFactor) !== "number")
@@ -304308,6 +304539,8 @@ class Format extends BaseFormat {
304308
304539
  newFormat._azimuthBaseUnit = this._azimuthBaseUnit;
304309
304540
  newFormat._azimuthCounterClockwise = this._azimuthCounterClockwise;
304310
304541
  newFormat._ratioType = this._ratioType;
304542
+ newFormat._ratioFormatType = this._ratioFormatType;
304543
+ newFormat._ratioSeparator = this._ratioSeparator;
304311
304544
  newFormat._revolutionUnit = this._revolutionUnit;
304312
304545
  newFormat._customProps = this._customProps;
304313
304546
  this._units && (newFormat._units = [...this._units]);
@@ -304368,25 +304601,28 @@ class Format extends BaseFormat {
304368
304601
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has a Composite with an invalid 'units' attribute. It must be of type 'array'`);
304369
304602
  }
304370
304603
  if (jsonObj.composite.units.length > 0 && jsonObj.composite.units.length <= 4) { // Composite requires 1-4 units
304371
- for (const nextUnit of jsonObj.composite.units) {
304372
- if (this._units) {
304373
- for (const existingUnit of this._units) {
304374
- const unitObj = existingUnit[0].name;
304375
- if (unitObj.toLowerCase() === nextUnit.unit.name.toLowerCase()) {
304376
- throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The unit ${unitObj} has a duplicate name.`);
304377
- }
304604
+ const isDuplicateAllowed = this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio;
304605
+ const seenUnits = new Set();
304606
+ this._units = [];
304607
+ for (const unitSpec of jsonObj.composite.units) {
304608
+ if (!isDuplicateAllowed) {
304609
+ const unitName = unitSpec.unit.name.toLowerCase();
304610
+ const existingName = seenUnits.has(unitName);
304611
+ if (existingName) {
304612
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} contains duplicate units: '${unitSpec.unit.name}'`);
304378
304613
  }
304614
+ seenUnits.add(unitName);
304379
304615
  }
304380
- if (undefined === this._units) {
304381
- this._units = [];
304382
- }
304383
- this._units.push([nextUnit.unit, nextUnit.label]);
304616
+ this._units.push([unitSpec.unit, unitSpec.label]);
304384
304617
  }
304385
304618
  }
304386
304619
  }
304387
304620
  if (undefined === this.units || this.units.length === 0)
304388
304621
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} has a Composite with no valid 'units'`);
304389
304622
  }
304623
+ if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && (!this._units || this._units.length === 0)) {
304624
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${this.name} is 'Ratio' type and must have 'composite' units.`);
304625
+ }
304390
304626
  if (this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth || this.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
304391
304627
  this._azimuthBaseUnit = jsonObj.azimuthBaseUnit;
304392
304628
  this._revolutionUnit = jsonObj.revolutionUnit;
@@ -304453,6 +304689,8 @@ class Format extends BaseFormat {
304453
304689
  uomSeparator: this.uomSeparator,
304454
304690
  scientificType: this.scientificType ? this.scientificType : undefined,
304455
304691
  ratioType: this.ratioType,
304692
+ ratioFormatType: this.ratioFormatType,
304693
+ ratioSeparator: this.ratioSeparator,
304456
304694
  stationOffsetSize: this.stationOffsetSize,
304457
304695
  stationSeparator: this.stationSeparator,
304458
304696
  stationBaseFactor: this.stationBaseFactor,
@@ -304497,6 +304735,15 @@ async function resolveFormatProps(formatName, unitsProvider, jsonObj) {
304497
304735
  const unit = await resolveCompositeUnit(unitsProvider, entry.name);
304498
304736
  return { unit, label: entry.label };
304499
304737
  }));
304738
+ // For Ratio formats with 2 units: validate both units have the same phenomenon
304739
+ const formatType = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
304740
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio && units.length === 2) {
304741
+ const phenomenon1 = units[0].unit.phenomenon;
304742
+ const phenomenon2 = units[1].unit.phenomenon;
304743
+ if (phenomenon1 !== phenomenon2) {
304744
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidJson, `The Format ${formatName} has 2-unit composite with different phenomena. Both units must have the same phenomenon. Found '${phenomenon1}' and '${phenomenon2}'.`);
304745
+ }
304746
+ }
304500
304747
  }
304501
304748
  let azimuthBaseUnit, revolutionUnit;
304502
304749
  const type = (0,_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.parseFormatType)(jsonObj.type, formatName);
@@ -304537,6 +304784,7 @@ __webpack_require__.r(__webpack_exports__);
304537
304784
  /* harmony export */ FormatTraits: () => (/* binding */ FormatTraits),
304538
304785
  /* harmony export */ FormatType: () => (/* binding */ FormatType),
304539
304786
  /* harmony export */ FractionalPrecision: () => (/* binding */ FractionalPrecision),
304787
+ /* harmony export */ RatioFormatType: () => (/* binding */ RatioFormatType),
304540
304788
  /* harmony export */ RatioType: () => (/* binding */ RatioType),
304541
304789
  /* harmony export */ ScientificType: () => (/* binding */ ScientificType),
304542
304790
  /* harmony export */ ShowSignOption: () => (/* binding */ ShowSignOption),
@@ -304550,6 +304798,7 @@ __webpack_require__.r(__webpack_exports__);
304550
304798
  /* harmony export */ parseFormatType: () => (/* binding */ parseFormatType),
304551
304799
  /* harmony export */ parseFractionalPrecision: () => (/* binding */ parseFractionalPrecision),
304552
304800
  /* harmony export */ parsePrecision: () => (/* binding */ parsePrecision),
304801
+ /* harmony export */ parseRatioFormatType: () => (/* binding */ parseRatioFormatType),
304553
304802
  /* harmony export */ parseRatioType: () => (/* binding */ parseRatioType),
304554
304803
  /* harmony export */ parseScientificType: () => (/* binding */ parseScientificType),
304555
304804
  /* harmony export */ parseShowSignOption: () => (/* binding */ parseShowSignOption),
@@ -304694,6 +304943,16 @@ var RatioType;
304694
304943
  /** scales the input ratio to its simplest integer form using the greatest common divisor (GCD) of the values. e.g. 0.3 turns into 3:10 */
304695
304944
  RatioType["UseGreatestCommonDivisor"] = "UseGreatestCommonDivisor";
304696
304945
  })(RatioType || (RatioType = {}));
304946
+ /** The format type for the numbers within a ratio.
304947
+ * @beta
304948
+ */
304949
+ var RatioFormatType;
304950
+ (function (RatioFormatType) {
304951
+ /** Decimal display (ie 2.125) */
304952
+ RatioFormatType["Decimal"] = "Decimal";
304953
+ /** Fractional display (ie 2-1/8) */
304954
+ RatioFormatType["Fractional"] = "Fractional";
304955
+ })(RatioFormatType || (RatioFormatType = {}));
304697
304956
  /** Determines how the sign of values are displayed
304698
304957
  * @beta */
304699
304958
  var ShowSignOption;
@@ -304739,6 +304998,18 @@ function parseRatioType(ratioType, formatName) {
304739
304998
  }
304740
304999
  throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioType' attribute.`);
304741
305000
  }
305001
+ /** @beta */
305002
+ function parseRatioFormatType(ratioFormatType, formatName) {
305003
+ const normalizedValue = ratioFormatType.toLowerCase();
305004
+ for (const key in RatioFormatType) {
305005
+ if (RatioFormatType.hasOwnProperty(key)) {
305006
+ const enumValue = RatioFormatType[key];
305007
+ if (enumValue.toLowerCase() === normalizedValue)
305008
+ return enumValue;
305009
+ }
305010
+ }
305011
+ throw new _Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_0__.QuantityStatus.InvalidJson, `The Format ${formatName} has an invalid 'ratioFormatType' attribute.`);
305012
+ }
304742
305013
  /** @beta */
304743
305014
  function parseShowSignOption(showSignOption, formatName) {
304744
305015
  switch (showSignOption.toLowerCase()) {
@@ -304900,10 +305171,18 @@ function parsePrecision(precision, type, formatName) {
304900
305171
  case FormatType.Decimal:
304901
305172
  case FormatType.Scientific:
304902
305173
  case FormatType.Station:
304903
- case FormatType.Ratio:
304904
305174
  case FormatType.Bearing:
304905
305175
  case FormatType.Azimuth:
304906
305176
  return parseDecimalPrecision(precision, formatName);
305177
+ case FormatType.Ratio:
305178
+ // Ratio type can use either decimal or fractional precision depending on ratioFormatType
305179
+ // Try decimal first, if it fails, try fractional
305180
+ try {
305181
+ return parseDecimalPrecision(precision, formatName);
305182
+ }
305183
+ catch {
305184
+ return parseFractionalPrecision(precision, formatName);
305185
+ }
304907
305186
  case FormatType.Fractional:
304908
305187
  return parseFractionalPrecision(precision, formatName);
304909
305188
  default:
@@ -304927,8 +305206,9 @@ __webpack_require__.r(__webpack_exports__);
304927
305206
  /* harmony export */ });
304928
305207
  /* harmony import */ var _Constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Constants */ "../../core/quantity/lib/esm/Constants.js");
304929
305208
  /* harmony import */ var _Exception__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Exception */ "../../core/quantity/lib/esm/Exception.js");
304930
- /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
304931
- /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
305209
+ /* harmony import */ var _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./FormatterSpec */ "../../core/quantity/lib/esm/Formatter/FormatterSpec.js");
305210
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
305211
+ /* harmony import */ var _Quantity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../Quantity */ "../../core/quantity/lib/esm/Quantity.js");
304932
305212
  /*---------------------------------------------------------------------------------------------
304933
305213
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
304934
305214
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -304940,6 +305220,7 @@ __webpack_require__.r(__webpack_exports__);
304940
305220
 
304941
305221
 
304942
305222
 
305223
+
304943
305224
  /** rounding additive
304944
305225
  * @internal
304945
305226
  */
@@ -305040,7 +305321,7 @@ class Formatter {
305040
305321
  static integerPartToText(wholePart, spec) {
305041
305322
  // build invariant string represent wholePart
305042
305323
  let formattedValue = wholePart.toFixed(0);
305043
- if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
305324
+ if ((formattedValue.length > 3) && (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.Use1000Separator) && (spec.format.thousandSeparator.length > 0))) {
305044
305325
  let numSeparators = Math.floor(formattedValue.length / 3);
305045
305326
  let groupLength = formattedValue.length % 3;
305046
305327
  if (groupLength === 0) {
@@ -305087,7 +305368,7 @@ class Formatter {
305087
305368
  else {
305088
305369
  componentText = Formatter.formatMagnitude(compositeValue, spec);
305089
305370
  }
305090
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
305371
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
305091
305372
  componentText = componentText + spec.format.uomSeparator + label;
305092
305373
  }
305093
305374
  return componentText;
@@ -305107,12 +305388,11 @@ class Formatter {
305107
305388
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification.`);
305108
305389
  if (i > 0 && unitConversion.offset !== 0) // offset should only ever be defined for major unit
305109
305390
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has a invalid unit specification.`);
305110
- let unitValue = 0.0;
305111
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio) {
305112
- if (1 !== (spec.format.units?.length ?? 0))
305113
- throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format '${spec.format.name}' with type 'ratio' must have exactly one unit.`);
305391
+ // Handle ratio format with composite units
305392
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio) {
305393
+ let ratioUnitValue = 0.0;
305114
305394
  try {
305115
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305395
+ ratioUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305116
305396
  }
305117
305397
  catch (e) {
305118
305398
  // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
@@ -305120,12 +305400,13 @@ class Formatter {
305120
305400
  if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
305121
305401
  return { componentText: "1:0", isNegative: false };
305122
305402
  }
305403
+ throw e;
305123
305404
  }
305124
- compositeStrings.push(this.formatRatio(unitValue, spec));
305125
- isNegative = unitValue < 0;
305405
+ compositeStrings.push(this.formatRatio(ratioUnitValue, spec));
305406
+ isNegative = ratioUnitValue < 0;
305126
305407
  continue;
305127
305408
  }
305128
- unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305409
+ let unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(remainingMagnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305129
305410
  if (0 === i) {
305130
305411
  // Only set isNegative from the first (major) unit conversion
305131
305412
  isNegative = unitValue < 0;
@@ -305133,16 +305414,16 @@ class Formatter {
305133
305414
  // but use higher precision if the format specifies it
305134
305415
  const precisionScale = Math.pow(10, Math.max(8, spec.format.precision));
305135
305416
  unitValue = Math.floor(unitValue * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
305136
- if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
305417
+ if ((Math.abs(unitValue) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
305137
305418
  return { componentText: "", isNegative: false };
305138
305419
  }
305139
305420
  if (i < (spec.format.units?.length ?? 0) - 1) {
305140
305421
  let wholePart = Math.trunc(unitValue);
305141
305422
  // Check if the remaining fractional part will round up to a full unit in the next (smaller) component
305142
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
305423
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && i === spec.unitConversions.length - 2) {
305143
305424
  // For the second-to-last unit with fractional formatting, check if rounding causes carry-over
305144
305425
  const fractionalPart = unitValue - wholePart;
305145
- const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
305426
+ const nextUnitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(fractionalPart, spec.unitConversions[i + 1].conversion);
305146
305427
  // Create a FractionalNumeric to determine what the rounded value would be
305147
305428
  const fn = new FractionalNumeric(Math.abs(nextUnitValue), spec.format.precision, true);
305148
305429
  // If the fractional numeric rounds to a whole unit (integral part increased due to rounding)
@@ -305172,18 +305453,18 @@ class Formatter {
305172
305453
  */
305173
305454
  static formatMagnitude(magnitude, spec) {
305174
305455
  let posMagnitude = Math.abs(magnitude);
305175
- if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ZeroEmpty))
305456
+ if ((Math.abs(posMagnitude) < 0.0001) && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ZeroEmpty))
305176
305457
  return "";
305177
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ApplyRounding))
305458
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ApplyRounding))
305178
305459
  posMagnitude = Math.abs(Formatter.roundDouble(magnitude, spec.format.roundFactor));
305179
- const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific);
305180
- const isDecimal = (isSci || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Decimal || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Ratio;
305181
- const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Fractional);
305460
+ const isSci = ((posMagnitude > 1.0e12) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific);
305461
+ const isDecimal = (isSci || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Decimal || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio;
305462
+ const isFractional = (!isDecimal && spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional);
305182
305463
  /* const usesStops = spec.format.type === FormatType.Station; */
305183
- const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.DecimalPrecision.Zero;
305184
- const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepSingleZero);
305464
+ const isPrecisionZero = spec.format.precision === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.DecimalPrecision.Zero;
305465
+ const isKeepSingleZero = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepSingleZero);
305185
305466
  const precisionScale = Math.pow(10.0, spec.format.precision);
305186
- const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.TrailZeroes);
305467
+ const isKeepTrailingZeroes = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.TrailZeroes);
305187
305468
  let expInt = 0.0;
305188
305469
  if (isSci && (posMagnitude !== 0.0)) {
305189
305470
  let exp = Math.log10(posMagnitude);
@@ -305193,10 +305474,10 @@ class Formatter {
305193
305474
  negativeExp = true;
305194
305475
  }
305195
305476
  expInt = Math.floor(exp);
305196
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Scientific) {
305197
- if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
305477
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Scientific) {
305478
+ if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.ZeroNormalized && posMagnitude > 1.0)
305198
305479
  expInt += 1.0;
305199
- else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ScientificType.Normalized && posMagnitude < 1.0)
305480
+ else if (spec.format.scientificType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ScientificType.Normalized && posMagnitude < 1.0)
305200
305481
  expInt += 1.0;
305201
305482
  if (negativeExp)
305202
305483
  expInt = -expInt;
@@ -305218,7 +305499,7 @@ class Formatter {
305218
305499
  }
305219
305500
  formattedValue = Formatter.integerPartToText(wholePart, spec);
305220
305501
  if (isPrecisionZero) {
305221
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
305502
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint) && !isKeepSingleZero)
305222
305503
  formattedValue = formattedValue + spec.format.decimalSeparator;
305223
305504
  else if (isKeepSingleZero)
305224
305505
  formattedValue = `${formattedValue + spec.format.decimalSeparator}0`;
@@ -305233,7 +305514,7 @@ class Formatter {
305233
305514
  if (fractionString.length > 0)
305234
305515
  formattedValue = formattedValue + spec.format.decimalSeparator + fractionString;
305235
305516
  else {
305236
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
305517
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
305237
305518
  formattedValue = formattedValue + spec.format.decimalSeparator + (isKeepSingleZero ? "0" : "");
305238
305519
  }
305239
305520
  }
@@ -305246,7 +305527,7 @@ class Formatter {
305246
305527
  const fn = new FractionalNumeric(posMagnitude, spec.format.precision, true);
305247
305528
  formattedValue = fn.getIntegralString();
305248
305529
  if (!fn.isZero && fn.hasFractionPart) {
305249
- const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.FractionDash) ? "-" : " ";
305530
+ const wholeFractionSeparator = spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.FractionDash) ? "-" : " ";
305250
305531
  const fractionString = `${fn.getNumeratorString()}/${fn.getDenominatorString()}`;
305251
305532
  formattedValue = formattedValue + wholeFractionSeparator + fractionString;
305252
305533
  }
@@ -305275,7 +305556,7 @@ class Formatter {
305275
305556
  else {
305276
305557
  if (isKeepTrailingZeroes)
305277
305558
  fractionString = spec.format.decimalSeparator + "".padEnd(spec.format.precision, "0");
305278
- else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.KeepDecimalPoint))
305559
+ else if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.KeepDecimalPoint))
305279
305560
  fractionString = spec.format.decimalSeparator;
305280
305561
  formattedValue = stationString + fractionString;
305281
305562
  }
@@ -305304,21 +305585,21 @@ class Formatter {
305304
305585
  let prefix = "";
305305
305586
  let suffix = "";
305306
305587
  switch (showSignOption) {
305307
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NegativeParentheses:
305588
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NegativeParentheses:
305308
305589
  if (isNegative) {
305309
305590
  prefix = "(";
305310
305591
  suffix = ")";
305311
305592
  }
305312
305593
  break;
305313
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.OnlyNegative:
305314
- if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305594
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.OnlyNegative:
305595
+ if (isNegative && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && formatType !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305315
305596
  prefix = "-";
305316
305597
  }
305317
305598
  break;
305318
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.SignAlways:
305599
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.SignAlways:
305319
305600
  prefix = isNegative ? "-" : "+";
305320
305601
  break;
305321
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.ShowSignOption.NoSign:
305602
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.ShowSignOption.NoSign:
305322
305603
  default:
305323
305604
  break;
305324
305605
  }
@@ -305334,14 +305615,20 @@ class Formatter {
305334
305615
  let suffix = "";
305335
305616
  let formattedValue = "";
305336
305617
  // Handle bearing/azimuth special formatting
305337
- if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305618
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing || spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305338
305619
  const result = this.processBearingAndAzimuth(magnitude, spec);
305339
305620
  magnitude = result.magnitude;
305340
305621
  prefix = result.prefix ?? "";
305341
305622
  suffix = result.suffix ?? "";
305342
305623
  }
305343
305624
  let formattedMagnitude = "";
305344
- if (spec.format.hasUnits) {
305625
+ if (spec.format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Ratio && spec.unitConversions.length >= 3) {
305626
+ // Handle ratio formatting separately when 2-unit composite provides 3 conversion specs
305627
+ const ratioResult = this.formatRatioQuantity(magnitude, spec);
305628
+ formattedMagnitude = ratioResult.componentText;
305629
+ valueIsNegative = ratioResult.isNegative;
305630
+ }
305631
+ else if (spec.format.hasUnits) {
305345
305632
  const compositeResult = Formatter.formatComposite(magnitude, spec);
305346
305633
  formattedMagnitude = compositeResult.componentText;
305347
305634
  // Override the sign detection with the composite conversion result
@@ -305350,8 +305637,8 @@ class Formatter {
305350
305637
  else {
305351
305638
  // unitless quantity
305352
305639
  formattedMagnitude = Formatter.formatMagnitude(magnitude, spec);
305353
- if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.ShowUnitLabel)) {
305354
- if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatTraits.PrependUnitLabel))
305640
+ if (formattedMagnitude.length > 0 && spec.unitConversions.length > 0 && spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel)) {
305641
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.PrependUnitLabel))
305355
305642
  formattedMagnitude = spec.unitConversions[0].label + spec.format.uomSeparator + formattedMagnitude;
305356
305643
  else
305357
305644
  formattedMagnitude = formattedMagnitude + spec.format.uomSeparator + spec.unitConversions[0].label;
@@ -305371,12 +305658,12 @@ class Formatter {
305371
305658
  }
305372
305659
  static processBearingAndAzimuth(magnitude, spec) {
305373
305660
  const type = spec.format.type;
305374
- if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth)
305661
+ if (type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing && type !== _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth)
305375
305662
  return { magnitude };
305376
305663
  const revolution = this.getRevolution(spec);
305377
305664
  magnitude = this.normalizeAngle(magnitude, revolution);
305378
305665
  const quarterRevolution = revolution / 4;
305379
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Bearing) {
305666
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Bearing) {
305380
305667
  let quadrant = 0;
305381
305668
  while (magnitude > quarterRevolution) {
305382
305669
  magnitude -= quarterRevolution;
@@ -305403,7 +305690,7 @@ class Formatter {
305403
305690
  if (quadrant === 2 && spec.unitConversions.length > 0) {
305404
305691
  // To determine if value is small, we need to convert it to the smallest unit presented and use the provided precision on it
305405
305692
  const unitConversion = spec.unitConversions[spec.unitConversions.length - 1].conversion;
305406
- const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_3__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
305693
+ const smallestFormattedDelta = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)((quarterRevolution - magnitude), unitConversion) + this.FPV_MINTHRESHOLD;
305407
305694
  const precisionScale = Math.pow(10.0, spec.format.precision);
305408
305695
  const floor = Math.floor((smallestFormattedDelta) * precisionScale + FPV_ROUNDFACTOR) / precisionScale;
305409
305696
  if (floor === 0) {
@@ -305412,13 +305699,13 @@ class Formatter {
305412
305699
  }
305413
305700
  return { magnitude, prefix, suffix };
305414
305701
  }
305415
- if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Azimuth) {
305702
+ if (type === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Azimuth) {
305416
305703
  let azimuthBase = 0; // default base is North
305417
305704
  if (spec.format.azimuthBase !== undefined) {
305418
305705
  if (spec.azimuthBaseConversion === undefined) {
305419
305706
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing azimuth base conversion for interpreting ${spec.name}'s azimuth base.`);
305420
305707
  }
305421
- const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
305708
+ const azBaseQuantity = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.azimuthBaseUnit, spec.format.azimuthBase);
305422
305709
  const azBaseConverted = azBaseQuantity.convertTo(spec.persistenceUnit, spec.azimuthBaseConversion);
305423
305710
  if (azBaseConverted === undefined || !azBaseConverted.isValid) {
305424
305711
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.UnsupportedUnit, `Failed to convert azimuth base unit to ${spec.persistenceUnit.name}.`);
@@ -305448,40 +305735,87 @@ class Formatter {
305448
305735
  if (spec.revolutionConversion === undefined) {
305449
305736
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing revolution unit conversion for calculating ${spec.name}'s revolution.`);
305450
305737
  }
305451
- const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.revolutionUnit, 1.0);
305738
+ const revolution = new _Quantity__WEBPACK_IMPORTED_MODULE_4__.Quantity(spec.format.revolutionUnit, 1.0);
305452
305739
  const converted = revolution.convertTo(spec.persistenceUnit, spec.revolutionConversion);
305453
305740
  if (converted === undefined || !converted.isValid) {
305454
305741
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.UnsupportedUnit, `Failed to convert revolution unit to ${spec.persistenceUnit.name}.`);
305455
305742
  }
305456
305743
  return converted.magnitude;
305457
305744
  }
305745
+ static formatRatioPart(value, spec, side) {
305746
+ const formatType = spec.format.ratioFormatType === "Fractional" ? _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional : _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Decimal;
305747
+ const tempFormat = spec.format.clone({ type: formatType });
305748
+ const tempSpec = new _FormatterSpec__WEBPACK_IMPORTED_MODULE_2__.FormatterSpec(spec.name, tempFormat, spec.unitConversions, spec.persistenceUnit);
305749
+ let formattedValue = this.formatMagnitude(value, tempSpec);
305750
+ // For fractional ratio formatting, suppress leading "0" if the value is purely fractional
305751
+ if (formatType === _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatType.Fractional && formattedValue.startsWith("0 ")) {
305752
+ formattedValue = formattedValue.substring(2); // Remove "0 " prefix
305753
+ }
305754
+ // Add unit label if ShowUnitLabel trait is set
305755
+ // unitConversions[0] = ratio scale factor, [1] = numerator unit, [2] = denominator unit
305756
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
305757
+ const labelToAdd = side === "numerator" ? spec.unitConversions[1].label : spec.unitConversions[2].label;
305758
+ formattedValue = formattedValue + labelToAdd;
305759
+ }
305760
+ return formattedValue;
305761
+ }
305762
+ /** Format a ratio quantity value (separate from composite formatting) */
305763
+ static formatRatioQuantity(magnitude, spec) {
305764
+ const unitConversion = spec.unitConversions[0].conversion;
305765
+ let unitValue = 0.0;
305766
+ try {
305767
+ unitValue = (0,_Quantity__WEBPACK_IMPORTED_MODULE_4__.applyConversion)(magnitude, unitConversion) + this.FPV_MINTHRESHOLD;
305768
+ }
305769
+ catch (e) {
305770
+ // The "InvertingZero" error is thrown when the value is zero and the conversion factor is inverted.
305771
+ // For ratio, we return "1:0" as the formatted value.
305772
+ if (e instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && e.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
305773
+ return { componentText: "1:0", isNegative: false };
305774
+ }
305775
+ throw e;
305776
+ }
305777
+ const componentText = this.formatRatio(unitValue, spec);
305778
+ const isNegative = unitValue < 0;
305779
+ return { componentText, isNegative };
305780
+ }
305458
305781
  static formatRatio(magnitude, spec) {
305459
305782
  if (null === spec.format.ratioType)
305460
305783
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} must have a ratio type specified.`);
305461
305784
  const precisionScale = Math.pow(10.0, spec.format.precision);
305785
+ const separator = spec.format.ratioSeparator;
305462
305786
  let reciprocal = 0;
305787
+ // Helper to get unit labels if ShowUnitLabel is set
305788
+ const getUnitLabels = () => {
305789
+ if (spec.format.hasFormatTraitSet(_FormatEnums__WEBPACK_IMPORTED_MODULE_3__.FormatTraits.ShowUnitLabel) && spec.unitConversions.length >= 3) {
305790
+ return { numeratorLabel: spec.unitConversions[1].label, denominatorLabel: spec.unitConversions[2].label };
305791
+ }
305792
+ return { numeratorLabel: "", denominatorLabel: "" };
305793
+ };
305794
+ const { numeratorLabel, denominatorLabel } = getUnitLabels();
305463
305795
  if (magnitude === 0.0)
305464
- return "0:1";
305796
+ return `0${separator}1`;
305465
305797
  else
305466
305798
  reciprocal = 1.0 / magnitude;
305467
305799
  switch (spec.format.ratioType) {
305468
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.OneToN:
305469
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
305470
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.NToOne:
305471
- return `${this.formatMagnitude(magnitude, spec)}:1`;
305472
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.ValueBased:
305473
- if (magnitude > 1.0)
305474
- return `${this.formatMagnitude(magnitude, spec)}:1`;
305475
- else
305476
- return `1:${this.formatMagnitude(reciprocal, spec)}`;
305477
- case _FormatEnums__WEBPACK_IMPORTED_MODULE_2__.RatioType.UseGreatestCommonDivisor:
305800
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.OneToN:
305801
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
305802
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.NToOne:
305803
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
305804
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.ValueBased:
305805
+ if (magnitude > 1.0) {
305806
+ return `${this.formatRatioPart(magnitude, spec, "numerator")}${separator}1${denominatorLabel}`;
305807
+ }
305808
+ else {
305809
+ return `1${numeratorLabel}${separator}${this.formatRatioPart(reciprocal, spec, "denominator")}`;
305810
+ }
305811
+ case _FormatEnums__WEBPACK_IMPORTED_MODULE_3__.RatioType.UseGreatestCommonDivisor:
305478
305812
  magnitude = Math.round(magnitude * precisionScale) / precisionScale;
305479
305813
  let numerator = magnitude * precisionScale;
305480
305814
  let denominator = precisionScale;
305481
305815
  const gcd = FractionalNumeric.getGreatestCommonFactor(numerator, denominator);
305482
305816
  numerator /= gcd;
305483
305817
  denominator /= gcd;
305484
- return `${this.formatMagnitude(numerator, spec)}:${this.formatMagnitude(denominator, spec)}`;
305818
+ return `${this.formatRatioPart(numerator, spec, "numerator")}${separator}${this.formatRatioPart(denominator, spec, "denominator")}`;
305485
305819
  default:
305486
305820
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvalidCompositeFormat, `The Format ${spec.format.name} has an invalid ratio type specified.`);
305487
305821
  }
@@ -305502,7 +305836,8 @@ __webpack_require__.r(__webpack_exports__);
305502
305836
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
305503
305837
  /* harmony export */ FormatterSpec: () => (/* binding */ FormatterSpec)
305504
305838
  /* harmony export */ });
305505
- /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
305839
+ /* harmony import */ var _FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
305840
+ /* harmony import */ var _Formatter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Formatter */ "../../core/quantity/lib/esm/Formatter/Formatter.js");
305506
305841
  /*---------------------------------------------------------------------------------------------
305507
305842
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
305508
305843
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -305511,6 +305846,7 @@ __webpack_require__.r(__webpack_exports__);
305511
305846
  * @module Quantity
305512
305847
  */
305513
305848
 
305849
+
305514
305850
  // cSpell:ignore ZERONORMALIZED, nosign, onlynegative, signalways, negativeparentheses
305515
305851
  // cSpell:ignore trailzeroes, keepsinglezero, zeroempty, keepdecimalpoint, applyrounding, fractiondash, showunitlabel, prependunitlabel, exponentonlynegative
305516
305852
  /** A class that contains both formatting information and the conversion factors necessary to convert from an input unit to the units specified in the format.
@@ -305557,6 +305893,49 @@ class FormatterSpec {
305557
305893
  get persistenceUnit() { return this._persistenceUnit; }
305558
305894
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
305559
305895
  get revolutionConversion() { return this._revolutionConversion; }
305896
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
305897
+ static async getRatioUnitConversions(units, unitsProvider, persistenceUnit) {
305898
+ const conversions = [];
305899
+ const [numeratorUnit, numeratorLabel] = units[0];
305900
+ const [denominatorUnit, denominatorLabel] = units[1];
305901
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
305902
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
305903
+ const displayRatioScale = denominatorToNumerator.factor;
305904
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
305905
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
305906
+ const persistenceName = persistenceUnit.name.toUpperCase();
305907
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
305908
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
305909
+ // Split by word boundaries (underscores, dots) and check for exact token matches
305910
+ const persistenceTokens = persistenceName.split(/[._]/);
305911
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
305912
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
305913
+ // First conversion spec: effective ratio unit conversion
305914
+ const ratioConversionSpec = {
305915
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
305916
+ label: "",
305917
+ system: numeratorUnit.system,
305918
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
305919
+ };
305920
+ conversions.push(ratioConversionSpec);
305921
+ // Numerator unit for label lookup
305922
+ const numeratorSpec = {
305923
+ name: numeratorUnit.name,
305924
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
305925
+ system: numeratorUnit.system,
305926
+ conversion: { factor: 1.0, offset: 0.0 },
305927
+ };
305928
+ conversions.push(numeratorSpec);
305929
+ // Denominator unit for label lookup
305930
+ const denominatorSpec = {
305931
+ name: denominatorUnit.name,
305932
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
305933
+ system: denominatorUnit.system,
305934
+ conversion: { factor: 1.0, offset: 0.0 },
305935
+ };
305936
+ conversions.push(denominatorSpec);
305937
+ return conversions;
305938
+ }
305560
305939
  /** Get an array of UnitConversionSpecs, one for each unit that is to be shown in the formatted quantity string. */
305561
305940
  static async getUnitConversions(format, unitsProvider, inputUnit) {
305562
305941
  const conversions = [];
@@ -305570,6 +305949,10 @@ class FormatterSpec {
305570
305949
  throw new Error("Formatter Spec needs persistence unit to be specified");
305571
305950
  }
305572
305951
  }
305952
+ // Handle 2-unit composite for ratio formats (scale factors)
305953
+ if (format.type === _FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
305954
+ return FormatterSpec.getRatioUnitConversions(format.units, unitsProvider, persistenceUnit);
305955
+ }
305573
305956
  if (format.units) {
305574
305957
  let convertFromUnit = inputUnit;
305575
305958
  for (const unit of format.units) {
@@ -305626,7 +306009,7 @@ class FormatterSpec {
305626
306009
  }
305627
306010
  /** Format a quantity value. */
305628
306011
  applyFormatting(magnitude) {
305629
- return _Formatter__WEBPACK_IMPORTED_MODULE_0__.Formatter.formatQuantity(magnitude, this);
306012
+ return _Formatter__WEBPACK_IMPORTED_MODULE_1__.Formatter.formatQuantity(magnitude, this);
305630
306013
  }
305631
306014
  }
305632
306015
 
@@ -305734,13 +306117,14 @@ var ParseError;
305734
306117
  ParseError[ParseError["BearingPrefixOrSuffixMissing"] = 7] = "BearingPrefixOrSuffixMissing";
305735
306118
  ParseError[ParseError["MathematicOperationFoundButIsNotAllowed"] = 8] = "MathematicOperationFoundButIsNotAllowed";
305736
306119
  ParseError[ParseError["BearingAngleOutOfRange"] = 9] = "BearingAngleOutOfRange";
306120
+ ParseError[ParseError["InvalidMathResult"] = 10] = "InvalidMathResult";
305737
306121
  })(ParseError || (ParseError = {}));
305738
306122
  var Operator;
305739
306123
  (function (Operator) {
305740
306124
  Operator["addition"] = "+";
305741
306125
  Operator["subtraction"] = "-";
305742
306126
  Operator["multiplication"] = "*";
305743
- Operator["division"] = "/"; // unsupported but we recognize it during parsing
306127
+ Operator["division"] = "/";
305744
306128
  })(Operator || (Operator = {}));
305745
306129
  function isOperator(char) {
305746
306130
  if (typeof char === "number") {
@@ -306495,36 +306879,107 @@ class Parser {
306495
306879
  magnitude = this.normalizeAngle(magnitude, revolution);
306496
306880
  return { ok: true, value: magnitude };
306497
306881
  }
306882
+ /**
306883
+ * Parse a ratio part string (numerator or denominator) to extract the numeric value and optional unit label.
306884
+ * This method processes tokens without applying unit conversions, allowing the ratio format
306885
+ * handler to manage conversions at the ratio level.
306886
+ *
306887
+ *
306888
+ * @note Fractions are already handled by parseQuantitySpecification, which converts them to
306889
+ * single numeric tokens (e.g., "1/2" becomes 0.5).
306890
+ *
306891
+ * @param partStr The string to parse, which may contain a number, fraction, or mixed fraction with optional unit label.
306892
+ * @param format The format specification used for token parsing.
306893
+ * @returns An object containing the parsed numeric value and optional unit label. Returns NaN for value if no number is found.
306894
+ */
306895
+ static parseRatioPart(partStr, format) {
306896
+ partStr = partStr.trim();
306897
+ // Parse tokens - fractions are automatically converted to decimal values by parseQuantitySpecification
306898
+ const tempFormat = format.clone({ type: _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_2__.FormatType.Decimal });
306899
+ const tokens = Parser.parseQuantitySpecification(partStr, tempFormat);
306900
+ let value = NaN;
306901
+ let unitLabel;
306902
+ // Pre-process: merge negative operators with following numbers
306903
+ const processedTokens = [];
306904
+ for (let i = 0; i < tokens.length; i++) {
306905
+ const token = tokens[i];
306906
+ if (token.isOperator && i === 0 && token.value === "-" &&
306907
+ i + 1 < tokens.length && tokens[i + 1].isNumber) {
306908
+ // Merge negative sign with number
306909
+ processedTokens.push(new ParseToken(-tokens[i + 1].value));
306910
+ i++; // Skip the number token since we consumed it
306911
+ }
306912
+ else {
306913
+ processedTokens.push(token);
306914
+ }
306915
+ }
306916
+ // Extract numeric value and unit label from processed tokens
306917
+ for (const token of processedTokens) {
306918
+ if (token.isNumber && isNaN(value)) {
306919
+ value = token.value;
306920
+ }
306921
+ else if (token.isString && !token.isOperator) {
306922
+ // String token that's not an operator - treat as unit label
306923
+ unitLabel = token.value;
306924
+ }
306925
+ }
306926
+ return { value, unitLabel };
306927
+ }
306498
306928
  static parseRatioFormat(inString, spec) {
306499
306929
  if (!inString)
306500
306930
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
306501
- const parts = inString.split(":");
306931
+ const separator = spec.format.ratioSeparator ?? ":";
306932
+ const parts = inString.split(separator);
306502
306933
  if (parts.length > 2)
306503
306934
  return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
306504
- const numerator = parseFloat(parts[0]);
306505
- let denominator;
306506
- if (parts.length === 1) {
306507
- denominator = 1.0;
306508
- }
306509
- else {
306510
- denominator = parseFloat(parts[1]);
306935
+ // If the string doesn't contain the expected separator but contains other ratio-like separators,
306936
+ // return an error since the wrong separator was used
306937
+ if (parts.length === 1 && !inString.includes(separator)) {
306938
+ // Check if the string contains other common ratio separators
306939
+ const otherSeparators = [":", "=", "/"];
306940
+ for (const otherSep of otherSeparators) {
306941
+ if (otherSep !== separator && inString.includes(otherSep)) {
306942
+ // The string looks like a ratio but uses the wrong separator
306943
+ return { ok: false, error: ParseError.UnableToConvertParseTokensToQuantity };
306944
+ }
306945
+ }
306946
+ // Parse as a regular quantity value (numerator only, denominator = 1)
306947
+ const result = this.parseAndProcessTokens(inString, spec.format, spec.unitConversions);
306948
+ return result;
306511
306949
  }
306512
- if (isNaN(numerator) || isNaN(denominator))
306950
+ // Parse numerator and denominator parts which may include unit labels
306951
+ const numeratorPart = this.parseRatioPart(parts[0], spec.format);
306952
+ const denominatorPart = parts.length === 1 ? { value: 1.0 } : this.parseRatioPart(parts[1], spec.format);
306953
+ if (isNaN(numeratorPart.value) || isNaN(denominatorPart.value))
306513
306954
  return { ok: false, error: ParseError.NoValueOrUnitFoundInString };
306955
+ // Handle 2-unit composite case - simpler conversion using the pre-computed scale factor
306956
+ if (spec.format.units && spec.format.units.length === 2 && spec.unitConversions.length >= 3) {
306957
+ const ratioConvSpec = spec.unitConversions[0];
306958
+ const scaleFactor = ratioConvSpec.conversion.factor;
306959
+ if (denominatorPart.value === 0) {
306960
+ return { ok: false, error: ParseError.InvalidMathResult };
306961
+ }
306962
+ // The ratio value is numerator/denominator in the display units (e.g., 12 for 12"=1')
306963
+ // Divide by scale factor to get persistence unit value (e.g., 12/12 = 1.0)
306964
+ const ratioValue = numeratorPart.value / denominatorPart.value;
306965
+ const convertedValue = ratioValue / scaleFactor;
306966
+ return { ok: true, value: convertedValue };
306967
+ }
306968
+ // Original flow for 1-unit composite - use Quantity.convertTo for proper unit conversion
306514
306969
  const defaultUnit = spec.format.units && spec.format.units.length > 0 ? spec.format.units[0][0] : undefined;
306515
306970
  const unitConversion = defaultUnit ? Parser.tryFindUnitConversion(defaultUnit.label, spec.unitConversions, defaultUnit) : undefined;
306516
306971
  if (!unitConversion) {
306517
306972
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, `Missing input unit or unit conversion for interpreting ${spec.format.name}.`);
306518
306973
  }
306519
- if (denominator === 0) {
306520
- if (unitConversion.inversion && numerator === 1)
306974
+ if (denominatorPart.value === 0) {
306975
+ if (unitConversion.inversion && numeratorPart.value === 1)
306521
306976
  return { ok: true, value: 0.0 };
306522
306977
  else
306523
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
306978
+ return { ok: false, error: ParseError.InvalidMathResult };
306524
306979
  }
306525
306980
  let quantity;
306526
306981
  if (spec.format.units && spec.outUnit) {
306527
- quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numerator / denominator);
306982
+ quantity = new _Quantity__WEBPACK_IMPORTED_MODULE_3__.Quantity(spec.format.units[0][0], numeratorPart.value / denominatorPart.value);
306528
306983
  }
306529
306984
  else {
306530
306985
  throw new _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError(_Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.MissingRequiredProperty, "Missing presentation unit or persistence unit for ratio format.");
@@ -306536,7 +306991,7 @@ class Parser {
306536
306991
  catch (err) {
306537
306992
  // for input of "0:N" with reversed unit
306538
306993
  if (err instanceof _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError && err.errorNumber === _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus.InvertingZero) {
306539
- return { ok: false, error: ParseError.MathematicOperationFoundButIsNotAllowed };
306994
+ return { ok: false, error: ParseError.InvalidMathResult };
306540
306995
  }
306541
306996
  }
306542
306997
  if (converted === undefined || !converted.isValid) {
@@ -306662,7 +307117,8 @@ __webpack_require__.r(__webpack_exports__);
306662
307117
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
306663
307118
  /* harmony export */ ParserSpec: () => (/* binding */ ParserSpec)
306664
307119
  /* harmony export */ });
306665
- /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
307120
+ /* harmony import */ var _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Formatter/FormatEnums */ "../../core/quantity/lib/esm/Formatter/FormatEnums.js");
307121
+ /* harmony import */ var _Parser__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Parser */ "../../core/quantity/lib/esm/Parser.js");
306666
307122
  /*---------------------------------------------------------------------------------------------
306667
307123
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
306668
307124
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -306671,6 +307127,7 @@ __webpack_require__.r(__webpack_exports__);
306671
307127
  * @module Quantity
306672
307128
  */
306673
307129
 
307130
+
306674
307131
  /** A ParserSpec holds information needed to parse a string into a quantity synchronously.
306675
307132
  * @beta
306676
307133
  */
@@ -306696,6 +307153,51 @@ class ParserSpec {
306696
307153
  get outUnit() { return this._outUnit; }
306697
307154
  get azimuthBaseConversion() { return this._azimuthBaseConversion; }
306698
307155
  get revolutionConversion() { return this._revolutionConversion; }
307156
+ /** Build conversion specs for ratio format with 2 composite units (numerator/denominator). */
307157
+ static async getRatioUnitConversions(units, unitsProvider, outUnit, altUnitLabelsProvider) {
307158
+ const conversions = [];
307159
+ const [numeratorUnit, numeratorLabel] = units[0];
307160
+ const [denominatorUnit, denominatorLabel] = units[1];
307161
+ // Compute ratio scale: how many numerator units per denominator unit (e.g., IN:FT = 12)
307162
+ const denominatorToNumerator = await unitsProvider.getConversion(denominatorUnit, numeratorUnit);
307163
+ const displayRatioScale = denominatorToNumerator.factor;
307164
+ // Avoid double-scaling: if persistence unit already encodes the display ratio, use factor 1.
307165
+ // Check by name heuristic (e.g., IN_PER_FT with ratioUnits [IN, FT] → no scaling needed)
307166
+ const persistenceName = outUnit.name.toUpperCase();
307167
+ const numName = numeratorUnit.name.toUpperCase().split(".").pop() ?? "";
307168
+ const denName = denominatorUnit.name.toUpperCase().split(".").pop() ?? "";
307169
+ // Split by word boundaries (underscores, dots) and check for exact token matches
307170
+ const persistenceTokens = persistenceName.split(/[._]/);
307171
+ const isPersistenceMatchingRatio = persistenceTokens.includes(numName) && persistenceTokens.includes(denName);
307172
+ const ratioScaleFactor = isPersistenceMatchingRatio ? 1.0 : displayRatioScale;
307173
+ // First conversion spec: effective ratio unit conversion
307174
+ const ratioConversionSpec = {
307175
+ name: `${numeratorUnit.name}_per_${denominatorUnit.name}`,
307176
+ label: "",
307177
+ system: numeratorUnit.system,
307178
+ conversion: { factor: ratioScaleFactor, offset: 0.0 },
307179
+ };
307180
+ conversions.push(ratioConversionSpec);
307181
+ // Numerator unit for label lookup
307182
+ const numeratorSpec = {
307183
+ name: numeratorUnit.name,
307184
+ label: numeratorLabel?.length ? numeratorLabel : numeratorUnit.label,
307185
+ system: numeratorUnit.system,
307186
+ conversion: { factor: 1.0, offset: 0.0 },
307187
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(numeratorUnit),
307188
+ };
307189
+ conversions.push(numeratorSpec);
307190
+ // Denominator unit for label lookup
307191
+ const denominatorSpec = {
307192
+ name: denominatorUnit.name,
307193
+ label: denominatorLabel?.length ? denominatorLabel : denominatorUnit.label,
307194
+ system: denominatorUnit.system,
307195
+ conversion: { factor: 1.0, offset: 0.0 },
307196
+ parseLabels: altUnitLabelsProvider?.getAlternateUnitLabels(denominatorUnit),
307197
+ };
307198
+ conversions.push(denominatorSpec);
307199
+ return conversions;
307200
+ }
306699
307201
  /** Static async method to create a ParserSpec given the format and unit of the quantity that will be passed to the Parser. The input unit will
306700
307202
  * be used to generate conversion information for each unit specified in the Format. This method is async due to the fact that the units provider must make
306701
307203
  * async calls to lookup unit definitions.
@@ -306704,7 +307206,14 @@ class ParserSpec {
306704
307206
  * @param outUnit The unit a value will be formatted to. This unit is often referred to as persistence unit.
306705
307207
  */
306706
307208
  static async create(format, unitsProvider, outUnit, altUnitLabelsProvider) {
306707
- const conversions = await _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
307209
+ let conversions;
307210
+ // For ratio formats with 2 composite units, use private helper method
307211
+ if (format.type === _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_0__.FormatType.Ratio && format.units && format.units.length === 2) {
307212
+ conversions = await ParserSpec.getRatioUnitConversions(format.units, unitsProvider, outUnit, altUnitLabelsProvider);
307213
+ }
307214
+ else {
307215
+ conversions = await _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.createUnitConversionSpecsForUnit(unitsProvider, outUnit, altUnitLabelsProvider);
307216
+ }
306708
307217
  const spec = new ParserSpec(outUnit, format, conversions);
306709
307218
  if (format.azimuthBaseUnit !== undefined) {
306710
307219
  if (outUnit !== undefined) {
@@ -306726,7 +307235,7 @@ class ParserSpec {
306726
307235
  }
306727
307236
  /** Do the parsing. Done this way to allow Custom Parser Specs to parse custom formatted strings into their quantities. */
306728
307237
  parseToQuantityValue(inString) {
306729
- return _Parser__WEBPACK_IMPORTED_MODULE_0__.Parser.parseQuantityString(inString, this);
307238
+ return _Parser__WEBPACK_IMPORTED_MODULE_1__.Parser.parseQuantityString(inString, this);
306730
307239
  }
306731
307240
  }
306732
307241
 
@@ -306924,6 +307433,7 @@ __webpack_require__.r(__webpack_exports__);
306924
307433
  /* harmony export */ QuantityConstants: () => (/* reexport safe */ _Constants__WEBPACK_IMPORTED_MODULE_0__.QuantityConstants),
306925
307434
  /* harmony export */ QuantityError: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityError),
306926
307435
  /* harmony export */ QuantityStatus: () => (/* reexport safe */ _Exception__WEBPACK_IMPORTED_MODULE_1__.QuantityStatus),
307436
+ /* harmony export */ RatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioFormatType),
306927
307437
  /* harmony export */ RatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.RatioType),
306928
307438
  /* harmony export */ ScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ScientificType),
306929
307439
  /* harmony export */ ShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.ShowSignOption),
@@ -306942,6 +307452,7 @@ __webpack_require__.r(__webpack_exports__);
306942
307452
  /* harmony export */ parseFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFormatType),
306943
307453
  /* harmony export */ parseFractionalPrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseFractionalPrecision),
306944
307454
  /* harmony export */ parsePrecision: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parsePrecision),
307455
+ /* harmony export */ parseRatioFormatType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioFormatType),
306945
307456
  /* harmony export */ parseRatioType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseRatioType),
306946
307457
  /* harmony export */ parseScientificType: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseScientificType),
306947
307458
  /* harmony export */ parseShowSignOption: () => (/* reexport safe */ _Formatter_FormatEnums__WEBPACK_IMPORTED_MODULE_9__.parseShowSignOption),
@@ -320651,7 +321162,7 @@ var loadLanguages = instance.loadLanguages;
320651
321162
  /***/ ((module) => {
320652
321163
 
320653
321164
  "use strict";
320654
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@itwin/core-frontend","version":"5.6.0-dev.8","description":"iTwin.js frontend components","main":"lib/cjs/core-frontend.js","module":"lib/esm/core-frontend.js","typings":"lib/cjs/core-frontend","license":"MIT","scripts":{"build":"npm run -s copy:public && npm run -s build:cjs && npm run -s build:esm && npm run -s webpackWorkers && npm run -s copy:workers && npm run -s copy:draco","build:cjs":"npm run -s copy:js:cjs && tsc 1>&2 --outDir lib/cjs","build:esm":"npm run -s copy:js:esm && tsc 1>&2 --module ES2022 --outDir lib/esm","clean":"rimraf -g lib .rush/temp/package-deps*.json","copy:public":"cpx \\"./src/public/**/*\\" ./lib/public","copy:js:cjs":"cpx \\"./src/**/*.js\\" ./lib/cjs","copy:js:esm":"cpx \\"./src/**/*.js\\" ./lib/esm","copy:workers":"cpx \\"./lib/workers/webpack/parse-imdl-worker.js\\" ./lib/public/scripts","copy:draco":"cpx \\"./node_modules/@loaders.gl/draco/dist/libs/*\\" ./lib/public/scripts","docs":"betools docs --json=../../generated-docs/core/core-frontend/file.json --tsIndexFile=./core-frontend.ts --onlyJson --excludes=webgl/**/*,**/map/*.d.ts,**/tile/*.d.ts,**/*-css.ts","extract-api":"betools extract-api --entry=core-frontend && npm run extract-extension-api","extract-extension-api":"eslint --no-inline-config -c extraction.eslint.config.js \\"./src/**/*.ts\\" 1>&2","lint":"eslint \\"./src/**/*.ts\\" 1>&2","lint-fix":"eslint --fix -f visualstudio \\"./src/**/*.ts\\" 1>&2","lint-deprecation":"eslint --fix -f visualstudio --no-inline-config -c ../../common/config/eslint/eslint.config.deprecation-policy.js \\"./src/**/*.ts\\"","pseudolocalize":"betools pseudolocalize --englishDir ./src/public/locales/en --out ./public/locales/en-PSEUDO","test":"npm run webpackTestWorker && vitest --run","cover":"npm run webpackTestWorker && vitest --run","webpackTests":"webpack --config ./src/test/utils/webpack.config.js 1>&2 && npm run -s webpackTestWorker","webpackTestWorker":"webpack --config ./src/test/worker/webpack.config.js 1>&2 && cpx \\"./lib/test/test-worker.js\\" ./lib/test","webpackWorkers":"webpack --config ./src/workers/ImdlParser/webpack.config.js 1>&2"},"repository":{"type":"git","url":"https://github.com/iTwin/itwinjs-core.git","directory":"core/frontend"},"keywords":["Bentley","BIM","iModel","digital-twin","iTwin"],"author":{"name":"Bentley Systems, Inc.","url":"http://www.bentley.com"},"peerDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*"},"//devDependencies":["NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install","NOTE: All tools used by scripts in this package must be listed as devDependencies"],"devDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/build-tools":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*","@itwin/object-storage-core":"^3.0.4","@itwin/eslint-plugin":"5.2.2-dev.2","@types/chai-as-promised":"^7","@types/draco3d":"^1.4.10","@types/sinon":"^17.0.2","@vitest/browser":"^3.0.6","@vitest/coverage-v8":"^3.0.6","cpx2":"^8.0.0","eslint":"^9.31.0","glob":"^10.5.0","playwright":"~1.56.1","rimraf":"^6.0.1","sinon":"^17.0.2","source-map-loader":"^5.0.0","typescript":"~5.6.2","typemoq":"^2.1.0","vitest":"^3.0.6","vite-multiple-assets":"^1.3.1","vite-plugin-static-copy":"2.2.0","webpack":"^5.97.1"},"//dependencies":["NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API","NOTE: core-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed"],"dependencies":{"@itwin/core-i18n":"workspace:*","@itwin/webgl-compatibility":"workspace:*","@loaders.gl/draco":"^4.3.4","fuse.js":"^3.3.0","wms-capabilities":"0.4.0"}}');
321165
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@itwin/core-frontend","version":"5.7.0-dev.1","description":"iTwin.js frontend components","main":"lib/cjs/core-frontend.js","module":"lib/esm/core-frontend.js","typings":"lib/cjs/core-frontend","license":"MIT","scripts":{"build":"npm run -s copy:public && npm run -s build:cjs && npm run -s build:esm && npm run -s webpackWorkers && npm run -s copy:workers && npm run -s copy:draco","build:cjs":"npm run -s copy:js:cjs && tsc 1>&2 --outDir lib/cjs","build:esm":"npm run -s copy:js:esm && tsc 1>&2 --module ES2022 --outDir lib/esm","clean":"rimraf -g lib .rush/temp/package-deps*.json","copy:public":"cpx \\"./src/public/**/*\\" ./lib/public","copy:js:cjs":"cpx \\"./src/**/*.js\\" ./lib/cjs","copy:js:esm":"cpx \\"./src/**/*.js\\" ./lib/esm","copy:workers":"cpx \\"./lib/workers/webpack/parse-imdl-worker.js\\" ./lib/public/scripts","copy:draco":"cpx \\"./node_modules/@loaders.gl/draco/dist/libs/*\\" ./lib/public/scripts","docs":"betools docs --json=../../generated-docs/core/core-frontend/file.json --tsIndexFile=./core-frontend.ts --onlyJson --excludes=webgl/**/*,**/map/*.d.ts,**/tile/*.d.ts,**/*-css.ts","extract-api":"betools extract-api --entry=core-frontend && npm run extract-extension-api","extract-extension-api":"eslint --no-inline-config -c extraction.eslint.config.js \\"./src/**/*.ts\\" 1>&2","lint":"eslint \\"./src/**/*.ts\\" 1>&2","lint-fix":"eslint --fix -f visualstudio \\"./src/**/*.ts\\" 1>&2","lint-deprecation":"eslint --fix -f visualstudio --no-inline-config -c ../../common/config/eslint/eslint.config.deprecation-policy.js \\"./src/**/*.ts\\"","pseudolocalize":"betools pseudolocalize --englishDir ./src/public/locales/en --out ./public/locales/en-PSEUDO","test":"npm run webpackTestWorker && vitest --run","cover":"npm run webpackTestWorker && vitest --run","webpackTests":"webpack --config ./src/test/utils/webpack.config.js 1>&2 && npm run -s webpackTestWorker","webpackTestWorker":"webpack --config ./src/test/worker/webpack.config.js 1>&2 && cpx \\"./lib/test/test-worker.js\\" ./lib/test","webpackWorkers":"webpack --config ./src/workers/ImdlParser/webpack.config.js 1>&2"},"repository":{"type":"git","url":"https://github.com/iTwin/itwinjs-core.git","directory":"core/frontend"},"keywords":["Bentley","BIM","iModel","digital-twin","iTwin"],"author":{"name":"Bentley Systems, Inc.","url":"http://www.bentley.com"},"peerDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*"},"//devDependencies":["NOTE: All peerDependencies should also be listed as devDependencies since peerDependencies are not considered by npm install","NOTE: All tools used by scripts in this package must be listed as devDependencies"],"devDependencies":{"@itwin/appui-abstract":"workspace:*","@itwin/build-tools":"workspace:*","@itwin/core-bentley":"workspace:*","@itwin/core-common":"workspace:*","@itwin/core-geometry":"workspace:*","@itwin/core-orbitgt":"workspace:*","@itwin/core-quantity":"workspace:*","@itwin/ecschema-metadata":"workspace:*","@itwin/ecschema-rpcinterface-common":"workspace:*","@itwin/object-storage-core":"^3.0.4","@itwin/eslint-plugin":"^6.0.0","@types/chai-as-promised":"^7","@types/draco3d":"^1.4.10","@types/sinon":"^17.0.2","@vitest/browser":"^3.0.6","@vitest/coverage-v8":"^3.0.6","cpx2":"^8.0.0","eslint":"^9.31.0","glob":"^10.5.0","playwright":"~1.56.1","rimraf":"^6.0.1","sinon":"^17.0.2","source-map-loader":"^5.0.0","typescript":"~5.6.2","typemoq":"^2.1.0","vitest":"^3.0.6","vite-multiple-assets":"^1.3.1","vite-plugin-static-copy":"2.2.0","webpack":"^5.97.1"},"//dependencies":["NOTE: these dependencies should be only for things that DO NOT APPEAR IN THE API","NOTE: core-frontend should remain UI technology agnostic, so no react/angular dependencies are allowed"],"dependencies":{"@itwin/core-i18n":"workspace:*","@itwin/webgl-compatibility":"workspace:*","@loaders.gl/core":"^4.3.4","@loaders.gl/draco":"^4.3.4","fuse.js":"^3.3.0","wms-capabilities":"0.4.0"}}');
320655
321166
 
320656
321167
  /***/ })
320657
321168