Skip to content

Conversation

pmconne
Copy link
Member

@pmconne pmconne commented Oct 7, 2025

This began as part of #8534.
Cloning a text style requires importing a font, which is an async operation. Cloning a text annotation element requires cloning a text style. I expect some other element types may need to perform asynchronous operations during cloning as well.

Element.onCloned was previously synchronous, returning void. Now it returns void | Promise<void>. This permits existing subclasses that declare their return types as void to continue compiling, but they will receive a lint error (assuming they've configured no-floating-promises) if they call super.onCloned without await.

onCloned is protected, so only subclasses of Element can invoke it legally. IModelElementCloneContext.cloneElement calls it illegally via jsClass["onCloned"]. So does IModelTransformer from the imodel-transformer package, which overrides (and does not call - but almost entirely copy-pastes the contents of - super.cloneElement).

IModelElementCloneContext.cloneElement is @internal. It used to return ElementProps. Now it returns Promise<ElementProps>.

The only implementations of onCloned that actually return a Promise are all currently leaves in the BIS class hierarchy: TextAnnotation2/3d and AnnotationTextStyle. So existing code that currently calls super.onCloned without await will continue to function the same. However, existing code that calls IModelElementCloneContext.cloneElement without awaiting will no longer compile. Since that's an @internal API, theoretically nobody should be calling it therefore nobody should be affected.

We need to talk to the transformer people about how their package and its consumers (including "evergreen" products like drawing production) are supposed to work with the transformer's dependencies on @internal APIs.

@pmconne pmconne requested a review from a team as a code owner October 7, 2025 18:51
@pmconne
Copy link
Member Author

pmconne commented Oct 7, 2025

@khanaffan @ColinKerr @aruniverse problem: Element.onCloned is not async. I have to acquire codes, which is async. We've had other cases where people needed callbacks like this to do async stuff. Do we have a plan for addressing that?

@khanaffan
Copy link
Contributor

@khanaffan @ColinKerr @aruniverse problem: Element.onCloned is not async. I have to acquire codes, which is async. We've had other cases where people needed callbacks like this to do async stuff. Do we have a plan for addressing that?

We’re considering two strategies to enable asynchronous handling:

  1. Move the relevant code into TypeScript from Native, which would allow us to make the handlers asynchronous directly.
  2. A less invasive approach is to split the native function into begin and end phases. This would return control to the TypeScript side between the two phases, enabling it to invoke asynchronous handlers. I used this technique during the rebase work, where I broke down a large native function into steps and triggered async events between them.

I believe the second approach is feasible for these handlers, as it requires minimal code changes. However, this work will need to be scheduled.

@pmconne pmconne mentioned this pull request Oct 7, 2025
28 tasks
@pmconne
Copy link
Member Author

pmconne commented Oct 13, 2025

We’re considering two strategies to enable asynchronous handling:

1. **Move the relevant code into TypeScript from Native**, which would allow us to make the handlers asynchronous directly.

2. **A less invasive approach** is to split the native function into `begin` and `end` phases. This would return control to the TypeScript side between the two phases, enabling it to invoke asynchronous handlers. I used this technique during the rebase work, where I broke down a large native function into steps and triggered async events between them.

I believe the second approach is feasible for these handlers, as it requires minimal code changes. However, this work will need to be scheduled.

Element.onCloned is not invoked by native. It's invoked by IModelElementCloneContext.cloneElement, which is synchronous. We could add cloneElementAsync and deprecate cloneElement. We could also change Element.onCloned to be async. All these APIs are @beta. However, making such breaking changes would torpedo apps who are definitely using these APIs and are relying on us to be "evergreen".

I'll try to come up with a way to add async cloning while preserving backward compatibility. See updated PR description.

@pmconne pmconne changed the title Import fonts when cloning text styles Support async element cloning Oct 14, 2025
@aruniverse aruniverse requested a review from DanRod1999 October 14, 2025 13:38
@pmconne
Copy link
Member Author

pmconne commented Oct 14, 2025

@DanRod1999 anyone else in imodel-transformer orbit who should review this PR?

@pmconne pmconne enabled auto-merge (squash) October 14, 2025 17:27
@DanRod1999
Copy link
Contributor

FYI @mindaugasdirg

@pmconne
Copy link
Member Author

pmconne commented Oct 14, 2025

@khanaffan more concurrent query test flakery.

--[ FAILURE: @itwin/core-backend ]-----------------[ 4 minutes 32.7 seconds ]--


  1692 passing (4m)
  26 pending
  1 failing

  1) ConcurrentQuery
       restart query:

      AssertionError: expected 3 to equal 2
      + expected - actual

      -3
      +2
      
      at Context.<anonymous> (src/test/ecdb/ConcurrentQuery.test.ts:135:28)

Copy link
Contributor

I have seen this failing today. I looking at it.

@pmconne
Copy link
Member Author

pmconne commented Oct 15, 2025

@dassaf4 @saeeedtorabi Voronoi test flaked out.

stdout | src/test/topology/Voronoi.test.ts > Voronoi > CurveChain
ERROR
[
  'Expect defined',
  undefined,
  [ 'interior point containing face found' ]
]

 ❯ src/test/topology/Voronoi.test.ts (14 tests | 1 failed) 1055ms
--[ FAILURE: @itwin/core-geometry ]-------------------------[ 59.41 seconds ]--


⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/test/topology/Voronoi.test.ts > Voronoi > CurveChain
AssertionError: expected 1 to be +0 // Object.is equality

- Expected
+ Received

- 0
  ...2 lines omitted...
 ❯ src/test/topology/Voronoi.test.ts:795:31
    793|     }
    794|     GeometryCoreTestIO.saveGeometry(allGeometry, "Voronoi", "CurveChai…
    795|     expect(ck.getNumErrors()).toBe(0);
       |                               ^
    796|   });
    797| 

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯

@pmconne pmconne changed the title Support async element cloning Make element cloning async Oct 15, 2025
@saeeedtorabi
Copy link
Contributor

@dassaf4 @saeeedtorabi Voronoi test flaked out.

@pmconne This test generates 100 random points and had never failed until now. Since it's random, we cannot regenerate it. However, we added logs here #8639, so next time it failed we can regenerate it. Please note that I ran it 10 more times locally (i.e., for 10000 random points) and it never failed.

@pmconne pmconne disabled auto-merge October 15, 2025 15:52
@pmconne pmconne enabled auto-merge (squash) October 15, 2025 16:41
@pmconne pmconne merged commit 160a28f into master Oct 15, 2025
15 checks passed
@pmconne pmconne deleted the pmc/clone-text-style branch October 15, 2025 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants