@vvfx/dsl-ir 0.0.1-beta.10

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 (39) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +240 -0
  3. package/dist/common/particle-expression.d.ts +6 -0
  4. package/dist/common/transform.d.ts +11 -0
  5. package/dist/common/utils.d.ts +19 -0
  6. package/dist/dsl/number-expression.d.ts +7 -0
  7. package/dist/dsl/parse-particle-shape.d.ts +2 -0
  8. package/dist/dsl/particle-shape.d.ts +86 -0
  9. package/dist/dsl/schema.d.ts +100 -0
  10. package/dist/dsl/validator/index.d.ts +2 -0
  11. package/dist/dsl/validator/labels.d.ts +1 -0
  12. package/dist/dsl/validator/particle.d.ts +8 -0
  13. package/dist/dsl/validator/types.d.ts +33 -0
  14. package/dist/dsl/validator/validation.d.ts +15 -0
  15. package/dist/dsl2ir/assemble.d.ts +3 -0
  16. package/dist/dsl2ir/content-conversion.d.ts +11 -0
  17. package/dist/dsl2ir/convert-layers.d.ts +4 -0
  18. package/dist/dsl2ir/convert-particle.d.ts +4 -0
  19. package/dist/dsl2ir/convert-property.d.ts +6 -0
  20. package/dist/dsl2ir/convert.d.ts +4 -0
  21. package/dist/dsl2ir/recombine/compare-properties.d.ts +18 -0
  22. package/dist/dsl2ir/recombine/convert.d.ts +5 -0
  23. package/dist/dsl2ir/recombine/update-ir.d.ts +6 -0
  24. package/dist/dsl2ir/recombine/update-layer-order.d.ts +5 -0
  25. package/dist/dsl2ir/recombine/update-layer.d.ts +4 -0
  26. package/dist/dsl2ir/recombine/update-particle.d.ts +4 -0
  27. package/dist/dsl2ir/types.d.ts +35 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.js +4424 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/index.mjs +4418 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/ir2dsl/assemble.d.ts +5 -0
  34. package/dist/ir2dsl/convert-layers.d.ts +10 -0
  35. package/dist/ir2dsl/convert-particle.d.ts +4 -0
  36. package/dist/ir2dsl/convert.d.ts +4 -0
  37. package/dist/ir2dsl/expand-precompositions.d.ts +6 -0
  38. package/dist/ir2dsl/types.d.ts +45 -0
  39. package/package.json +45 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) 2019-present Ant Group Co., Ltd. https://www.antgroup.com/
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # @vvfx/dsl-ir
2
+
3
+ Bidirectional conversion and validation for editable DSL and source-neutral Animation IR.
4
+
5
+ The package owns the DSL boundary only. Importing and exporting Galacean Effects JSONScene, including preservation of
6
+ GE-only data, is handled by `@vvfx/ge-ir`.
7
+
8
+ ## Contents
9
+
10
+ - [Install](#install)
11
+ - [Usage](#usage)
12
+ - [Data and editing boundaries](#data-and-editing-boundaries)
13
+ - [API](#api)
14
+ - [Project structure](#project-structure)
15
+ - [Contributing](#contributing)
16
+ - [License](#license)
17
+
18
+ ## Install
19
+
20
+ Node.js 22 or later is required.
21
+
22
+ ```bash
23
+ pnpm add @vvfx/dsl-ir
24
+ ```
25
+
26
+ Install `@vvfx/ge-ir` as well when working with Galacean Effects:
27
+
28
+ ```bash
29
+ pnpm add @vvfx/dsl-ir @vvfx/ge-ir
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Convert DSL to Animation IR
35
+
36
+ A DSL document created from scratch does not need preserved data:
37
+
38
+ ```ts
39
+ import { convertDSLToIR, type Composition } from '@vvfx/dsl-ir';
40
+
41
+ const dsl: Composition = {
42
+ meta: {
43
+ name: 'example',
44
+ duration: 2,
45
+ fps: 30,
46
+ width: 512,
47
+ height: 512,
48
+ loop: true,
49
+ background_color: null,
50
+ },
51
+ layers: [],
52
+ };
53
+
54
+ const { scene, diagnostics } = convertDSLToIR(dsl);
55
+ ```
56
+
57
+ DSL timing is expressed in seconds. Animation IR timing is expressed in frames, using `dsl.meta.fps` as the frame
58
+ rate. Adjacent animation segments may have different end and start values to represent an instantaneous reset.
59
+
60
+ DSL uses the initial `position.z` as a sibling stacking value: higher values render above lower values.
61
+ Array order does not control stacking. Equal values retain the imported order with preserved data; standalone DSL
62
+ uses layer IDs as a deterministic tie-breaker. Imported DSL exposes painter ranks in Z, while the paired sidecar
63
+ retains source spatial depth. Changing only Z reorders whole sibling subtrees without moving them toward the camera.
64
+ Standalone DSL positions are placed on the IR 2D plane (`z = 0`). Animated position uses its first segment's start Z
65
+ as a fixed rank. Editing XY on a layer with varying source depth retains depth at matching keyframe times;
66
+ changing that position's timing or replacing it with a static position is unsupported.
67
+
68
+ ### Convert Animation IR to DSL
69
+
70
+ `convertIRToDSL` always returns the editable DSL, diagnostics, and an `IRToDSLPreservedData` sidecar:
71
+
72
+ ```ts
73
+ import { convertIRToDSL } from '@vvfx/dsl-ir';
74
+
75
+ const { dsl, preservedData, diagnostics } = convertIRToDSL(scene);
76
+ ```
77
+
78
+ The sidecar retains IR data and write-back relationships that DSL cannot represent completely. It is adapter-owned
79
+ data rather than business data. It can be persisted with `JSON.stringify` and `JSON.parse`, but applications should not
80
+ modify or depend on its internal structure.
81
+
82
+ ### Write edited DSL back to its source IR
83
+
84
+ Pass the DSL and sidecar produced by the same conversion back to `convertDSLToIR`:
85
+
86
+ ```ts
87
+ const converted = convertIRToDSL(scene);
88
+ const editedDSL = structuredClone(converted.dsl);
89
+
90
+ editedDSL.layers[0].opacity = { a: false, value: 0.5 };
91
+
92
+ const { scene: updatedScene, diagnostics } = convertDSLToIR(editedDSL, {
93
+ preservedData: converted.preservedData,
94
+ onChange: change => console.info(change),
95
+ onWarning: warning => console.warn(warning),
96
+ });
97
+ ```
98
+
99
+ When `preservedData` is supplied, the converter applies DSL-representable changes to the retained IR snapshot while
100
+ keeping information outside the DSL contract. `strict: true` promotes conversion warnings and unsupported edits to
101
+ errors.
102
+
103
+ ### Combine with `@vvfx/ge-ir`
104
+
105
+ The application coordinates the adapters and keeps their sidecars paired.
106
+
107
+ ```ts
108
+ import { convertGEToIR, convertIRToGE, updateGEToIRPreservedData } from '@vvfx/ge-ir';
109
+ import { convertIRToDSL, convertDSLToIR } from '@vvfx/dsl-ir';
110
+
111
+ const ge = convertGEToIR(geScene, { frameRate: 30 });
112
+ const dsl = convertIRToDSL(ge.scene);
113
+ const edited = structuredClone(dsl.dsl);
114
+
115
+ edited.meta.duration = 4;
116
+
117
+ const updated = convertDSLToIR(edited, { preservedData: dsl.preservedData });
118
+ const preservedData = updateGEToIRPreservedData(ge.scene, updated.scene, ge.preservedData);
119
+ const restored = convertIRToGE(updated.scene, { preservedData });
120
+ ```
121
+
122
+ ### Particle layers
123
+
124
+ `Composition` and `Layer` define the DSL for all layer types. Particle layers use `type: 'particle'` and the same
125
+ transform semantics as other layers. All layers may omit `time_range`, `rotation`, `scale`, `anchor`, and
126
+ `opacity`. Preserved write-back leaves omitted channels unchanged; fresh conversion uses composition timing and
127
+ identity transforms. Input DSL objects are not completed or mutated. `convertIRToDSL` produces complete layers;
128
+ `validateDSL` and `convertDSLToIR` validate and convert them through the common pipeline. `parseParticleShape` parses optional,
129
+ read-only emitter geometry supplied by a source adapter.
130
+
131
+ Applications compose source effects, target layout, model requests and GE resource restoration through public adapter APIs.
132
+
133
+ ## Data and editing boundaries
134
+
135
+ - `@vvfx/animation-ir` is the single source of truth for Animation IR types and validation.
136
+ - DSL is an editing-oriented subset, not a mirror of any source format schema.
137
+ - `convertIRToDSL` expands precompositions and stores the write-back relationships in `IRToDSLPreservedData`.
138
+ - Fidelity-preserving write-back supports field edits, deletion, and reordering of existing layers under the same
139
+ parent. Unsafe edits are reported through diagnostics or warnings.
140
+ - Both conversion directions validate their IR boundary. Every successfully returned `scene` satisfies the Animation
141
+ IR contract.
142
+ - External images entering Animation IR must use absolute URLs or Data URLs. DSL font families become system font
143
+ assets.
144
+ Inline SVG assets use their SVG content when `url` is omitted or empty.
145
+ - Particle Layers use the same `convertIRToDSL` and `convertDSLToIR` workflow as other supported Layers. Editable
146
+ `options` and `emission` values are represented in DSL. Source adapters may also expose an optional read-only
147
+ `shape` block for model context; renderer, resources, and other GE-only fields remain in the GE adapter sidecar.
148
+ - Burst `cycles` defaults to one; zero means unbounded repetition until the Layer ends. Repeated bursts require a
149
+ positive `interval` in seconds.
150
+ - Particle lifetime and emission values use the DSL subset of GE `NumberExpression`: constant tag `0` and
151
+ random-range tag `4`. IR curves are projected with diagnostics and retained in the DSL sidecar until the
152
+ corresponding field is edited. A projected `shape` block is not written into Animation IR; the source adapter
153
+ remains responsible for restoring the original emitter geometry.
154
+
155
+ Further documentation:
156
+
157
+ - [DSL format](../../docs/dsl_definition.md)
158
+ - [DSL and IR guide](../../docs/dsl-ir-guide.md)
159
+ - [DSL and IR editing capabilities](../../docs/dsl-ir-editing-capabilities.md)
160
+ - [DSL, IR, and GE package boundaries](../../docs/dsl-ir-ge-ir-boundary.md)
161
+ - [Animation IR particle contract](../../docs/particle-ir-contract.md)
162
+
163
+ ## API
164
+
165
+ ### Runtime exports
166
+
167
+ | Export | Purpose |
168
+ | --- | --- |
169
+ | `convertDSLToIR` | Convert a new DSL document to IR, or write DSL values back to retained IR |
170
+ | `convertIRToDSL` | Convert IR to editable DSL and return the sidecar needed for write-back |
171
+ | `validateDSL` | Validate an unknown value against the DSL contract |
172
+ | `DSLValidationError` | Structured error thrown when conversion cannot continue |
173
+
174
+ Core signatures:
175
+
176
+ ```ts
177
+ function convertDSLToIR(composition: Composition, options?: ConvertDSLToIROptions): DSLToIRResult;
178
+
179
+ function convertIRToDSL(scene: IRScene): IRToDSLResult;
180
+
181
+ function validateDSL(dsl: unknown, options?: ValidateDSLOptions): DSLValidationResult;
182
+ ```
183
+
184
+ ### Type exports
185
+
186
+ All public types are imported from the package root:
187
+
188
+ - DSL schema: `AnimationCurve`, `AnimatedProperty`, `Asset`, `Composition`, `CompositionMeta`, `Description`, `Layer`,
189
+ `LLMSVGAsset`, `NumberExpression`, the `Particle*` types, `TextProperties`, `UserAsset`, and
190
+ `ValueType`.
191
+ - Validation: `DSLValidationIssue`, `DSLValidationResult`, and `ValidateDSLOptions`.
192
+ - DSL to IR: `ConvertDSLToIROptions`, `DSLToIRChange`, `DSLToIRResult`, and `DSLToIRWarning`.
193
+ - IR to DSL: `IRToDSLPreservedData` and `IRToDSLResult`.
194
+
195
+ Important result and option types:
196
+
197
+ ```ts
198
+ interface IRToDSLResult {
199
+ dsl: Composition;
200
+ preservedData: IRToDSLPreservedData;
201
+ diagnostics: ConversionDiagnostic[];
202
+ }
203
+
204
+ interface DSLToIRResult {
205
+ scene: IRScene;
206
+ diagnostics: ConversionDiagnostic[];
207
+ }
208
+
209
+ interface ConvertDSLToIROptions {
210
+ preservedData?: IRToDSLPreservedData;
211
+ strict?: boolean;
212
+ onWarning?: (warning: DSLToIRWarning) => void;
213
+ onChange?: (change: DSLToIRChange) => void;
214
+ }
215
+ ```
216
+
217
+ ## Project structure
218
+
219
+ ```text
220
+ src/
221
+ dsl/ DSL schema and validation
222
+ dsl2ir/ DSL to Animation IR
223
+ recombine/ Preserved-data write-back into retained IR
224
+ ir2dsl/ Animation IR to DSL and precomposition expansion
225
+ common/ Value mappings shared by both directions
226
+ ```
227
+
228
+ ## Contributing
229
+
230
+ Use repository issues for questions and bug reports. Pull requests are welcome. Run the package checks after changing
231
+ public behavior:
232
+
233
+ ```bash
234
+ pnpm --filter @vvfx/dsl-ir test
235
+ pnpm --filter @vvfx/dsl-ir build
236
+ ```
237
+
238
+ ## License
239
+
240
+ [MIT](./LICENSE) © 2019-present Ant Group Co., Ltd.
@@ -0,0 +1,6 @@
1
+ import type { IRParticleScalarExpression } from '@vvfx/animation-ir';
2
+ import { type NumberExpression } from '../dsl/number-expression';
3
+ export declare function mapDSLParticleExpressionToIR(expression: NumberExpression): IRParticleScalarExpression;
4
+ export declare function mapIRParticleExpressionToDSL(expression: IRParticleScalarExpression): NumberExpression;
5
+ export declare function isDSLCompatibleParticleExpression(expression: IRParticleScalarExpression): boolean;
6
+ export declare function isDSLNumberExpressionEqual(left: NumberExpression, right: NumberExpression): boolean;
@@ -0,0 +1,11 @@
1
+ import type { Layer } from '../dsl/schema';
2
+ /** Static defaults for absent transform channels. */
3
+ export declare const TRANSFORM_FALLBACK: {
4
+ position: [number, number, number];
5
+ rotation: [number, number, number];
6
+ scale: [number, number, number];
7
+ anchor: [number, number, number];
8
+ opacity: number;
9
+ };
10
+ export declare function getLayerStackingOrder(layer: Layer): number;
11
+ export declare function withPositionZ(position: Layer['position'], depth: number): Layer['position'];
@@ -0,0 +1,19 @@
1
+ import type { IRColor } from '@vvfx/animation-ir';
2
+ export declare const EPSILON = 0.000001;
3
+ export type HexColorInput = readonly [number, number, number] | readonly [number, number, number, number] | IRColor;
4
+ /** Generate a 32-character hexadecimal UUID v4. */
5
+ export declare function createId(): string;
6
+ /** Convert normalized RGB(A) channels to a canonical hex color. */
7
+ export declare function colorToHex(color: HexColorInput): string;
8
+ /** Normalize supported hex forms for semantic comparison. */
9
+ export declare function normalizeHex(hexColor: string): string;
10
+ /** Compare nullable hex colors after normalization. */
11
+ export declare function isHexEqual(leftHex: string | null | undefined, rightHex: string | null | undefined): boolean;
12
+ export declare function isValidHexColor(value: unknown): value is string;
13
+ export declare function hexToIRColor(hexColor: string): IRColor;
14
+ export declare function resolveFiniteNumber(value: unknown, fallback: number): number;
15
+ export declare function secondsToFrames(seconds: number, frameRate: number): number;
16
+ export declare function isScalarEqual(leftValue: number, rightValue: number): boolean;
17
+ export declare function isNumberArrayEqual(leftValues: readonly number[], rightValues: readonly number[]): boolean;
18
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
19
+ export declare function toJSONPath(path: string): string;
@@ -0,0 +1,7 @@
1
+ export declare enum ValueType {
2
+ CONSTANT = 0,
3
+ RANDOM = 4
4
+ }
5
+ export type NumberExpression = ConstantNumber | RandomValue;
6
+ export type ConstantNumber = [type: ValueType.CONSTANT, value: number];
7
+ export type RandomValue = [type: ValueType.RANDOM, value: [min: number, max: number]];
@@ -0,0 +1,2 @@
1
+ import type { ParticleShape } from './particle-shape';
2
+ export declare function parseParticleShape(value: unknown): ParticleShape | undefined;
@@ -0,0 +1,86 @@
1
+ export type ParticleShapeArcMode = 0 | 1 | 2 | 3;
2
+ export type ParticleShapeName = ParticleShape['shape'];
3
+ /** Source expression retained only to describe emitter direction. */
4
+ export type ParticleShapeNumberExpression = [type: number, value: unknown];
5
+ interface ParticleShapeBase {
6
+ arcMode: ParticleShapeArcMode;
7
+ alignSpeedDirection?: boolean;
8
+ upDirection?: [x: number, y: number, z: number];
9
+ turbulenceX?: ParticleShapeNumberExpression;
10
+ turbulenceY?: ParticleShapeNumberExpression;
11
+ turbulenceZ?: ParticleShapeNumberExpression;
12
+ }
13
+ interface ParticleShapeNone {
14
+ type: 0;
15
+ shape: 'None';
16
+ turbulenceX?: ParticleShapeNumberExpression;
17
+ turbulenceY?: ParticleShapeNumberExpression;
18
+ turbulenceZ?: ParticleShapeNumberExpression;
19
+ upDirection?: number[];
20
+ }
21
+ interface ParticleShapeSphere extends ParticleShapeBase {
22
+ type: 1;
23
+ shape: 'Sphere';
24
+ radius: number;
25
+ arc: number;
26
+ }
27
+ interface ParticleShapeCone extends ParticleShapeBase {
28
+ type: 2;
29
+ shape: 'Cone';
30
+ angle: number;
31
+ radius: number;
32
+ arc: number;
33
+ }
34
+ interface ParticleShapeHemisphere extends ParticleShapeBase {
35
+ type: 3;
36
+ shape: 'Hemisphere';
37
+ radius: number;
38
+ arc: number;
39
+ }
40
+ interface ParticleShapeCircle extends ParticleShapeBase {
41
+ type: 4;
42
+ shape: 'Circle';
43
+ radius: number;
44
+ arc: number;
45
+ }
46
+ interface ParticleShapeDonut extends ParticleShapeBase {
47
+ type: 5;
48
+ shape: 'Donut';
49
+ radius: number;
50
+ donutRadius: number;
51
+ arc: number;
52
+ }
53
+ interface ParticleShapeRectangle extends ParticleShapeBase {
54
+ type: 6;
55
+ shape: 'Rectangle';
56
+ width: number;
57
+ height: number;
58
+ }
59
+ interface ParticleShapeRectangleEdge extends ParticleShapeBase {
60
+ type: 7;
61
+ shape: 'RectangleEdge';
62
+ width: number;
63
+ height: number;
64
+ }
65
+ interface ParticleShapeEdge extends ParticleShapeBase {
66
+ type: 8;
67
+ shape: 'Edge';
68
+ width: number;
69
+ }
70
+ export interface ParticleTextureShapeDetail {
71
+ width: number;
72
+ height: number;
73
+ block: [width: number, height: number];
74
+ anchors: number[];
75
+ }
76
+ interface ParticleShapeTexture extends ParticleShapeBase {
77
+ type: 9;
78
+ shape: 'Texture';
79
+ detail: ParticleTextureShapeDetail;
80
+ random: number;
81
+ width: number;
82
+ height: number;
83
+ }
84
+ /** Particle emitter geometry projected from a source adapter for model context. */
85
+ export type ParticleShape = ParticleShapeNone | ParticleShapeSphere | ParticleShapeCone | ParticleShapeHemisphere | ParticleShapeCircle | ParticleShapeDonut | ParticleShapeRectangle | ParticleShapeRectangleEdge | ParticleShapeEdge | ParticleShapeTexture;
86
+ export {};
@@ -0,0 +1,100 @@
1
+ import type { NumberExpression } from './number-expression';
2
+ import type { ParticleShape } from './particle-shape';
3
+ export interface AnimationCurve {
4
+ t: [number, number];
5
+ start: number[];
6
+ end: number[];
7
+ easing: [number, number, number, number];
8
+ in?: [number, number, number];
9
+ out?: [number, number, number];
10
+ }
11
+ export type AnimatedProperty = {
12
+ a: false;
13
+ value: number | number[];
14
+ } | {
15
+ a: true;
16
+ animation: AnimationCurve[];
17
+ };
18
+ export interface Description {
19
+ self: string;
20
+ composite: string;
21
+ }
22
+ export interface UserAsset {
23
+ source: 'user';
24
+ url: string;
25
+ prompt: '';
26
+ }
27
+ export interface LLMSVGAsset {
28
+ source: 'llm_svg';
29
+ format: 'svg';
30
+ svg: string;
31
+ prompt: string;
32
+ url?: string;
33
+ }
34
+ export type Asset = UserAsset | LLMSVGAsset;
35
+ export interface TextProperties {
36
+ content: string;
37
+ font?: string;
38
+ size: number;
39
+ color: string;
40
+ align?: 'left' | 'center' | 'right';
41
+ line_height?: number;
42
+ letter_spacing?: number;
43
+ }
44
+ export interface ParticleOptions {
45
+ maxCount: number;
46
+ startLifetime: NumberExpression;
47
+ }
48
+ export interface ParticleBurst {
49
+ time: number;
50
+ count: number;
51
+ /** Total cycles. Omitted means one cycle; zero means unbounded repetition. */
52
+ cycles?: number;
53
+ /** Seconds between repeated cycles. Required to be positive when repetition is enabled. */
54
+ interval?: number;
55
+ }
56
+ export interface ParticleEmissionProperties {
57
+ rateOverTime: NumberExpression;
58
+ bursts?: ParticleBurst[];
59
+ }
60
+ export interface ParticleProperties {
61
+ /** Read-only emitter geometry supplied by source adapters when available. */
62
+ shape?: ParticleShape;
63
+ options: ParticleOptions;
64
+ emission: ParticleEmissionProperties;
65
+ }
66
+ export interface Layer {
67
+ id: string;
68
+ name: string;
69
+ type: 'null' | 'layer' | 'text' | 'particle';
70
+ width: number;
71
+ height: number;
72
+ description: Description;
73
+ parent?: string | null;
74
+ /** Omission retains source timing during preserved write-back, otherwise uses the composition duration. */
75
+ time_range?: [number, number];
76
+ position: AnimatedProperty;
77
+ /** Omitted channels retain source values during preserved write-back, otherwise use identity defaults. */
78
+ rotation?: AnimatedProperty;
79
+ scale?: AnimatedProperty;
80
+ anchor?: AnimatedProperty;
81
+ opacity?: AnimatedProperty;
82
+ asset?: Asset;
83
+ text?: TextProperties;
84
+ particle?: ParticleProperties;
85
+ children?: Layer[];
86
+ }
87
+ export interface CompositionMeta {
88
+ name: string;
89
+ duration: number;
90
+ fps: number;
91
+ width: number;
92
+ height: number;
93
+ loop: boolean;
94
+ background_color?: string | null;
95
+ }
96
+ /** Public DSL document. Its runtime shape matches the persisted DSL JSON. */
97
+ export interface Composition {
98
+ meta: CompositionMeta;
99
+ layers: Layer[];
100
+ }
@@ -0,0 +1,2 @@
1
+ export { DSLValidationError, validateDSL } from './validation';
2
+ export type { DSLValidationIssue, DSLValidationResult, ValidateDSLOptions } from './types';
@@ -0,0 +1 @@
1
+ export declare const DSL_CODE_LABELS: Readonly<Record<string, string>>;
@@ -0,0 +1,8 @@
1
+ type DSLRecord = Record<string, unknown>;
2
+ export interface ParticleValidationIssue {
3
+ code: string;
4
+ message: string;
5
+ path: string;
6
+ }
7
+ export declare function lintParticle(particle: DSLRecord, layer: DSLRecord, particlePath: string): ParticleValidationIssue[];
8
+ export {};
@@ -0,0 +1,33 @@
1
+ export type DSLSeverity = 'ERROR' | 'WARNING';
2
+ export interface DSLLintIssue {
3
+ severity: DSLSeverity;
4
+ code: string;
5
+ message: string;
6
+ path: string;
7
+ layerId: string | null;
8
+ }
9
+ export interface DSLLintReport {
10
+ ok: boolean;
11
+ hasErrors: boolean;
12
+ counts: {
13
+ ERROR: number;
14
+ WARNING: number;
15
+ };
16
+ issues: DSLLintIssue[];
17
+ }
18
+ export interface DSLValidationIssue {
19
+ code: string;
20
+ label: string;
21
+ message: string;
22
+ path: string;
23
+ layerId: string | null;
24
+ }
25
+ export interface DSLValidationResult {
26
+ result: 'pass' | 'fail';
27
+ summary: string;
28
+ errors: DSLValidationIssue[];
29
+ warnings: DSLValidationIssue[];
30
+ }
31
+ export interface ValidateDSLOptions {
32
+ strict?: boolean;
33
+ }
@@ -0,0 +1,15 @@
1
+ import type { DSLLintReport, DSLValidationIssue, DSLValidationResult, ValidateDSLOptions } from './types';
2
+ /** Lint an unnormalized DSL value while preserving issue severity. */
3
+ export declare function lintDSL(dsl: unknown): DSLLintReport;
4
+ /**
5
+ * Validate DSL and return the public validation result.
6
+ * Strings are interpreted as raw JSON. File-system paths are intentionally not
7
+ * accepted so the same validator works unchanged in Node.js and browsers.
8
+ */
9
+ export declare function validateDSL(dsl: unknown, options?: ValidateDSLOptions): DSLValidationResult;
10
+ /** Error thrown when a value cannot be used as a valid DSL composition. */
11
+ export declare class DSLValidationError extends TypeError {
12
+ readonly issues: DSLValidationIssue[];
13
+ constructor(issues: DSLValidationIssue[]);
14
+ static fromValidationResult(validationResult: DSLValidationResult): DSLValidationError;
15
+ }
@@ -0,0 +1,3 @@
1
+ import { type IRComposition } from '@vvfx/animation-ir';
2
+ import type { Composition } from '../dsl/schema';
3
+ export declare function assembleIRComposition(composition: Composition, layers: IRComposition['layers']): IRComposition;
@@ -0,0 +1,11 @@
1
+ import type { IRAsset, IRFontAsset, IRTextContent } from '@vvfx/animation-ir';
2
+ import type { Asset } from '../dsl/schema';
3
+ interface EstimatedTextBounds {
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export declare function normalizeDSLFontFamily(family: string | undefined): string;
8
+ export declare function ensureSystemFontAsset(assets: IRAsset[], family: string, style: IRFontAsset['style'], weight: number): string;
9
+ export declare function estimateTextBounds(text: IRTextContent): EstimatedTextBounds;
10
+ export declare function resolveAssetUrl(asset: Asset): string;
11
+ export {};
@@ -0,0 +1,4 @@
1
+ import { type IRImageAsset, type IRFontAsset, type IRLayer } from '@vvfx/animation-ir';
2
+ import type { Composition } from '../dsl/schema';
3
+ /** Convert authored 2D DSL sibling groups to flat IR layers in bottom-to-top painter order. */
4
+ export declare function convertDSLLayersToIR(composition: Composition, imageAssets?: IRImageAsset[], fontAssets?: IRFontAsset[]): IRLayer[];
@@ -0,0 +1,4 @@
1
+ import type { IRParticleBurst, IRParticleContent } from '@vvfx/animation-ir';
2
+ import type { ParticleBurst, ParticleProperties } from '../dsl/schema';
3
+ export declare function convertDSLParticleToIRContent(particle: ParticleProperties, frameRate: number): IRParticleContent;
4
+ export declare function mapDSLParticleBurstsToIR(bursts: ParticleBurst[], frameRate: number): IRParticleBurst[];
@@ -0,0 +1,6 @@
1
+ import type { IRAnimatedProperty, IRRotation, IRStaticProperty, IRVec3 } from '@vvfx/animation-ir';
2
+ import type { AnimatedProperty } from '../dsl/schema';
3
+ export declare function convertDSLPropertyToIRVec3(property: AnimatedProperty | undefined, frameRate: number, fallback: IRVec3, includeSpatialTangents?: boolean): IRAnimatedProperty<IRVec3>;
4
+ export declare function convertDSLPropertyToStaticIRVec3(property: AnimatedProperty | undefined, fallback: IRVec3): IRStaticProperty<IRVec3>;
5
+ export declare function convertDSLPropertyToIRNumber(property: AnimatedProperty | undefined, frameRate: number, fallback: number): IRAnimatedProperty<number>;
6
+ export declare function convertDSLPropertyToIRRotation(property: AnimatedProperty | undefined, frameRate: number, fallback: IRVec3): IRRotation;
@@ -0,0 +1,4 @@
1
+ import type { Composition } from '../dsl/schema';
2
+ import type { ConvertDSLToIROptions, DSLToIRResult } from './types';
3
+ /** Convert an editable DSL composition into a shared Animation IR scene. */
4
+ export declare function convertDSLToIR(composition: Composition, options?: ConvertDSLToIROptions): DSLToIRResult;
@@ -0,0 +1,18 @@
1
+ import type { AnimatedProperty, Asset, Layer, TextProperties } from '../../dsl/schema';
2
+ declare const TEXT_PROPERTY_FIELDS: readonly ["content", "font", "size", "color", "align", "line_height", "letter_spacing"];
3
+ interface TextPropertyChange {
4
+ field: (typeof TEXT_PROPERTY_FIELDS)[number];
5
+ oldValue: TextProperties[(typeof TEXT_PROPERTY_FIELDS)[number]];
6
+ newValue: TextProperties[(typeof TEXT_PROPERTY_FIELDS)[number]];
7
+ }
8
+ /**
9
+ * Structural, epsilon-tolerant equality between two editable-DSL properties.
10
+ *
11
+ * Used for preserved-data change detection so writeback replaces only edited
12
+ * fields in the IR snapshot.
13
+ */
14
+ export declare function isAnimatedPropertyEqual(leftProperty: AnimatedProperty | undefined, rightProperty: AnimatedProperty | undefined): boolean;
15
+ export declare function collectTextPropertyChanges(baselineText: TextProperties, editedText: TextProperties): TextPropertyChange[];
16
+ export declare function readTextProperties(layer: Layer): TextProperties | undefined;
17
+ export declare function isAssetEqual(leftAsset: Asset, rightAsset: Asset): boolean;
18
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Composition } from '../../dsl/schema';
2
+ import type { IRToDSLPreservedData } from '../../ir2dsl/types';
3
+ import type { ConvertDSLToIROptions, DSLToIRResult } from '../types';
4
+ /** Convert DSL to a clone of the baseline IR carried by the adapter-owned preserved data. */
5
+ export declare function convertDSLToIRWithPreservedData(composition: Composition, preservedData: IRToDSLPreservedData, options?: ConvertDSLToIROptions): DSLToIRResult;
@@ -0,0 +1,6 @@
1
+ import { type IRScene } from '@vvfx/animation-ir';
2
+ import type { Composition } from '../../dsl/schema';
3
+ import type { IRToDSLLayerMapping } from '../../ir2dsl/types';
4
+ import type { ConvertDSLToIROptions } from '../types';
5
+ /** Apply previously validated DSL to a clone of the baseline IR. */
6
+ export declare function updateValidatedIRFromDSL(baselineScene: IRScene, editedDocument: Composition, layerMapping: IRToDSLLayerMapping, options?: ConvertDSLToIROptions): IRScene;
@@ -0,0 +1,5 @@
1
+ import type { IRComposition } from '@vvfx/animation-ir';
2
+ import type { Layer } from '../../dsl/schema';
3
+ import type { DSLToIRChangeReporter } from '../types';
4
+ /** Apply edited sibling order without changing parent relationships. */
5
+ export declare function updateLayerOrder(updatedIR: IRComposition, editedLayers: Layer[], irLayerIdByEditedLayer: Map<Layer, string>, reportChange: DSLToIRChangeReporter, rootDSLParentId?: string): void;
@@ -0,0 +1,4 @@
1
+ import type { IRLayer, IRScene } from '@vvfx/animation-ir';
2
+ import type { Layer } from '../../dsl/schema';
3
+ import type { DSLToIRChangeReporter, DSLToIRWarningReporter } from '../types';
4
+ export declare function updateIRLayer(irLayer: IRLayer, irScene: IRScene, editedLayer: Layer, baselineLayer: Layer, dslPath: string, frameRate: number, strict: boolean, reportWarning: DSLToIRWarningReporter, reportChange: DSLToIRChangeReporter): void;