@itwin/core-backend 5.14.0-dev.9 → 5.15.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.
Files changed (134) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/lib/cjs/ChangesetReader.d.ts +1 -1
  3. package/lib/cjs/ChangesetReader.js +2 -2
  4. package/lib/cjs/ChangesetReader.js.map +1 -1
  5. package/lib/cjs/ChangesetReaderTypes.d.ts +4 -1
  6. package/lib/cjs/ChangesetReaderTypes.d.ts.map +1 -1
  7. package/lib/cjs/ChangesetReaderTypes.js +3 -0
  8. package/lib/cjs/ChangesetReaderTypes.js.map +1 -1
  9. package/lib/cjs/ECDb.d.ts +63 -6
  10. package/lib/cjs/ECDb.d.ts.map +1 -1
  11. package/lib/cjs/ECDb.js +62 -6
  12. package/lib/cjs/ECDb.js.map +1 -1
  13. package/lib/cjs/ECSqlStatement.d.ts +2 -2
  14. package/lib/cjs/ECSqlStatement.js +2 -2
  15. package/lib/cjs/ECSqlStatement.js.map +1 -1
  16. package/lib/cjs/Element.d.ts.map +1 -1
  17. package/lib/cjs/Element.js +6 -3
  18. package/lib/cjs/Element.js.map +1 -1
  19. package/lib/cjs/IModelDb.d.ts +12 -10
  20. package/lib/cjs/IModelDb.d.ts.map +1 -1
  21. package/lib/cjs/IModelDb.js +26 -12
  22. package/lib/cjs/IModelDb.js.map +1 -1
  23. package/lib/cjs/SchemaSync.d.ts +2 -2
  24. package/lib/cjs/SchemaSync.d.ts.map +1 -1
  25. package/lib/cjs/SchemaSync.js +2 -2
  26. package/lib/cjs/SchemaSync.js.map +1 -1
  27. package/lib/cjs/SqliteChangesetReader.d.ts +16 -8
  28. package/lib/cjs/SqliteChangesetReader.d.ts.map +1 -1
  29. package/lib/cjs/SqliteChangesetReader.js +11 -1
  30. package/lib/cjs/SqliteChangesetReader.js.map +1 -1
  31. package/lib/cjs/TxnManager.d.ts.map +1 -1
  32. package/lib/cjs/TxnManager.js +4 -0
  33. package/lib/cjs/TxnManager.js.map +1 -1
  34. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  35. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  36. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
  37. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  38. package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
  39. package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
  40. package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
  41. package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
  42. package/lib/cjs/internal/annotations/fields.d.ts +39 -2
  43. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  44. package/lib/cjs/internal/annotations/fields.js +140 -13
  45. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  46. package/lib/esm/ChangesetReader.d.ts +1 -1
  47. package/lib/esm/ChangesetReader.js +2 -2
  48. package/lib/esm/ChangesetReader.js.map +1 -1
  49. package/lib/esm/ChangesetReaderTypes.d.ts +4 -1
  50. package/lib/esm/ChangesetReaderTypes.d.ts.map +1 -1
  51. package/lib/esm/ChangesetReaderTypes.js +3 -0
  52. package/lib/esm/ChangesetReaderTypes.js.map +1 -1
  53. package/lib/esm/ECDb.d.ts +63 -6
  54. package/lib/esm/ECDb.d.ts.map +1 -1
  55. package/lib/esm/ECDb.js +62 -6
  56. package/lib/esm/ECDb.js.map +1 -1
  57. package/lib/esm/ECSqlStatement.d.ts +2 -2
  58. package/lib/esm/ECSqlStatement.js +2 -2
  59. package/lib/esm/ECSqlStatement.js.map +1 -1
  60. package/lib/esm/Element.d.ts.map +1 -1
  61. package/lib/esm/Element.js +6 -3
  62. package/lib/esm/Element.js.map +1 -1
  63. package/lib/esm/IModelDb.d.ts +12 -10
  64. package/lib/esm/IModelDb.d.ts.map +1 -1
  65. package/lib/esm/IModelDb.js +26 -12
  66. package/lib/esm/IModelDb.js.map +1 -1
  67. package/lib/esm/SchemaSync.d.ts +2 -2
  68. package/lib/esm/SchemaSync.d.ts.map +1 -1
  69. package/lib/esm/SchemaSync.js +2 -2
  70. package/lib/esm/SchemaSync.js.map +1 -1
  71. package/lib/esm/SqliteChangesetReader.d.ts +16 -8
  72. package/lib/esm/SqliteChangesetReader.d.ts.map +1 -1
  73. package/lib/esm/SqliteChangesetReader.js +11 -1
  74. package/lib/esm/SqliteChangesetReader.js.map +1 -1
  75. package/lib/esm/TxnManager.d.ts.map +1 -1
  76. package/lib/esm/TxnManager.js +4 -0
  77. package/lib/esm/TxnManager.js.map +1 -1
  78. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  79. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  80. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
  81. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  82. package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
  83. package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
  84. package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
  85. package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
  86. package/lib/esm/internal/annotations/fields.d.ts +39 -2
  87. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  88. package/lib/esm/internal/annotations/fields.js +140 -13
  89. package/lib/esm/internal/annotations/fields.js.map +1 -1
  90. package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
  91. package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
  92. package/lib/esm/test/AnnotationTestUtils.js +17 -0
  93. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  94. package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
  95. package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
  96. package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
  97. package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
  98. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
  99. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
  100. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
  101. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
  102. package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
  103. package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
  104. package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
  105. package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
  106. package/lib/esm/test/annotations/Fields.test.js +221 -27
  107. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  108. package/lib/esm/test/ecdb/CSVImport.test.d.ts +2 -0
  109. package/lib/esm/test/ecdb/CSVImport.test.d.ts.map +1 -0
  110. package/lib/esm/test/ecdb/CSVImport.test.js +186 -0
  111. package/lib/esm/test/ecdb/CSVImport.test.js.map +1 -0
  112. package/lib/esm/test/ecdb/ConcurrentQuery.test.js +19 -2
  113. package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
  114. package/lib/esm/test/ecdb/ECDb.test.js +207 -95
  115. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  116. package/lib/esm/test/ecdb/ECSqlAst.test.js +2 -2
  117. package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
  118. package/lib/esm/test/ecdb/QueryReaders.test.js +105 -30
  119. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  120. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.d.ts.map +1 -1
  121. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js +7 -0
  122. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
  123. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js +86 -7
  124. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
  125. package/lib/esm/test/imodel/IModelElements.test.js +5 -0
  126. package/lib/esm/test/imodel/IModelElements.test.js.map +1 -1
  127. package/lib/esm/test/standalone/ChangesetReader.test.js +111 -2
  128. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  129. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js +61 -0
  130. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js.map +1 -1
  131. package/lib/esm/test/standalone/SQLiteDb.test.d.ts.map +1 -1
  132. package/lib/esm/test/standalone/SQLiteDb.test.js +116 -0
  133. package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
  134. package/package.json +16 -14
@@ -1,14 +1,51 @@
1
- import { FieldRun, FieldValue, RelationshipProps, TextBlock } from "@itwin/core-common";
1
+ import { FieldRun, RelationshipProps, TextBlock } from "@itwin/core-common";
2
2
  import { IModelDb } from "../../IModelDb";
3
3
  import { Id64String } from "@itwin/core-bentley";
4
+ import { FieldFormatting, ResolvedFieldValue } from "./fieldSpecs";
4
5
  import type { EditTxn } from "../../EditTxn";
6
+ /** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to
7
+ * read a field's property value, and how to format quantities.
8
+ */
5
9
  export interface UpdateFieldsContext {
10
+ /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —
11
+ * an edit to one source element leaves the annotation's other fields untouched.
12
+ */
6
13
  readonly hostElementId: Id64String | undefined;
7
- getProperty(field: FieldRun): FieldValue | undefined;
14
+ /** Reads the value a field points at, or `undefined` if it cannot be resolved — including
15
+ * every field when the source element was deleted.
16
+ */
17
+ getProperty(field: FieldRun): ResolvedFieldValue | undefined;
18
+ /** The formats `"quantity"` and `"coordinate"` values resolve through. [[updateField]] builds
19
+ * a [FormatterSpec]($core-quantity) per field from the FormatSet named by
20
+ * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and
21
+ * the schemas; a value none of them can format falls back to `value.toString()`.
22
+ * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in
23
+ * tests that never format a quantity leave it out.
24
+ */
25
+ readonly formatting?: FieldFormatting;
8
26
  }
9
27
  export declare function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext;
28
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
29
+ * cachedContent changed.
30
+ *
31
+ * Resolving the property value and formatting it are both fallible. A failure of either is
32
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
33
+ * fields in the same block are unaffected.
34
+ */
10
35
  export declare function updateField(field: FieldRun, context: UpdateFieldsContext): boolean;
36
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
37
+ * whose cached display string changed. Fields targeting an element other than
38
+ * `context.hostElementId` (when set) are skipped.
39
+ */
11
40
  export declare function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number;
41
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
42
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
43
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
44
+ */
12
45
  export declare function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void;
46
+ /** Re-evaluates every field of the given annotation element against its current property
47
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
48
+ * establishing / refreshing relationships.
49
+ */
13
50
  export declare function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void;
14
51
  //# sourceMappingURL=fields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAAE,UAAU,EAAiD,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC3M,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;CACrD;AAoKD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAK9H;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAsBlF;AAID,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AA0BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAA8E,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC5N,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,EAAE,eAAe,EAAuC,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAE7D;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACvC;AA+OD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAM9H;AA0CD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CA0BlF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AAyBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
@@ -15,7 +15,9 @@ const BackendLoggerCategory_1 = require("../../BackendLoggerCategory");
15
15
  const ElementDrivesTextAnnotation_1 = require("../../annotations/ElementDrivesTextAnnotation");
16
16
  const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
17
17
  const ECSqlInstanceReshaper_1 = require("../ECSqlInstanceReshaper");
18
- // Resolve the raw primitive value of the property that a field points to.
18
+ const fieldSpecs_1 = require("./fieldSpecs");
19
+ // Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for
20
+ // `"quantity"` / `"coordinate"` types, the property-side KoQ and persistence unit.
19
21
  function getFieldPropertyValue(field, iModel) {
20
22
  const host = field.propertyHost;
21
23
  const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);
@@ -23,7 +25,7 @@ function getFieldPropertyValue(field, iModel) {
23
25
  return undefined;
24
26
  }
25
27
  let ecClass = schemaItem;
26
- const { propertyName, accessors } = field.propertyPath;
28
+ const { propertyName, accessors, jsonAccessors } = field.propertyPath;
27
29
  let ecProp = ecClass.getPropertySync(propertyName);
28
30
  if (!ecProp) {
29
31
  return undefined;
@@ -115,15 +117,81 @@ function getFieldPropertyValue(field, iModel) {
115
117
  }
116
118
  }
117
119
  }
120
+ // The ultimate result must be a primitive value.
121
+ if (undefined === curValue.primitive) {
122
+ return undefined;
123
+ }
124
+ // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the
125
+ // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.
126
+ if (jsonAccessors && jsonAccessors.length > 0) {
127
+ if (!isJsonProperty(ecProp) || typeof curValue.primitive !== "string") {
128
+ return undefined;
129
+ }
130
+ return readJsonLeaf(curValue.primitive, jsonAccessors);
131
+ }
118
132
  const propertyType = determineFieldPropertyType(ecProp);
119
133
  if (!propertyType) {
120
134
  return undefined;
121
135
  }
122
- // The ultimate result must be a primitive value.
123
- if (undefined === curValue.primitive) {
136
+ // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are
137
+ // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback
138
+ // when the override doesn't resolve.
139
+ let kindOfQuantityFullName;
140
+ let persistenceUnitFullName;
141
+ if (propertyType === "quantity" || propertyType === "coordinate") {
142
+ const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;
143
+ kindOfQuantityFullName = koq?.fullName;
144
+ persistenceUnitFullName = koq?.persistenceUnit?.fullName;
145
+ }
146
+ return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };
147
+ }
148
+ function isJsonProperty(prop) {
149
+ return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === ecschema_metadata_1.PrimitiveType.String && prop.extendedTypeName === "Json";
150
+ }
151
+ /** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,
152
+ * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC
153
+ * metadata is consulted.
154
+ *
155
+ * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path
156
+ * stops anywhere but a scalar — including on a JSON `null`, which is not a
157
+ * [FieldPrimitiveValue]($common).
158
+ */
159
+ function readJsonLeaf(raw, accessors) {
160
+ let cur;
161
+ try {
162
+ cur = JSON.parse(raw);
163
+ }
164
+ catch {
165
+ // Not valid JSON; treat as a normal string.
124
166
  return undefined;
125
167
  }
126
- return { value: curValue.primitive, type: propertyType };
168
+ for (const accessor of accessors) {
169
+ // A number indexes an array and a string keys an object; anything else doesn't resolve.
170
+ const isIndex = typeof accessor === "number";
171
+ if (typeof cur !== "object" || null === cur || Array.isArray(cur) !== isIndex) {
172
+ return undefined;
173
+ }
174
+ if (isIndex) {
175
+ cur = cur[accessor < 0 ? cur.length + accessor : accessor];
176
+ }
177
+ else {
178
+ // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.
179
+ cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;
180
+ }
181
+ }
182
+ // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.
183
+ // A numeric leaf is typed `"quantity"` but has no property-side KoQ, so only the field's own
184
+ // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.
185
+ switch (typeof cur) {
186
+ case "number":
187
+ return { value: cur, type: "quantity" };
188
+ case "boolean":
189
+ return { value: cur, type: "boolean" };
190
+ case "string":
191
+ return { value: cur, type: "string" };
192
+ default:
193
+ return undefined;
194
+ }
127
195
  }
128
196
  function determineFieldPropertyType(prop) {
129
197
  if (prop.isEnumeration()) {
@@ -145,16 +213,18 @@ function determineFieldPropertyType(prop) {
145
213
  case ecschema_metadata_1.PrimitiveType.DateTime:
146
214
  return "datetime";
147
215
  case ecschema_metadata_1.PrimitiveType.Double:
216
+ case ecschema_metadata_1.PrimitiveType.Integer:
148
217
  case ecschema_metadata_1.PrimitiveType.Long:
218
+ // Any numeric property is a potential quantity. Classifying one as "quantity" is not an
219
+ // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is
220
+ // consulted. A number that resolves no spec falls back to the same `toString()` the
221
+ // "string" formatter would have produced, so counts and identifiers still render bare.
149
222
  return "quantity";
150
223
  case ecschema_metadata_1.PrimitiveType.Point2d:
151
224
  case ecschema_metadata_1.PrimitiveType.Point3d:
152
225
  return "coordinate";
153
226
  case ecschema_metadata_1.PrimitiveType.Binary:
154
227
  return prop.extendedTypeName === "BeGuid" ? "string" : undefined;
155
- case ecschema_metadata_1.PrimitiveType.Integer:
156
- case ecschema_metadata_1.PrimitiveType.Long:
157
- return "string";
158
228
  default:
159
229
  return undefined;
160
230
  }
@@ -165,9 +235,53 @@ function createUpdateContext(hostElementId, iModel, deleted) {
165
235
  return {
166
236
  hostElementId,
167
237
  getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),
238
+ formatting: (0, fieldSpecs_1.getFieldFormatting)(iModel),
168
239
  };
169
240
  }
170
- // Recompute the display value of a single field, return false if it couldn't be evaluated.
241
+ /** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,
242
+ * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats
243
+ * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the
244
+ * field may format through. In that last case the shortfall is logged: a persistence unit or
245
+ * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a
246
+ * format whose units belong to a different phenomenon than the persisted value. A
247
+ * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged
248
+ * too; the property's unit is what the stored magnitude means.
249
+ */
250
+ function resolveFormatMagnitude(value, field, context) {
251
+ const formatting = context.formatting;
252
+ if (!formatting || (value.type !== "quantity" && value.type !== "coordinate")) {
253
+ return undefined;
254
+ }
255
+ const quantityOptions = field.formatOptions?.quantity;
256
+ const { spec, candidates, ignoredPersistenceUnit } = (0, fieldSpecs_1.lookupFieldSpec)(quantityOptions, value, formatting);
257
+ if (ignoredPersistenceUnit) {
258
+ core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "Ignoring persistenceUnit override that disagrees with the property's own persistence unit", () => ({
259
+ elementId: field.propertyHost.elementId,
260
+ propertyName: field.propertyPath.propertyName,
261
+ persistenceUnit: ignoredPersistenceUnit,
262
+ propertyPersistenceUnit: value.persistenceUnitFullName,
263
+ }));
264
+ }
265
+ if (!spec) {
266
+ if (candidates.length > 0) {
267
+ core_bentley_1.Logger.logWarning(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, "No format resolved for text annotation field; rendering raw value", () => ({
268
+ elementId: field.propertyHost.elementId,
269
+ propertyName: field.propertyPath.propertyName,
270
+ formatSet: quantityOptions?.formatSet,
271
+ tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),
272
+ }));
273
+ }
274
+ return undefined;
275
+ }
276
+ return (magnitude) => spec.applyFormatting(magnitude);
277
+ }
278
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
279
+ * cachedContent changed.
280
+ *
281
+ * Resolving the property value and formatting it are both fallible. A failure of either is
282
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
283
+ * fields in the same block are unaffected.
284
+ */
171
285
  function updateField(field, context) {
172
286
  if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {
173
287
  return false;
@@ -176,7 +290,11 @@ function updateField(field, context) {
176
290
  try {
177
291
  const propValue = context.getProperty(field);
178
292
  if (undefined !== propValue) {
179
- newContent = (0, core_common_1.formatFieldValue)(propValue, field.formatOptions);
293
+ newContent = (0, core_common_1.formatFieldValue)({
294
+ value: propValue,
295
+ options: field.formatOptions,
296
+ formatMagnitude: resolveFormatMagnitude(propValue, field, context),
297
+ });
180
298
  }
181
299
  }
182
300
  catch (err) {
@@ -189,8 +307,10 @@ function updateField(field, context) {
189
307
  field.setCachedContent(newContent);
190
308
  return true;
191
309
  }
192
- // Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number
193
- // of fields whose display strings changed as a result.
310
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
311
+ * whose cached display string changed. Fields targeting an element other than
312
+ * `context.hostElementId` (when set) are skipped.
313
+ */
194
314
  function updateFields(textBlock, context) {
195
315
  let numUpdated = 0;
196
316
  for (const { child } of (0, core_common_1.traverseTextBlockComponent)(textBlock)) {
@@ -222,10 +342,17 @@ function doUpdateFields(txn, annotationId, sourceId, deleted) {
222
342
  core_bentley_1.Logger.logError(BackendLoggerCategory_1.BackendLoggerCategory.IModelDb, err);
223
343
  }
224
344
  }
225
- // Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.
345
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
346
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
347
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
348
+ */
226
349
  function updateElementFields(props, txn, deleted) {
227
350
  doUpdateFields(txn, props.targetId, props.sourceId, deleted);
228
351
  }
352
+ /** Re-evaluates every field of the given annotation element against its current property
353
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
354
+ * establishing / refreshing relationships.
355
+ */
229
356
  function updateAllFields(annotationElementId, txn) {
230
357
  doUpdateFields(txn, annotationElementId, undefined, false);
231
358
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\nexport interface UpdateFieldsContext {\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n getProperty(field: FieldRun): FieldValue | undefined\r\n}\r\n\r\n// Resolve the raw primitive value of the property that a field points to.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): FieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType };\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Long:\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n return \"string\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n };\r\n}\r\n\r\n// Recompute the display value of a single field, return false if it couldn't be evaluated.\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue(propValue, field.formatOptions);\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n// Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number\r\n// of fields whose display strings changed as a result.\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n// Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":";AAAA;;;+FAG+F;;AAyS/F,kDAMC;AAiDD,kCA0BC;AAMD,oCASC;AA6BD,kDAEC;AAMD,0CAEC;AA9aD,oDAA4N;AAE5N,sDAAgF;AAChF,uEAAoE;AACpE,+FAAkF;AAClF,gEAA6H;AAC7H,oEAAgE;AAChE,6CAAwG;AAmDxG,oGAAoG;AACpG,mFAAmF;AACnF,SAAS,qBAAqB,CAAC,KAAe,EAAE,MAAgB;IAC9D,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC;IAChC,MAAM,UAAU,GAAG,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAC3F,IAAI,CAAC,+BAAW,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,GAAa,UAAU,CAAC;IACnC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC;IACtE,IAAI,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,UAAU,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,cAAc,aAAa,CAAC;IAC9E,+FAA+F;IAC/F,mFAAmF;IACnF,oGAAoG;IACpG,0GAA0G;IAC1G,IAAI,QAAQ,GAA+B,MAAM,CAAC,eAAe,CAAC,UAAU,YAAY,SAAS,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE,EAAE,CAAC,MAAM,EAA8B,EAAE;QACpL,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YACnB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACvC,IAAI,SAAS,KAAK,YAAY,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,IAAA,4BAAa,EAAC,MAAM,CAAC,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAA,4CAAoB,EAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YACrB,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;QACxF,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACtB,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;YAC7B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC/B,CAAC;QAED,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,OAAO;gBACL,SAAS,EAAE,MAAM,CAAC,aAAa,KAAK,iCAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;aAC7F,CAAC;QACJ,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC,EAAE,IAAI,yBAAW,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,4BAAc,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAE/G,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,SAAS,KAAK,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACrC,gCAAgC;gBAChC,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACjC,MAAM,KAAK,GAA2D,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,WAAW,CAAC;gBACtH,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,SAAS,CAAC;gBACnB,CAAC;gBAED,MAAM,KAAK,GAAW,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC1E,MAAM,IAAI,GAA2C,KAAK,CAAC,KAAK,CAAC,CAAC;gBAClE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;oBACvB,OAAO,SAAS,CAAC;gBACnB,CAAC;qBAAM,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;oBACnC,QAAQ,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,CAAC;qBAAM,CAAC;oBACN,IAAA,qBAAM,EAAC,SAAS,KAAK,QAAQ,CAAC,WAAW,CAAC,CAAC;oBAC3C,IAAA,qBAAM,EAAC,MAAM,YAAY,uCAAmB,CAAC,CAAC;oBAE9C,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;oBAC7B,QAAQ,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,SAAS,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;oBAClC,OAAO,SAAS,CAAC;gBACnB,CAAC;gBAED,MAAM,IAAI,GAAQ,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBAC5C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;oBACvB,OAAO,SAAS,CAAC;gBACnB,CAAC;gBAED,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,SAAS,CAAC;gBACnB,CAAC;gBAED,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;oBACrB,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;gBAClF,CAAC;qBAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;oBAC7B,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;oBAC7B,QAAQ,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;gBAC9B,CAAC;qBAAM,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;oBAChC,QAAQ,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACN,OAAO,SAAS,CAAC;gBACnB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAI,SAAS,KAAK,QAAQ,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,mHAAmH;IACnH,sHAAsH;IACtH,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;YACtE,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,YAAY,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,YAAY,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,uFAAuF;IACvF,6FAA6F;IAC7F,qCAAqC;IACrC,IAAI,sBAA0C,CAAC;IAC/C,IAAI,uBAA2C,CAAC;IAChD,IAAI,YAAY,KAAK,UAAU,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;QACjE,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/E,sBAAsB,GAAG,GAAG,EAAE,QAAQ,CAAC;QACvC,uBAAuB,GAAG,GAAG,EAAE,eAAe,EAAE,QAAQ,CAAC;IAC3D,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,CAAC;AAC5G,CAAC;AAED,SAAS,cAAc,CAAC,IAAc;IACpC,OAAO,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,aAAa,KAAK,iCAAa,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,KAAK,MAAM,CAAC;AAClI,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,SAAyC;IAC1E,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;QAC5C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,wFAAwF;QACxF,MAAM,OAAO,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC;QAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,EAAE,CAAC;YAC9E,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,GAAG,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,wFAAwF;YACxF,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACjE,CAAC;IACH,CAAC;IAED,gGAAgG;IAChG,6FAA6F;IAC7F,2EAA2E;IAC3E,QAAQ,OAAO,GAAG,EAAE,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC1C,KAAK,SAAS;YACZ,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACzC,KAAK,QAAQ;YACX,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACxC;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAAC,IAAc;IAChD,IAAI,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QACzB,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;YAC1B,KAAK,gCAAY,CAAC,mBAAmB;gBACnC,OAAO,UAAU,CAAC;YACpB,KAAK,gCAAY,CAAC,kBAAkB;gBAClC,OAAO,aAAa,CAAC;YACvB;gBACE,OAAO,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;IAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,QAAQ,IAAI,CAAC,aAAa,EAAE,CAAC;YAC3B,KAAK,iCAAa,CAAC,OAAO;gBACxB,OAAO,SAAS,CAAC;YACnB,KAAK,iCAAa,CAAC,MAAM;gBACvB,OAAO,IAAI,CAAC,gBAAgB,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;YACtE,KAAK,iCAAa,CAAC,QAAQ;gBACzB,OAAO,UAAU,CAAC;YACpB,KAAK,iCAAa,CAAC,MAAM,CAAC;YAC1B,KAAK,iCAAa,CAAC,OAAO,CAAC;YAC3B,KAAK,iCAAa,CAAC,IAAI;gBACrB,wFAAwF;gBACxF,qFAAqF;gBACrF,oFAAoF;gBACpF,uFAAuF;gBACvF,OAAO,UAAU,CAAC;YACpB,KAAK,iCAAa,CAAC,OAAO,CAAC;YAC3B,KAAK,iCAAa,CAAC,OAAO;gBACxB,OAAO,YAAY,CAAC;YACtB,KAAK,iCAAa,CAAC,MAAM;gBACvB,OAAO,IAAI,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YACnE;gBACE,OAAO,SAAS,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAgB,mBAAmB,CAAC,aAAiC,EAAE,MAAgB,EAAE,OAAgB;IACvG,OAAO;QACL,aAAa;QACb,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC;QACxF,UAAU,EAAE,IAAA,+BAAkB,EAAC,MAAM,CAAC;KACvC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,sBAAsB,CAAC,KAAyB,EAAE,KAAe,EAAE,OAA4B;IACtG,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACtC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;QAC9E,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,eAAe,GAAG,KAAK,CAAC,aAAa,EAAE,QAAQ,CAAC;IACtD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,sBAAsB,EAAE,GAAG,IAAA,4BAAe,EAAC,eAAe,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IACzG,IAAI,sBAAsB,EAAE,CAAC;QAC3B,qBAAM,CAAC,UAAU,CAAC,6CAAqB,CAAC,QAAQ,EAAE,2FAA2F,EAAE,GAAG,EAAE,CAAC,CAAC;YACpJ,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS;YACvC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,YAAY;YAC7C,eAAe,EAAE,sBAAsB;YACvC,uBAAuB,EAAE,KAAK,CAAC,uBAAuB;SACvD,CAAC,CAAC,CAAC;IACN,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,qBAAM,CAAC,UAAU,CAAC,6CAAqB,CAAC,QAAQ,EAAE,mEAAmE,EAAE,GAAG,EAAE,CAAC,CAAC;gBAC5H,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC,SAAS;gBACvC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,YAAY;gBAC7C,SAAS,EAAE,eAAe,EAAE,SAAS;gBACrC,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,mBAAmB,EAAE,CAAC;aACtE,CAAC,CAAC,CAAC;QACN,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,KAAe,EAAE,OAA4B;IACvE,IAAI,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,aAAa,KAAK,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC;QACpF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,UAAU,GAAG,IAAA,8BAAgB,EAAC;gBAC5B,KAAK,EAAE,SAAS;gBAChB,OAAO,EAAE,KAAK,CAAC,aAAa;gBAC5B,eAAe,EAAE,sBAAsB,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC;aACnE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qBAAM,CAAC,QAAQ,CAAC,6CAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,UAAU,GAAG,UAAU,IAAI,sBAAQ,CAAC,uBAAuB,CAAC;IAC5D,IAAI,UAAU,KAAK,KAAK,CAAC,aAAa,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,SAAoB,EAAE,OAA4B;IAC7E,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,IAAA,wCAA0B,EAAC,SAAS,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;YAC1D,EAAE,UAAU,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,YAAwB,EAAE,QAAgC,EAAE,OAAgB;IAChH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,IAAA,+CAAiB,EAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/D,MAAM,aAAa,GAAG,EAAE,CAAC;YACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC;gBAC3C,IAAI,YAAY,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC3C,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;gBACvC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,qBAAM,CAAC,QAAQ,CAAC,6CAAqB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,KAAwB,EAAE,GAAY,EAAE,OAAgB;IAC1F,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,mBAA+B,EAAE,GAAY;IAC3E,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAC7D,CAAC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, FormatMagnitude, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport { FieldFormatting, getFieldFormatting, lookupFieldSpec, ResolvedFieldValue } from \"./fieldSpecs\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\n/** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to\r\n * read a field's property value, and how to format quantities.\r\n */\r\nexport interface UpdateFieldsContext {\r\n /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —\r\n * an edit to one source element leaves the annotation's other fields untouched.\r\n */\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n /** Reads the value a field points at, or `undefined` if it cannot be resolved — including\r\n * every field when the source element was deleted.\r\n */\r\n getProperty(field: FieldRun): ResolvedFieldValue | undefined;\r\n\r\n /** The formats `\"quantity\"` and `\"coordinate\"` values resolve through. [[updateField]] builds\r\n * a [FormatterSpec]($core-quantity) per field from the FormatSet named by\r\n * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and\r\n * the schemas; a value none of them can format falls back to `value.toString()`.\r\n * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in\r\n * tests that never format a quantity leave it out.\r\n */\r\n readonly formatting?: FieldFormatting;\r\n}\r\n\r\n// Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for\r\n// `\"quantity\"` / `\"coordinate\"` types, the property-side KoQ and persistence unit.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): ResolvedFieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors, jsonAccessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the\r\n // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.\r\n if (jsonAccessors && jsonAccessors.length > 0) {\r\n if (!isJsonProperty(ecProp) || typeof curValue.primitive !== \"string\") {\r\n return undefined;\r\n }\r\n\r\n return readJsonLeaf(curValue.primitive, jsonAccessors);\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are\r\n // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback\r\n // when the override doesn't resolve.\r\n let kindOfQuantityFullName: string | undefined;\r\n let persistenceUnitFullName: string | undefined;\r\n if (propertyType === \"quantity\" || propertyType === \"coordinate\") {\r\n const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;\r\n kindOfQuantityFullName = koq?.fullName;\r\n persistenceUnitFullName = koq?.persistenceUnit?.fullName;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };\r\n}\r\n\r\nfunction isJsonProperty(prop: Property): boolean {\r\n return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === PrimitiveType.String && prop.extendedTypeName === \"Json\";\r\n}\r\n\r\n/** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,\r\n * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC\r\n * metadata is consulted.\r\n *\r\n * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path\r\n * stops anywhere but a scalar — including on a JSON `null`, which is not a\r\n * [FieldPrimitiveValue]($common).\r\n */\r\nfunction readJsonLeaf(raw: string, accessors: ReadonlyArray<string | number>): FieldValue | undefined {\r\n let cur: any;\r\n try {\r\n cur = JSON.parse(raw);\r\n } catch {\r\n // Not valid JSON; treat as a normal string.\r\n return undefined;\r\n }\r\n\r\n for (const accessor of accessors) {\r\n // A number indexes an array and a string keys an object; anything else doesn't resolve.\r\n const isIndex = typeof accessor === \"number\";\r\n if (typeof cur !== \"object\" || null === cur || Array.isArray(cur) !== isIndex) {\r\n return undefined;\r\n }\r\n\r\n if (isIndex) {\r\n cur = cur[accessor < 0 ? cur.length + accessor : accessor];\r\n } else {\r\n // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.\r\n cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;\r\n }\r\n }\r\n\r\n // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.\r\n // A numeric leaf is typed `\"quantity\"` but has no property-side KoQ, so only the field's own\r\n // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.\r\n switch (typeof cur) {\r\n case \"number\":\r\n return { value: cur, type: \"quantity\" };\r\n case \"boolean\":\r\n return { value: cur, type: \"boolean\" };\r\n case \"string\":\r\n return { value: cur, type: \"string\" };\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n // Any numeric property is a potential quantity. Classifying one as \"quantity\" is not an\r\n // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is\r\n // consulted. A number that resolves no spec falls back to the same `toString()` the\r\n // \"string\" formatter would have produced, so counts and identifiers still render bare.\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n formatting: getFieldFormatting(iModel),\r\n };\r\n}\r\n\r\n/** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,\r\n * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats\r\n * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the\r\n * field may format through. In that last case the shortfall is logged: a persistence unit or\r\n * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a\r\n * format whose units belong to a different phenomenon than the persisted value. A\r\n * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged\r\n * too; the property's unit is what the stored magnitude means.\r\n */\r\nfunction resolveFormatMagnitude(value: ResolvedFieldValue, field: FieldRun, context: UpdateFieldsContext): FormatMagnitude | undefined {\r\n const formatting = context.formatting;\r\n if (!formatting || (value.type !== \"quantity\" && value.type !== \"coordinate\")) {\r\n return undefined;\r\n }\r\n\r\n const quantityOptions = field.formatOptions?.quantity;\r\n const { spec, candidates, ignoredPersistenceUnit } = lookupFieldSpec(quantityOptions, value, formatting);\r\n if (ignoredPersistenceUnit) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"Ignoring persistenceUnit override that disagrees with the property's own persistence unit\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n persistenceUnit: ignoredPersistenceUnit,\r\n propertyPersistenceUnit: value.persistenceUnitFullName,\r\n }));\r\n }\r\n if (!spec) {\r\n if (candidates.length > 0) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"No format resolved for text annotation field; rendering raw value\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n formatSet: quantityOptions?.formatSet,\r\n tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),\r\n }));\r\n }\r\n return undefined;\r\n }\r\n\r\n return (magnitude) => spec.applyFormatting(magnitude);\r\n}\r\n\r\n/** Recomputes a single field's cached display string synchronously. Returns true iff\r\n * cachedContent changed.\r\n *\r\n * Resolving the property value and formatting it are both fallible. A failure of either is\r\n * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other\r\n * fields in the same block are unaffected.\r\n */\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue({\r\n value: propValue,\r\n options: field.formatOptions,\r\n formatMagnitude: resolveFormatMagnitude(propValue, field, context),\r\n });\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number\r\n * whose cached display string changed. Fields targeting an element other than\r\n * `context.hostElementId` (when set) are skipped.\r\n */\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element\r\n * change (`deleted=false`) or delete (`deleted=true`). Invoked from\r\n * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.\r\n */\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\n/** Re-evaluates every field of the given annotation element against its current property\r\n * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when\r\n * establishing / refreshing relationships.\r\n */\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
@@ -188,7 +188,7 @@ export declare class ChangesetReader implements Disposable, ChangeSource {
188
188
  * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
189
189
  *
190
190
  * Default batch sizes when `setBatchSize` is not called:
191
- * - `InstanceKey` filter: **100**.
191
+ * - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.
192
192
  * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
193
193
  * - `All` filter, `abbreviateBlobs: false`: **5**.
194
194
  * - `All` filter (blobs abbreviated or unset): **10**.
@@ -62,7 +62,7 @@ export class ChangesetReader {
62
62
  get _batchSize() {
63
63
  if (this._batchSizeOverride !== undefined)
64
64
  return this._batchSizeOverride;
65
- if (this._propFilter === PropertyFilter.InstanceKey)
65
+ if (this._propFilter === PropertyFilter.InstanceKey || this._propFilter === PropertyFilter.InstanceKeyAndIdentifiers)
66
66
  return 100;
67
67
  if (this._propFilter === PropertyFilter.BisCoreElement)
68
68
  return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here
@@ -330,7 +330,7 @@ export class ChangesetReader {
330
330
  * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.
331
331
  *
332
332
  * Default batch sizes when `setBatchSize` is not called:
333
- * - `InstanceKey` filter: **100**.
333
+ * - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.
334
334
  * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.
335
335
  * - `All` filter, `abbreviateBlobs: false`: **5**.
336
336
  * - `All` filter (blobs abbreviated or unset): **10**.
@@ -1 +1 @@
1
- {"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAC/F;;GAEG;AACH,OAAO,EAAE,QAAQ,EAAc,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAqD,cAAc,EAAoB,MAAM,wBAAwB,CAAC;AAI7H,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,eAAe;IAClB,MAAM,CAAU,4BAA4B,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,SAAS;IACjE,aAAa,GAAmC,IAAI,YAAY,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC;IAC7G,+EAA+E;IACvE,WAAW,CAAoB;IAC/B,kBAAkB,CAAU;IAC5B,WAAW,GAAmB,cAAc,CAAC,GAAG,CAAC;IACjD,YAAY,GAAG,CAAC,CAAC;IACzB,yDAAyD;IACjD,MAAM,GAAsC,EAAE,CAAC;IACvD;;;;OAIG;IACK,WAAW,GAAG,CAAC,CAAC;IACxB,uHAAuH;IAC/G,eAAe,GAA+B,SAAS,CAAC;IAChE,sHAAsH;IAC9G,cAAc,GAA+B,SAAS,CAAC;IAE/D,4CAA4C;IAC5B,EAAE,CAAQ;IAE1B;mBACe;IACf,IAAY,WAAW;QACrB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;YACxC,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,UAAU,EAAE,sDAAsD,CAAC,CAAC;QACzG,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACvC,CAAC;IAED;mBACe;IACf,IAAY,UAAU;QACpB,IAAI,IAAI,CAAC,kBAAkB,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,kBAAkB,CAAC;QAC1E,IAAI,IAAI,CAAC,WAAW,KAAK,cAAc,CAAC,WAAW;YAAE,OAAO,GAAG,CAAC;QAChE,IAAI,IAAI,CAAC,WAAW,KAAK,cAAc,CAAC,cAAc;YAAE,OAAO,EAAE,CAAC,CAAC,+GAA+G;QAClL,IAAI,IAAI,CAAC,WAAW,EAAE,eAAe,KAAK,KAAK;YAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,EAAE,CAAC,CAAC,qBAAqB;IAClC,CAAC;IAED;;;;;OAKG;IACH,IAAW,SAAS,KAAc,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAE/E;;;;;OAKG;IACH,IAAW,SAAS,KAAa,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAE9E;;;;;OAKG;IACH,IAAW,gBAAgB,KAAc,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAE7F;;;;;;;;OAQG;IACH,IAAW,QAAQ;QACjB,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9F,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS;YACxB,OAAO,SAAS,CAAC;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,eAAe,GAAG;YAC7B,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI;YACrB,KAAK,EAAE;gBACL,EAAE;gBACF,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAChC,aAAa,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;gBAClC,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG;gBAC9B,UAAU,EAAE,IAAI,CAAC,WAAW;gBAC5B,sBAAsB,EAAE,GAAG,CAAC,SAAS,CAAC,sBAAsB;gBAC5D,UAAU,EAAE,IAAI,CAAC,WAAW;gBAC5B,gBAAgB,EAAE,GAAG,CAAC,QAAQ,CAAC,gBAAgB;aAChD;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,IAAW,OAAO;QAChB,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,cAAc,CAAC;QAClE,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9F,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS;YACxB,OAAO,SAAS,CAAC;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG;YAC5B,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI;YACrB,KAAK,EAAE;gBACL,EAAE;gBACF,MAAM,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAChC,aAAa,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;gBAClC,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,GAAG,CAAC,SAAS,CAAC,GAAG;gBAC9B,UAAU,EAAE,IAAI,CAAC,WAAW;gBAC5B,sBAAsB,EAAE,GAAG,CAAC,SAAS,CAAC,sBAAsB;gBAC5D,UAAU,EAAE,IAAI,CAAC,WAAW;gBAC5B,gBAAgB,EAAE,GAAG,CAAC,QAAQ,CAAC,gBAAgB;aAChD;SACF,CAAC,CAAC;IACL,CAAC;IAED,gDAAgD;IAChD,YAAoB,EAAS;QAC3B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED;mBACe;IACP,kBAAkB,CAAC,IAAsB;QAC/C,OAAO;YACL,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAC/C,4DAA4D;YAC5D,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,yBAAyB;IACzB,8EAA8E;IAE9E;;;;;;;;;OASG;IACI,MAAM,CAAC,QAAQ,CAAC,IAAyD;QAC9E,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAC7G,CAAC;QACD,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAC,SAAS,CAAC,IAAiG;QACvH,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC;YAClC,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,MAAM,EAAE,gDAAgD,CAAC,CAAC;QAC/F,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QACrL,CAAC;QACD,OAAO,CAAC,EAAE,CAAC;YACT,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAC,gBAAgB,CAC5B,IAA0H;QAE1H,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,sBAAsB,IAAI,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC7M,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,MAAM,CAAC,mBAAmB,CAC/B,IAAwF;QAExF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC1K,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,MAAM,CAAC,OAAO,CACnB,IAA2G;QAE3G,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,cAAc,CAAC,GAAG,CAAC;QACzD,MAAM,CAAC,WAAW,GAAG,UAAU,CAAC;QAChC,IAAI,CAAC;YACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,qBAAqB,IAAI,IAAI,CAAC,4BAA4B,CAAC,CAAC;QAC1K,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,CAAC;QACV,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;mBACe;IACP,qBAAqB;QAC3B,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC;YACvB,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,UAAU,EAAE,6GAA6G,CAAC,CAAC;IAClK,CAAC;IAED,gHAAgH;IACxG,4BAA4B,CAAC,CAAU;QAC7C,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,MAAM,EAAE,6CAA6C,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;kEAEtE,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;gDACvF,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,YAAY,CAAC,SAAiB;QACnC,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;YAChD,MAAM,IAAI,WAAW,CAAC,YAAY,CAAC,MAAM,EAAE,wDAAwD,CAAC,CAAC;QACvG,IAAI,CAAC,kBAAkB,GAAG,SAAS,CAAC;IACtC,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;;OAOG;IACI,mBAAmB,CAAC,UAAuB;QAChD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;OAMG;IACI,gBAAgB,CAAC,GAAwB;QAC9C,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;;OAOG;IACI,mBAAmB,CAAC,UAAuB;QAChD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACI,qBAAqB;QAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACI,kBAAkB;QACvB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACI,qBAAqB;QAC1B,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,CAAC;IAC7C,CAAC;IAED,8EAA8E;IAC9E,cAAc;IACd,8EAA8E;IAE9E;;;;;;;;;;;;;;;;;OAiBG;IACI,gBAAgB;QACrB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,gBAAgB,EAAE,CAAC;IACxC,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,iBAAiB;QACtB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;IACzC,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;OAMG;IACI,IAAI;QACT,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC9C,iDAAiD;YACjD,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YACtE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;YACrB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,IAAW,EAAE;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC;QAChD,OAAO,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU;YAC5C,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACtC,CAAC,CAAC,SAAS,CAAC;IAClB,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E;;;;;;;OAOG;IACI,KAAK;QACV,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACI,CAAC,MAAM,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECDb\r\n */\r\nimport { DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\r\nimport { IModelError } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"./IModelDb\";\r\nimport { IModelNative } from \"./internal/NativePlatform\";\r\nimport { _nativeDb } from \"./internal/Symbols\";\r\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\r\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\r\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChangesetReader\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\r\n * in-memory transaction, or local un-pushed changes.\r\n *\r\n * Implements [ChangeSource]($backend) so rows can be fed directly into\r\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\r\n * complete EC instances.\r\n *\r\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\r\n * and both [[inserted]] and [[deleted]] remain `undefined`.\r\n *\r\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\r\n * instances must be merged using [PartialChangeUnifier]($backend).\r\n * @beta\r\n */\r\nexport class ChangesetReader implements Disposable, ChangeSource {\r\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\r\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\r\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\r\n private _rowOptions?: RowFormatOptions;\r\n private _batchSizeOverride?: number;\r\n private _propFilter: PropertyFilter = PropertyFilter.All;\r\n private _changeIndex = 0;\r\n /** Rows fetched in the most recent native batch call. */\r\n private _cache: IModelJsNative.ChangesetRowData[] = [];\r\n /**\r\n * Index of the current row in `_cache`.\r\n * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:\r\n * initial state, after exhaustion, or after close().\r\n */\r\n private _cacheIndex = 0;\r\n /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedInserted: ChangeInstance | undefined = undefined;\r\n /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedDeleted: ChangeInstance | undefined = undefined;\r\n\r\n /** The db used for EC schema resolution. */\r\n public readonly db: AnyDb;\r\n\r\n /** Returns the active cached row, throwing if no row is current.\r\n * @internal */\r\n private get _currentRow(): IModelJsNative.ChangesetRowData {\r\n if (this._cacheIndex >= this._cache.length)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: no current row — call step() first.\");\r\n return this._cache[this._cacheIndex];\r\n }\r\n\r\n /** Returns the batch size to use for native step() calls based on the active property filter.\r\n * @internal */\r\n private get _batchSize(): number {\r\n if (this._batchSizeOverride !== undefined) return this._batchSizeOverride;\r\n if (this._propFilter === PropertyFilter.InstanceKey) return 100;\r\n if (this._propFilter === PropertyFilter.BisCoreElement) return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here\r\n if (this._rowOptions?.abbreviateBlobs === false) return 5;\r\n return 10; // PropertyFilter.All\r\n }\r\n\r\n /**\r\n * `true` when the current row belongs to an EC-mapped table.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isECTable(): boolean { return this._currentRow.metadata.isECTable; }\r\n\r\n /**\r\n * Name of the SQLite table for the current change row.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get tableName(): string { return this._currentRow.metadata.tableName; }\r\n\r\n /**\r\n * `true` when the current change was applied indirectly\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isIndirectChange(): boolean { return this._currentRow.metadata.isIndirectChange; }\r\n\r\n /**\r\n * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\r\n * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and\r\n * deleted instances indicate the old state of the instance before the change has been applied.\r\n * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.\r\n * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.\r\n * @beta\r\n */\r\n public get inserted(): ChangeInstance | undefined {\r\n if (this._cachedInserted !== undefined) return this._cachedInserted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.newValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedInserted = {\r\n ...row.newValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"New\",\r\n instanceKey: row.newValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.newValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\r\n * @beta\r\n */\r\n public get deleted(): ChangeInstance | undefined {\r\n if (this._cachedDeleted !== undefined) return this._cachedDeleted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.oldValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedDeleted = {\r\n ...row.oldValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"Old\",\r\n instanceKey: row.oldValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.oldValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n // Private — callers use static factory methods.\r\n private constructor(db: AnyDb) {\r\n this.db = db;\r\n }\r\n\r\n /** Map public RowFormatOptions to the native adaptor options.\r\n * @internal */\r\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\r\n return {\r\n abbreviateBlobs: opts.abbreviateBlobs,\r\n classIdsToClassNames: opts.classIdsToClassNames,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n useJsName: opts.useJsName,\r\n };\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Static factory methods\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Open a changeset file from disk.\r\n * @param args.fileName Absolute path to the changeset file.\r\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @throws if the native layer fails to open the file.\r\n * @beta\r\n */\r\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Concatenate multiple changeset files and read them as a single logical stream.\r\n * @param args.changesetFiles Ordered list of changeset file paths.\r\n * @param args.db Database with schema at or ahead of the last changeset.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\r\n * the group.\r\n * @beta\r\n */\r\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\r\n if (args.changesetFiles.length === 0)\r\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read pending (not yet pushed) local changes from an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\r\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer\r\n * fails to open the local changes.\r\n * @beta\r\n */\r\n public static openLocalChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend).\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer encounters an error while opening the in-memory changes.\r\n * @beta\r\n */\r\n public static openInMemoryChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read a single saved transaction by its id.\r\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\r\n * @param args.txnId The id of the saved transaction to read.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `txnId` is not found, or\r\n * the native layer fails to open the transaction data.\r\n * @beta\r\n */\r\n public static openTxn(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.\r\n * @internal */\r\n private throwIfAlreadyStepped(): void {\r\n if (this._changeIndex > 0)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().\");\r\n }\r\n\r\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\r\n private handleCloseErrorWhileOpening(e: unknown): void {\r\n try {\r\n this.close();\r\n } catch (closeError) {\r\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\r\n Additionally, that triggered an automatic closure of the reader\r\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\r\n Check native error logs for more details.`);\r\n }\r\n }\r\n\r\n /**\r\n * Set the number of rows to fetch and cache while stepping.\r\n * This is an advanced option that can be used to tune performance for large changesets.\r\n * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.\r\n *\r\n * Default batch sizes when `setBatchSize` is not called:\r\n * - `InstanceKey` filter: **100**.\r\n * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.\r\n * - `All` filter, `abbreviateBlobs: false`: **5**.\r\n * - `All` filter (blobs abbreviated or unset): **10**.\r\n *\r\n * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.\r\n * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.\r\n * @beta\r\n */\r\n public setBatchSize(batchSize: number): void {\r\n this.throwIfAlreadyStepped();\r\n if (!Number.isInteger(batchSize) || batchSize <= 0)\r\n throw new IModelError(IModelStatus.BadArg, \"ChangesetReader: batchSize must be a positive integer.\");\r\n this._batchSizeOverride = batchSize;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Filtering\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Restrict iteration to changes from the named SQLite tables.\r\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\r\n * @param tableNames SQLite table names to include.\r\n * Note: Table names must be provided in the correct case for proper filtering.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setTableNameFilters(tableNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes with the given operation types.\r\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\r\n * @param ops Operations to include.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setOpCodeFilters(Array.from(ops));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes for the given EC class names.\r\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\r\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\r\n * Note: Schema names and class names must be provided in the correct case for proper filtering. Derived classes are not automatically included, so they must be specified explicitly if needed.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setClassNameFilters(classNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setClassNameFilters(Array.from(classNames));\r\n }\r\n\r\n /**\r\n * Remove the table-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearTableNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearTableNameFilters();\r\n }\r\n\r\n /**\r\n * Remove the op-code filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearOpCodeFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearOpCodeFilters();\r\n }\r\n\r\n /**\r\n * Remove the class-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearClassNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearClassNameFilters();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Strict mode\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Enable strict mode on the reader.\r\n *\r\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\r\n * record and the corresponding live database table. Such a mismatch can occur when columns\r\n * have been added to a table after the changeset was created.\r\n *\r\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader throws an error\r\n * instead of processing that row.\r\n *\r\n * Use strict mode when you need to be certain that every change row is interpreted against\r\n * exactly the schema that was in effect when the changeset was written.\r\n *\r\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public enableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.enableStrictMode();\r\n }\r\n\r\n /**\r\n * Disable strict mode on the reader (this is the default).\r\n *\r\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader takes the\r\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\r\n * because SQLite only ever appends new columns at the end of a table and never removes them —\r\n * so older change records simply lack the trailing columns that were added later, and those\r\n * missing columns are silently ignored.\r\n *\r\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public disableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.disableStrictMode();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Iteration\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Advance to the next change.\r\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\r\n * @throws if the native layer encounters an error while reading or decoding\r\n * the next change.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n if (this._cacheIndex + 1 < this._cache.length) {\r\n // Still have rows in cache — advance the pointer\r\n this._cacheIndex++;\r\n } else {\r\n // Cache empty or fully consumed — fetch next batch from native\r\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\r\n this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);\r\n this._cacheIndex = 0;\r\n if (this._cache.length === 0) return false;\r\n }\r\n this._changeIndex++;\r\n return true;\r\n }\r\n\r\n /**\r\n * SQLite opcode of the current change.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get op(): SqliteChangeOp {\r\n const opCode = this._currentRow.metadata.opCode;\r\n return opCode === DbOpcode.Insert ? \"Inserted\"\r\n : opCode === DbOpcode.Update ? \"Updated\"\r\n : \"Deleted\";\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Lifecycle\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Close the reader and release all native resources.\r\n *\r\n * @throws if the native layer encounters an error during cleanup. Native resources\r\n * are not fully released when this throws — check the native error\r\n * logs for details.\r\n * @beta\r\n */\r\n public close(): void {\r\n this._changeIndex = 0;\r\n this._cache = [];\r\n this._cacheIndex = 0;\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n this._nativeReader.close();\r\n }\r\n\r\n /**\r\n * Implements the `Disposable` contract — delegates to [[close]].\r\n *\r\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\r\n * @beta\r\n */\r\n public [Symbol.dispose](): void {\r\n this.close();\r\n }\r\n}\r\n\r\n"]}
1
+ {"version":3,"file":"ChangesetReader.js","sourceRoot":"","sources":["../../src/ChangesetReader.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n/** @packageDocumentation\r\n * @module ECDb\r\n */\r\nimport { DbOpcode, Id64String, IModelStatus } from \"@itwin/core-bentley\";\r\nimport { IModelError } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"./IModelDb\";\r\nimport { IModelNative } from \"./internal/NativePlatform\";\r\nimport { _nativeDb } from \"./internal/Symbols\";\r\nimport { IModelJsNative } from \"@bentley/imodeljs-native\";\r\nimport { ChangeInstance, ChangesetReaderArgs, ChangeSource, PropertyFilter, RowFormatOptions } from \"./ChangesetReaderTypes\";\r\nimport { AnyDb, SqliteChangeOp } from \"./SqliteChangesetReader\";\r\n\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChangesetReader\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Reads EC-typed changeset data natively from a changeset file, changeset group,\r\n * in-memory transaction, or local un-pushed changes.\r\n *\r\n * Implements [ChangeSource]($backend) so rows can be fed directly into\r\n * [PartialChangeUnifier]($backend) to merge partial (per-table) instances into\r\n * complete EC instances.\r\n *\r\n * When the current row is a non-EC internal SQLite table, [[isECTable]] is `false`\r\n * and both [[inserted]] and [[deleted]] remain `undefined`.\r\n *\r\n * @note The native reader operates one SQLite table-row at a time. Multi-table EC\r\n * instances must be merged using [PartialChangeUnifier]($backend).\r\n * @beta\r\n */\r\nexport class ChangesetReader implements Disposable, ChangeSource {\r\n private static readonly defaultSpillThresholdInBytes = 50 * 1024 * 1024; // 50 MiB\r\n private readonly _nativeReader: IModelJsNative.ChangesetReader = new IModelNative.platform.ChangesetReader();\r\n // Internal options — keep ECClassId as raw Id so the unifier can use it as-is.\r\n private _rowOptions?: RowFormatOptions;\r\n private _batchSizeOverride?: number;\r\n private _propFilter: PropertyFilter = PropertyFilter.All;\r\n private _changeIndex = 0;\r\n /** Rows fetched in the most recent native batch call. */\r\n private _cache: IModelJsNative.ChangesetRowData[] = [];\r\n /**\r\n * Index of the current row in `_cache`.\r\n * Equals `_cache.length` (i.e. out-of-bounds) when no row is active:\r\n * initial state, after exhaustion, or after close().\r\n */\r\n private _cacheIndex = 0;\r\n /** Cached result of the `inserted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedInserted: ChangeInstance | undefined = undefined;\r\n /** Cached result of the `deleted` getter for the current row. `undefined` when not yet computed or not applicable. */\r\n private _cachedDeleted: ChangeInstance | undefined = undefined;\r\n\r\n /** The db used for EC schema resolution. */\r\n public readonly db: AnyDb;\r\n\r\n /** Returns the active cached row, throwing if no row is current.\r\n * @internal */\r\n private get _currentRow(): IModelJsNative.ChangesetRowData {\r\n if (this._cacheIndex >= this._cache.length)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: no current row — call step() first.\");\r\n return this._cache[this._cacheIndex];\r\n }\r\n\r\n /** Returns the batch size to use for native step() calls based on the active property filter.\r\n * @internal */\r\n private get _batchSize(): number {\r\n if (this._batchSizeOverride !== undefined) return this._batchSizeOverride;\r\n if (this._propFilter === PropertyFilter.InstanceKey || this._propFilter === PropertyFilter.InstanceKeyAndIdentifiers) return 100;\r\n if (this._propFilter === PropertyFilter.BisCoreElement) return 20; // because BisCore Element class do not contain any GeomStream property so abbreviateBlobs is not relevant here\r\n if (this._rowOptions?.abbreviateBlobs === false) return 5;\r\n return 10; // PropertyFilter.All\r\n }\r\n\r\n /**\r\n * `true` when the current row belongs to an EC-mapped table.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isECTable(): boolean { return this._currentRow.metadata.isECTable; }\r\n\r\n /**\r\n * Name of the SQLite table for the current change row.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get tableName(): string { return this._currentRow.metadata.tableName; }\r\n\r\n /**\r\n * `true` when the current change was applied indirectly\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get isIndirectChange(): boolean { return this._currentRow.metadata.isIndirectChange; }\r\n\r\n /**\r\n * Post-change (inserted or updated-new) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is a Delete or a non-EC table row or [[step]] returned false.\r\n * For UPDATE,inserted instances indicate the new state of the instance after the change has been applied and\r\n * deleted instances indicate the old state of the instance before the change has been applied.\r\n * For INSERT, inserted instances indicate the new state of the instance after the change has been applied and deleted instances are undefined.\r\n * For DELETE, deleted instances indicate the old state of the instance before the change has been applied and inserted instances are undefined.\r\n * @beta\r\n */\r\n public get inserted(): ChangeInstance | undefined {\r\n if (this._cachedInserted !== undefined) return this._cachedInserted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.newValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedInserted = {\r\n ...row.newValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"New\",\r\n instanceKey: row.newValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.newValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n /**\r\n * Pre-change (deleted or updated-old) EC instance, computed lazily after each [[step]] call.\r\n * `undefined` when the current row is an Insert or a non-EC table row or [[step]] returned false.\r\n * @beta\r\n */\r\n public get deleted(): ChangeInstance | undefined {\r\n if (this._cachedDeleted !== undefined) return this._cachedDeleted;\r\n const row = this._cacheIndex < this._cache.length ? this._cache[this._cacheIndex] : undefined;\r\n if (!row || !row.oldValues)\r\n return undefined;\r\n const op = this.op;\r\n return (this._cachedDeleted = {\r\n ...row.oldValues.data,\r\n $meta: {\r\n op,\r\n tables: [row.metadata.tableName],\r\n changeIndexes: [this._changeIndex],\r\n stage: \"Old\",\r\n instanceKey: row.oldValues.key,\r\n propFilter: this._propFilter,\r\n changeFetchedPropNames: row.oldValues.changeFetchedPropNames,\r\n rowOptions: this._rowOptions,\r\n isIndirectChange: row.metadata.isIndirectChange,\r\n },\r\n });\r\n }\r\n\r\n // Private — callers use static factory methods.\r\n private constructor(db: AnyDb) {\r\n this.db = db;\r\n }\r\n\r\n /** Map public RowFormatOptions to the native adaptor options.\r\n * @internal */\r\n private toNativeRowOptions(opts: RowFormatOptions): IModelJsNative.ECSqlRowAdaptorOptions {\r\n return {\r\n abbreviateBlobs: opts.abbreviateBlobs,\r\n classIdsToClassNames: opts.classIdsToClassNames,\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated\r\n useJsName: opts.useJsName,\r\n };\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Static factory methods\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Open a changeset file from disk.\r\n * @param args.fileName Absolute path to the changeset file.\r\n * @param args.db Database at or after the changeset's ending state, used for schema resolution.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @throws if the native layer fails to open the file.\r\n * @beta\r\n */\r\n public static openFile(args: { readonly fileName: string } & ChangesetReaderArgs): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openFile(args.db[_nativeDb], args.fileName, args.invert ?? false, reader._propFilter);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Concatenate multiple changeset files and read them as a single logical stream.\r\n * @param args.changesetFiles Ordered list of changeset file paths.\r\n * @param args.db Database with schema at or ahead of the last changeset.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the changeset data in the change group exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for processing large changeset groups under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `changesetFiles` is empty, or if the native layer fails to open\r\n * the group.\r\n * @beta\r\n */\r\n public static openGroup(args: { readonly changesetFiles: string[], spillThresholdInBytes?: number } & ChangesetReaderArgs): ChangesetReader {\r\n if (args.changesetFiles.length === 0)\r\n throw new IModelError(IModelStatus.BadArg, \"changesetFiles must contain at least one file.\");\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openGroup(args.db[_nativeDb], args.changesetFiles, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n }\r\n catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read pending (not yet pushed) local changes from an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend) (not [ECDb]($backend)).\r\n * @param args.includeInMemoryChanges Also include in-memory (not yet saved to disk) changes.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of all local un-pushed saved changes exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for iModels with large local change backlogs under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer\r\n * fails to open the local changes.\r\n * @beta\r\n */\r\n public static openLocalChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; includeInMemoryChanges?: boolean, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openLocalChanges(args.db[_nativeDb], args.includeInMemoryChanges ?? false, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read the in-memory (not yet saved to disk) changes of an open IModelDb.\r\n * @param args.db Must be an [IModelDb]($backend).\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the in-memory (unsaved) change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large in-memory transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if the native layer encounters an error while opening the in-memory changes.\r\n * @beta\r\n */\r\n public static openInMemoryChanges(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openInMemoryChanges(args.db[_nativeDb], args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /**\r\n * Read a single saved transaction by its id.\r\n * @param args.db Must be an [IModelDb]($backend) ([ECDb]($backend) does not support transactions).\r\n * @param args.txnId The id of the saved transaction to read.\r\n * @param args.invert When `true`, invert all operations (Insert↔Delete, New↔Old).\r\n * @param args.rowOptions Row adaptor options controlling how EC property values are formatted.\r\n * @param args.propFilter Controls which properties are included. Defaults to `All`.\r\n * @param args.spillThresholdInBytes When the total size of the transaction's change data exceeds this threshold (in bytes),\r\n * the reader writes the data to a temporary file on disk and streams it from there instead of buffering everything in memory.\r\n * This keeps peak memory usage bounded, making the API suitable for large transactions under low-memory conditions.\r\n * Defaults to 50 MiB.\r\n * @throws if `txnId` is not found, or\r\n * the native layer fails to open the transaction data.\r\n * @beta\r\n */\r\n public static openTxn(\r\n args: Omit<ChangesetReaderArgs, \"db\"> & { db: IModelDb; txnId: Id64String, spillThresholdInBytes?: number },\r\n ): ChangesetReader {\r\n const reader = new ChangesetReader(args.db);\r\n reader._rowOptions = args.rowOptions;\r\n const propFilter = args.propFilter ?? PropertyFilter.All;\r\n reader._propFilter = propFilter;\r\n try {\r\n reader._nativeReader.openTxn(args.db[_nativeDb], args.txnId, args.invert ?? false, reader._propFilter, args.spillThresholdInBytes ?? this.defaultSpillThresholdInBytes);\r\n } catch (e) {\r\n reader.handleCloseErrorWhileOpening(e);\r\n throw e;\r\n }\r\n return reader;\r\n }\r\n\r\n /** Throws if [[step]] has already been called, preventing filter/mode changes mid-iteration.\r\n * @internal */\r\n private throwIfAlreadyStepped(): void {\r\n if (this._changeIndex > 0)\r\n throw new IModelError(IModelStatus.BadRequest, \"ChangesetReader: filters and strict mode and batch size must be configured before the first call to step().\");\r\n }\r\n\r\n /** Handle errors that occur while auto closing the reader if there is also an error while opening the reader */\r\n private handleCloseErrorWhileOpening(e: unknown): void {\r\n try {\r\n this.close();\r\n } catch (closeError) {\r\n throw new IModelError(IModelStatus.BadArg, `Failed to open ChangesetReader with error ${e instanceof Error ? e.message : String(e)}.\r\n Additionally, that triggered an automatic closure of the reader\r\n releasing native resources which also failed with failure ${closeError instanceof Error ? closeError.message : String(closeError)}.\r\n Check native error logs for more details.`);\r\n }\r\n }\r\n\r\n /**\r\n * Set the number of rows to fetch and cache while stepping.\r\n * This is an advanced option that can be used to tune performance for large changesets.\r\n * Increasing the batch size improves throughput at the cost of higher peak memory; decreasing it keeps memory consumption lower.\r\n *\r\n * Default batch sizes when `setBatchSize` is not called:\r\n * - `InstanceKey` or `InstanceKeyAndIdentifiers` filter: **100**.\r\n * - `BisCoreElement` filter (any `abbreviateBlobs` setting): **20**.\r\n * - `All` filter, `abbreviateBlobs: false`: **5**.\r\n * - `All` filter (blobs abbreviated or unset): **10**.\r\n *\r\n * @param batchSize Number of rows to fetch and cache while stepping. Must be a positive integer.\r\n * @throws [[IModelError]] if [[step]] has already been called successfully, or if `batchSize` is not a positive integer.\r\n * @beta\r\n */\r\n public setBatchSize(batchSize: number): void {\r\n this.throwIfAlreadyStepped();\r\n if (!Number.isInteger(batchSize) || batchSize <= 0)\r\n throw new IModelError(IModelStatus.BadArg, \"ChangesetReader: batchSize must be a positive integer.\");\r\n this._batchSizeOverride = batchSize;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Filtering\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Restrict iteration to changes from the named SQLite tables.\r\n * That means the rows for changes from other tables will be skipped entirely and won't be visible through the reader.\r\n * @param tableNames SQLite table names to include.\r\n * Note: Table names must be provided in the correct case for proper filtering.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setTableNameFilters(tableNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setTableNameFilters(Array.from(tableNames));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes with the given operation types.\r\n * That means the rows for changes with other operation types will be skipped entirely and won't be visible through the reader.\r\n * @param ops Operations to include.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setOpCodeFilters(ops: Set<SqliteChangeOp>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setOpCodeFilters(Array.from(ops));\r\n }\r\n\r\n /**\r\n * Restrict iteration to changes for the given EC class names.\r\n * That means the rows for changes from other EC classes will be skipped entirely and won't be visible through the reader.\r\n * @param classNames EC class names to include. The classNames should be in the full name format(i.e. \"SchemaName:ClassName\").\r\n * Note: Schema names and class names must be provided in the correct case for proper filtering. Derived classes are not automatically included, so they must be specified explicitly if needed.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error while setting the filter.\r\n * @beta\r\n */\r\n public setClassNameFilters(classNames: Set<string>): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.setClassNameFilters(Array.from(classNames));\r\n }\r\n\r\n /**\r\n * Remove the table-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearTableNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearTableNameFilters();\r\n }\r\n\r\n /**\r\n * Remove the op-code filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearOpCodeFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearOpCodeFilters();\r\n }\r\n\r\n /**\r\n * Remove the class-name filters\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public clearClassNameFilters(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.clearClassNameFilters();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Strict mode\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Enable strict mode on the reader.\r\n *\r\n * Strict mode affects how the reader handles a **column-count mismatch** between a change\r\n * record and the corresponding live database table. Such a mismatch can occur when columns\r\n * have been added to a table after the changeset was created.\r\n *\r\n * When strict mode is **enabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader throws an error\r\n * instead of processing that row.\r\n *\r\n * Use strict mode when you need to be certain that every change row is interpreted against\r\n * exactly the schema that was in effect when the changeset was written.\r\n *\r\n * @see [[disableStrictMode]] — the default (lenient) behaviour.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public enableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.enableStrictMode();\r\n }\r\n\r\n /**\r\n * Disable strict mode on the reader (this is the default).\r\n *\r\n * When strict mode is **disabled**: if the number of columns recorded in a change row differs\r\n * from the number of columns currently present in the live table, the reader takes the\r\n * **minimum** of the two column counts and proceeds normally with that subset. This is safe\r\n * because SQLite only ever appends new columns at the end of a table and never removes them —\r\n * so older change records simply lack the trailing columns that were added later, and those\r\n * missing columns are silently ignored.\r\n *\r\n * @see [[enableStrictMode]] — throw on column-count mismatches instead.\r\n * @throws if [[step]] has already been called and the reader successfully stepped at least once(i.e. returned true for a step() call) or if the native layer encounters an error.\r\n * @beta\r\n */\r\n public disableStrictMode(): void {\r\n this.throwIfAlreadyStepped();\r\n this._nativeReader.disableStrictMode();\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Iteration\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Advance to the next change.\r\n * @returns `true` while positioned on a valid change; `false` when the stream is exhausted.\r\n * @throws if the native layer encounters an error while reading or decoding\r\n * the next change.\r\n * @beta\r\n */\r\n public step(): boolean {\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n if (this._cacheIndex + 1 < this._cache.length) {\r\n // Still have rows in cache — advance the pointer\r\n this._cacheIndex++;\r\n } else {\r\n // Cache empty or fully consumed — fetch next batch from native\r\n const nativeRowOpts = this._rowOptions ? this.toNativeRowOptions(this._rowOptions) : {};\r\n this._cache = this._nativeReader.step(this._batchSize, nativeRowOpts);\r\n this._cacheIndex = 0;\r\n if (this._cache.length === 0) return false;\r\n }\r\n this._changeIndex++;\r\n return true;\r\n }\r\n\r\n /**\r\n * SQLite opcode of the current change.\r\n * Valid only after a successful call to [[step]].\r\n * @throws [[IModelError]] if called before a successful [[step]] call.\r\n * @beta\r\n */\r\n public get op(): SqliteChangeOp {\r\n const opCode = this._currentRow.metadata.opCode;\r\n return opCode === DbOpcode.Insert ? \"Inserted\"\r\n : opCode === DbOpcode.Update ? \"Updated\"\r\n : \"Deleted\";\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Lifecycle\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Close the reader and release all native resources.\r\n *\r\n * @throws if the native layer encounters an error during cleanup. Native resources\r\n * are not fully released when this throws — check the native error\r\n * logs for details.\r\n * @beta\r\n */\r\n public close(): void {\r\n this._changeIndex = 0;\r\n this._cache = [];\r\n this._cacheIndex = 0;\r\n this._cachedInserted = undefined;\r\n this._cachedDeleted = undefined;\r\n this._nativeReader.close();\r\n }\r\n\r\n /**\r\n * Implements the `Disposable` contract — delegates to [[close]].\r\n *\r\n * @throws if the native layer fails to release its resources (re-thrown from [[close]]).\r\n * @beta\r\n */\r\n public [Symbol.dispose](): void {\r\n this.close();\r\n }\r\n}\r\n\r\n"]}