Skip to content

feat: support multi-line string literals in the Pure protocol - #5404

Draft
rafaelbey wants to merge 1 commit into
finos:masterfrom
rafaelbey:port-multiline-string-protocol
Draft

feat: support multi-line string literals in the Pure protocol#5404
rafaelbey wants to merge 1 commit into
finos:masterfrom
rafaelbey:port-multiline-string-protocol

Conversation

@rafaelbey

@rafaelbey rafaelbey commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Important

Blocked on finos/legend-engine#5008. Kept as a draft until that lands.

The TaggedValue half of this change is written against that PR's hand-rolled Jackson serializer, whose wire shape can still move in review. Before marking this ready, re-read TaggedValue.java on the merged commit and diff it against V1_taggedValueModelSchema.

The CString half depends only on finos/legend-engine#4998, which is already merged, and could be split out and landed independently if that is preferred.

Engine is adding multi-line ('''...''') string literals to the Pure grammar, and records them in the protocol in two places:

  • finos/legend-engine#4998 (merged) — CString gains an optional boolean multiLine, annotated @JsonInclude(NON_DEFAULT) so it is omitted from the wire when false.
  • finos/legend-engine#5008 (open) — TaggedValue.value becomes a CString. A hand-rolled Jackson serializer keeps the wire shape a plain string unless the value was authored multi-line, in which case it is {"_type":"string","multiLine":true,"value":"..."}. Both shapes deserialize.

Engine is backward compatible in both directions. Studio is not forward compatible — nothing here knew about the flag, and serializr drops keys that are not in the schema. So a {_type, multiLine, value} tagged value would deserialize into V1_TaggedValue.value as an object (which then flows into hashArray, .split(','), .trim(), <textarea value=…>), and even in the benign single-line case Studio would silently strip multiLine on the entity roundtrip — rewriting a user's '''...''' documentation as an escaped single-line literal on the next save.

Approach

Tagged values keep a flat value: string with a sibling multiLine flag on both the metamodel and the protocol class, rather than mirroring engine's value: CString typing. Engine changed the field type because Java has nowhere else to hang the flag; here that would ripple through ~28 taggedValue.value read sites across legend-graph, legend-query-builder, legend-application-studio, legend-application-query and three DSL extensions. The union instead lives entirely in V1_taggedValueModelSchema, which is where engine put it too, and which all ~25 consumers already share.

Hashing:

  • multiLine is hashed on TaggedValue and V1_TaggedValue (kept in lockstep) — this hash drives change detection, so without it toggling a value to multi-line would not register as a change and could not be saved.
  • multiLine is not hashed on V1_CString, to keep that hash compatible with PrimitiveInstanceValue.hashCode — the metamodel has no CString counterpart to carry the flag, and diverging would produce phantom changes.

Also covered: the parallel raw value specification hierarchy (V1_RawPrimitiveInstanceValue), which carries its primitive type as a _type field rather than by subclass, so it never surfaces in a grep V1_CString. It is reachable from relational milestoning infinityDate and from buildRawValueSpecification.

Testing

  • V1_TaggedValueBackwardCompatibleSerialization.test.ts — a direct port of engine's TestTaggedValueCompatibility.java: legacy plain string, plain string containing \n (must stay single-line), object form with and without the flag, single-line serializing back to a bare string, multi-line serializing to an object, and both shapes round-tripping.
  • V1_ValueSpecificationBackwardCompatibleSerialization.test.tsmultiLine round-trips, is omitted when false, and survives the legacy values: [...] shim.
  • V1_RawValueSpecificationSerialization.test.ts — same for the raw hierarchy.
  • TEST_DATA__ClassWithMultiLineTaggedValue in the entity roundtrip — asserts entity-in === entity-out, and via TEST__checkGraphHashUnchanged also pins protocol hashes to metamodel hashes.

core test group is green. The data-cube and engine-roundtrip groups were not evaluated locally (they need a running engine).

Out of scope

Authoring support: there is no multi-line toggle in TaggedValueEditor, and the Monaco Pure tokenizer does not yet highlight '''...''' blocks. This PR only guarantees Studio carries the flag through faithfully.

@rafaelbey
rafaelbey requested a review from a team as a code owner August 3, 2026 19:23
@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3b1b8fc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@finos/legend-graph Patch
@finos/legend-application-data-cube Patch
@finos/legend-application-marketplace Patch
@finos/legend-application-pure-ide Patch
@finos/legend-application-query-bootstrap Patch
@finos/legend-application-query Patch
@finos/legend-application-repl Patch
@finos/legend-application-studio-bootstrap Patch
@finos/legend-application-studio Patch
@finos/legend-code-editor Patch
@finos/legend-data-cube Patch
@finos/legend-extension-application-studio-depot-dashboard Patch
@finos/legend-extension-assortment Patch
@finos/legend-extension-dsl-data-product Patch
@finos/legend-extension-dsl-data-quality Patch
@finos/legend-extension-dsl-data-space-studio Patch
@finos/legend-extension-dsl-data-space Patch
@finos/legend-extension-dsl-diagram-studio Patch
@finos/legend-extension-dsl-diagram Patch
@finos/legend-extension-dsl-persistence Patch
@finos/legend-extension-dsl-service Patch
@finos/legend-extension-dsl-text Patch
@finos/legend-extension-store-flat-data Patch
@finos/legend-extension-store-relational Patch
@finos/legend-extension-store-service-store Patch
@finos/legend-lego Patch
@finos/legend-query-builder Patch
@finos/legend-server-lakehouse Patch
@finos/legend-server-marketplace Patch
@finos/legend-vscode-extension-dependencies Patch
@finos/legend-application-data-cube-bootstrap Patch
@finos/legend-application-marketplace-bootstrap Patch
@finos/legend-application-pure-ide-deployment Patch
@finos/legend-application-query-deployment Patch
@finos/legend-application-repl-deployment Patch
@finos/legend-application-studio-deployment Patch
@finos/legend-application-data-cube-deployment Patch
@finos/legend-application-marketplace-deployment Patch
@finos/legend-server-showcase-deployment Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@rafaelbey
rafaelbey marked this pull request as draft August 3, 2026 19:23
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.80220% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.40%. Comparing base (3bb0d2c) to head (3b1b8fc).

Files with missing lines Patch % Lines
...l/pure/v1/helpers/V1_ValueSpecificationObserver.ts 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5404      +/-   ##
==========================================
+ Coverage   50.36%   50.40%   +0.03%     
==========================================
  Files        2779     2779              
  Lines      556495   556581      +86     
  Branches    19971    25608    +5637     
==========================================
+ Hits       280276   280522     +246     
+ Misses     275672   274816     -856     
- Partials      547     1243     +696     
Files with missing lines Coverage Δ
...ger/action/changeDetection/DomainObserverHelper.ts 37.88% <100.00%> (+2.41%) ⬆️
...n/changeDetection/RawValueSpecificationObserver.ts 56.19% <100.00%> (+9.07%) ⬆️
...model/packageableElements/domain/V1_TaggedValue.ts 100.00% <100.00%> (ø)
...ValueSpecification/V1_RawPrimitiveInstanceValue.ts 86.53% <100.00%> (+29.09%) ⬆️
...pure/v1/model/valueSpecification/raw/V1_CString.ts 87.27% <100.00%> (+2.49%) ⬆️
...nsformation/pureGraph/from/V1_DomainTransformer.ts 81.29% <100.00%> (+0.04%) ⬆️
...eGraph/from/V1_RawValueSpecificationTransformer.ts 97.77% <100.00%> (+7.89%) ⬆️
...on/pureGraph/to/V1_RawValueSpecificationBuilder.ts 74.62% <100.00%> (+31.54%) ⬆️
...ion/pureGraph/to/helpers/V1_DomainBuilderHelper.ts 82.09% <100.00%> (+0.08%) ⬆️
...serializationHelpers/V1_CoreSerializationHelper.ts 100.00% <100.00%> (ø)
... and 5 more

... and 488 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Engine is adding multi-line ('''...''') string literals to the Pure
grammar and records them in the protocol in two places:

- CString gains an optional `multiLine` flag, omitted from the wire
  when false (finos/legend-engine#4998)
- TaggedValue's `value` becomes a CString, so it is sent as a plain
  string unless the value was authored multi-line, in which case it
  is an object carrying the flag (finos/legend-engine#5008)

Both changes are backward compatible on engine's side. Studio, however,
drops unknown keys on deserialization, so without this it would silently
rewrite a multi-line documentation as an escaped single-line literal on
the next save.

Tagged values keep a flat `value: string` with a sibling `multiLine`
flag rather than mirroring engine's CString typing - the union is
handled entirely in `V1_taggedValueModelSchema`, which leaves every
`taggedValue.value` consumer untouched. The flag is hashed on both the
metamodel and the protocol side so that toggling it registers as a
change; note it has to be stringified first, as `hash.js` digests any
boolean to the hash of the empty string. It is left out of
`V1_CString`'s hash to keep that compatible with
`PrimitiveInstanceValue`, whose metamodel has no counterpart.
@rafaelbey
rafaelbey force-pushed the port-multiline-string-protocol branch from dd38bd2 to 3b1b8fc Compare August 3, 2026 19:59
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.

1 participant