sheetom 0.1.0-rc.9 → 0.1.0

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.
package/dist/index.d.cts CHANGED
@@ -1,6 +1,30 @@
1
1
  //#region src/diagnostics.d.ts
2
2
  /** Stable machine-readable codes emitted by opt-in mutation diagnostics. */
3
- type SheetOMDiagnosticCode = "INVALID_PRIORITY" | "INVALID_PROPERTY_VALUE" | "UNSUPPORTED_SHORTHAND_VALUE";
3
+ type SheetOMMutationDiagnosticCode = "INVALID_PRIORITY" | "INVALID_PROPERTY_VALUE" | "UNSUPPORTED_SHORTHAND_VALUE";
4
+ type SheetOMSerializationDiagnosticCode = "UNREPRESENTABLE_PENDING_SHORTHAND";
5
+ type SheetOMDiagnosticCode = SheetOMMutationDiagnosticCode | SheetOMSerializationDiagnosticCode;
6
+ /** A structured explanation for an ignored mutation. */
7
+ interface SheetOMMutationDiagnostic {
8
+ code: SheetOMMutationDiagnosticCode;
9
+ severity: "warning";
10
+ operation: "setProperty";
11
+ message: string;
12
+ property: string;
13
+ input: string;
14
+ location: null;
15
+ }
16
+ /** A structured explanation for a best-effort serialization recovery. */
17
+ interface SheetOMSerializationDiagnostic {
18
+ code: SheetOMSerializationDiagnosticCode;
19
+ severity: "warning";
20
+ operation: "serialize";
21
+ message: string;
22
+ property: string;
23
+ input: "";
24
+ location: null;
25
+ conflictingLonghands: readonly string[];
26
+ }
27
+ type SheetOMDiagnostic = SheetOMMutationDiagnostic | SheetOMSerializationDiagnostic;
4
28
  //#endregion
5
29
  //#region src/generated/css-style-declaration-properties.d.ts
6
30
  /** Named CSS properties supported by the pinned Chromium compatibility baseline. */
@@ -728,17 +752,36 @@ interface SheetOMResourceBudget {
728
752
  maxDeclarationsPerBlock?: number;
729
753
  }
730
754
  //#endregion
755
+ //#region src/serialization-error.d.ts
756
+ type SheetOMSerializationErrorCode = "UNREPRESENTABLE_PENDING_SHORTHAND";
757
+ /** A reparsable stylesheet cannot faithfully represent the current CSSOM state. */
758
+ declare class SheetOMSerializationError extends Error {
759
+ readonly code: SheetOMSerializationErrorCode;
760
+ readonly shorthand: string;
761
+ readonly conflictingLonghands: readonly string[];
762
+ constructor(shorthand: string, conflictingLonghands: readonly string[], options?: ErrorOptions);
763
+ }
764
+ //#endregion
731
765
  //#region src/index.d.ts
732
- /** A structured explanation for an ignored or recovered mutation. */
733
- interface SheetOMDiagnostic {
734
- code: SheetOMDiagnosticCode;
735
- severity: "warning";
736
- operation: "setProperty";
737
- message: string;
766
+ /** One ordered declaration mutation executed by SheetOM's engine extension. */
767
+ type CSSDeclarationMutation = {
768
+ kind: "set";
738
769
  property: string;
739
- input: string;
740
- location: null;
741
- }
770
+ value: string | null;
771
+ priority?: string | null;
772
+ } | {
773
+ kind: "remove";
774
+ property: string;
775
+ };
776
+ /** The result at the matching index of an ordered declaration mutation batch. */
777
+ type CSSDeclarationMutationResult = {
778
+ kind: "set";
779
+ accepted: boolean;
780
+ diagnostic: SheetOMDiagnostic | null;
781
+ } | {
782
+ kind: "remove";
783
+ value: string;
784
+ };
742
785
  /** Options for a constructed SheetOM stylesheet. */
743
786
  interface CSSStyleSheetOptions {
744
787
  baseURL?: string;
@@ -954,6 +997,8 @@ declare class CSSStyleDeclaration {
954
997
  getPropertyValue(name: string): string;
955
998
  getPropertyPriority(name: string): string;
956
999
  setProperty(name: string, value: string | null, priority?: string): void;
1000
+ /** Apply ordered declaration mutations through one engine boundary crossing. */
1001
+ applyMutations(mutations: readonly CSSDeclarationMutation[]): CSSDeclarationMutationResult[];
957
1002
  removeProperty(name: string): string;
958
1003
  }
959
1004
  /** A live `@font-face` descriptor rule. */
@@ -1126,11 +1171,13 @@ declare class CSSStyleSheet {
1126
1171
  replace(cssText: string): Promise<CSSStyleSheet>;
1127
1172
  /** Serialize current state as reparsable CSS without mutating live objects. */
1128
1173
  serialize(): string;
1129
- /** Drain opt-in mutation diagnostics without affecting CSSOM behavior. */
1174
+ /** Serialize current state only when its CSS semantics can be preserved exactly. */
1175
+ serializeStrict(): string;
1176
+ /** Drain opt-in mutation and serialization diagnostics without affecting CSSOM behavior. */
1130
1177
  takeDiagnostics(): SheetOMDiagnostic[];
1131
1178
  }
1132
1179
  /** Forgivingly parse an existing regular stylesheet without loading imports. */
1133
1180
  declare function parseStyleSheet(cssText: string, options?: ParseStyleSheetOptions): CSSStyleSheet;
1134
1181
  //#endregion
1135
- export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSStyleSheetOptions, CSSSupportsRule, CSSViewTransitionRule, FunctionParameter, MediaList, ParseStyleSheetOptions, SheetOMDiagnostic, type SheetOMDiagnosticCode, type SheetOMResourceBudget, parseStyleSheet };
1182
+ export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSDeclarationMutation, CSSDeclarationMutationResult, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSStyleSheetOptions, CSSSupportsRule, CSSViewTransitionRule, FunctionParameter, MediaList, ParseStyleSheetOptions, type SheetOMDiagnostic, type SheetOMDiagnosticCode, type SheetOMMutationDiagnostic, type SheetOMMutationDiagnosticCode, type SheetOMResourceBudget, type SheetOMSerializationDiagnostic, type SheetOMSerializationDiagnosticCode, SheetOMSerializationError, type SheetOMSerializationErrorCode, parseStyleSheet };
1136
1183
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1,6 +1,30 @@
1
1
  //#region src/diagnostics.d.ts
2
2
  /** Stable machine-readable codes emitted by opt-in mutation diagnostics. */
3
- type SheetOMDiagnosticCode = "INVALID_PRIORITY" | "INVALID_PROPERTY_VALUE" | "UNSUPPORTED_SHORTHAND_VALUE";
3
+ type SheetOMMutationDiagnosticCode = "INVALID_PRIORITY" | "INVALID_PROPERTY_VALUE" | "UNSUPPORTED_SHORTHAND_VALUE";
4
+ type SheetOMSerializationDiagnosticCode = "UNREPRESENTABLE_PENDING_SHORTHAND";
5
+ type SheetOMDiagnosticCode = SheetOMMutationDiagnosticCode | SheetOMSerializationDiagnosticCode;
6
+ /** A structured explanation for an ignored mutation. */
7
+ interface SheetOMMutationDiagnostic {
8
+ code: SheetOMMutationDiagnosticCode;
9
+ severity: "warning";
10
+ operation: "setProperty";
11
+ message: string;
12
+ property: string;
13
+ input: string;
14
+ location: null;
15
+ }
16
+ /** A structured explanation for a best-effort serialization recovery. */
17
+ interface SheetOMSerializationDiagnostic {
18
+ code: SheetOMSerializationDiagnosticCode;
19
+ severity: "warning";
20
+ operation: "serialize";
21
+ message: string;
22
+ property: string;
23
+ input: "";
24
+ location: null;
25
+ conflictingLonghands: readonly string[];
26
+ }
27
+ type SheetOMDiagnostic = SheetOMMutationDiagnostic | SheetOMSerializationDiagnostic;
4
28
  //#endregion
5
29
  //#region src/generated/css-style-declaration-properties.d.ts
6
30
  /** Named CSS properties supported by the pinned Chromium compatibility baseline. */
@@ -728,17 +752,36 @@ interface SheetOMResourceBudget {
728
752
  maxDeclarationsPerBlock?: number;
729
753
  }
730
754
  //#endregion
755
+ //#region src/serialization-error.d.ts
756
+ type SheetOMSerializationErrorCode = "UNREPRESENTABLE_PENDING_SHORTHAND";
757
+ /** A reparsable stylesheet cannot faithfully represent the current CSSOM state. */
758
+ declare class SheetOMSerializationError extends Error {
759
+ readonly code: SheetOMSerializationErrorCode;
760
+ readonly shorthand: string;
761
+ readonly conflictingLonghands: readonly string[];
762
+ constructor(shorthand: string, conflictingLonghands: readonly string[], options?: ErrorOptions);
763
+ }
764
+ //#endregion
731
765
  //#region src/index.d.ts
732
- /** A structured explanation for an ignored or recovered mutation. */
733
- interface SheetOMDiagnostic {
734
- code: SheetOMDiagnosticCode;
735
- severity: "warning";
736
- operation: "setProperty";
737
- message: string;
766
+ /** One ordered declaration mutation executed by SheetOM's engine extension. */
767
+ type CSSDeclarationMutation = {
768
+ kind: "set";
738
769
  property: string;
739
- input: string;
740
- location: null;
741
- }
770
+ value: string | null;
771
+ priority?: string | null;
772
+ } | {
773
+ kind: "remove";
774
+ property: string;
775
+ };
776
+ /** The result at the matching index of an ordered declaration mutation batch. */
777
+ type CSSDeclarationMutationResult = {
778
+ kind: "set";
779
+ accepted: boolean;
780
+ diagnostic: SheetOMDiagnostic | null;
781
+ } | {
782
+ kind: "remove";
783
+ value: string;
784
+ };
742
785
  /** Options for a constructed SheetOM stylesheet. */
743
786
  interface CSSStyleSheetOptions {
744
787
  baseURL?: string;
@@ -954,6 +997,8 @@ declare class CSSStyleDeclaration {
954
997
  getPropertyValue(name: string): string;
955
998
  getPropertyPriority(name: string): string;
956
999
  setProperty(name: string, value: string | null, priority?: string): void;
1000
+ /** Apply ordered declaration mutations through one engine boundary crossing. */
1001
+ applyMutations(mutations: readonly CSSDeclarationMutation[]): CSSDeclarationMutationResult[];
957
1002
  removeProperty(name: string): string;
958
1003
  }
959
1004
  /** A live `@font-face` descriptor rule. */
@@ -1126,11 +1171,13 @@ declare class CSSStyleSheet {
1126
1171
  replace(cssText: string): Promise<CSSStyleSheet>;
1127
1172
  /** Serialize current state as reparsable CSS without mutating live objects. */
1128
1173
  serialize(): string;
1129
- /** Drain opt-in mutation diagnostics without affecting CSSOM behavior. */
1174
+ /** Serialize current state only when its CSS semantics can be preserved exactly. */
1175
+ serializeStrict(): string;
1176
+ /** Drain opt-in mutation and serialization diagnostics without affecting CSSOM behavior. */
1130
1177
  takeDiagnostics(): SheetOMDiagnostic[];
1131
1178
  }
1132
1179
  /** Forgivingly parse an existing regular stylesheet without loading imports. */
1133
1180
  declare function parseStyleSheet(cssText: string, options?: ParseStyleSheetOptions): CSSStyleSheet;
1134
1181
  //#endregion
1135
- export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSStyleSheetOptions, CSSSupportsRule, CSSViewTransitionRule, FunctionParameter, MediaList, ParseStyleSheetOptions, SheetOMDiagnostic, type SheetOMDiagnosticCode, type SheetOMResourceBudget, parseStyleSheet };
1182
+ export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSDeclarationMutation, CSSDeclarationMutationResult, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSStyleSheetOptions, CSSSupportsRule, CSSViewTransitionRule, FunctionParameter, MediaList, ParseStyleSheetOptions, type SheetOMDiagnostic, type SheetOMDiagnosticCode, type SheetOMMutationDiagnostic, type SheetOMMutationDiagnosticCode, type SheetOMResourceBudget, type SheetOMSerializationDiagnostic, type SheetOMSerializationDiagnosticCode, SheetOMSerializationError, type SheetOMSerializationErrorCode, parseStyleSheet };
1136
1183
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import { fileURLToPath } from "node:url";
4
4
  //#endregion
5
5
  //#region src/internal/engine-binding.ts
6
6
  const expectedEngineAbiIdentity = Object.freeze({
7
- abiVersion: 1,
8
- sheetomVersion: "0.1.0-rc.9",
9
- syntaxEngineSetSha256: "2af7ec76ba46a3f20f74a90172a708afab5dcb20f45b658a4d975caed76dc855"
7
+ abiVersion: 3,
8
+ sheetomVersion: "0.1.0",
9
+ syntaxEngineSetSha256: "31990c26ede4e17c3a8941027f92b2be96a167984d0ab6529e724c3d6126be50"
10
10
  });
11
11
  var SheetOMEngineBindingError = class extends Error {
12
12
  code;
@@ -90,16 +90,47 @@ function limitValue(value, name, fallback, maximum = maximumUnsignedLong) {
90
90
  throw new RangeError(`${name} must be an integer between 0 and ${maximum}`);
91
91
  }
92
92
  //#endregion
93
+ //#region src/serialization-error.ts
94
+ /** A reparsable stylesheet cannot faithfully represent the current CSSOM state. */
95
+ var SheetOMSerializationError = class extends Error {
96
+ code;
97
+ shorthand;
98
+ conflictingLonghands;
99
+ constructor(shorthand, conflictingLonghands, options) {
100
+ super(`SheetOM cannot serialize the pending ${shorthand} shorthand without changing its semantics.`, options);
101
+ this.name = "SheetOMSerializationError";
102
+ this.code = "UNREPRESENTABLE_PENDING_SHORTHAND";
103
+ this.shorthand = shorthand;
104
+ this.conflictingLonghands = Object.freeze([...conflictingLonghands]);
105
+ }
106
+ };
107
+ const unrepresentablePendingShorthandPattern = /^SHEETOM_UNREPRESENTABLE_PENDING_SHORTHAND: shorthand=([^;]+); conflicting=([a-z0-9,-]+)$/u;
108
+ function serializationErrorFrom(error) {
109
+ if (!(error instanceof Error)) return null;
110
+ const match = unrepresentablePendingShorthandPattern.exec(error.message);
111
+ if (!match) return null;
112
+ const shorthand = match[1];
113
+ const conflicts = match[2];
114
+ if (!shorthand || !conflicts) return null;
115
+ return new SheetOMSerializationError(shorthand, conflicts.split(","), { cause: error });
116
+ }
117
+ function rethrowSerializationError(error) {
118
+ const serializationError = serializationErrorFrom(error);
119
+ if (serializationError) throw serializationError;
120
+ }
121
+ //#endregion
93
122
  //#region src/internal/native-declaration-block.ts
94
123
  /** Thin JS ownership boundary around the Rust declaration state machine. */
95
124
  var NativeDeclarationBlock = class {
96
125
  #state;
97
126
  #reportDiagnostic;
127
+ #reportSerializationDiagnostic;
98
128
  #reservedNestingDepth;
99
129
  #observableCache;
100
130
  #serializationCache = /* @__PURE__ */ new Map();
101
- constructor(reportDiagnostic, context = "style", resourceBudget = defaultResourceBudget, reservedNestingDepth = () => 0) {
131
+ constructor(reportDiagnostic, reportSerializationDiagnostic, context = "style", resourceBudget = defaultResourceBudget, reservedNestingDepth = () => 0) {
102
132
  this.#reportDiagnostic = reportDiagnostic;
133
+ this.#reportSerializationDiagnostic = reportSerializationDiagnostic;
103
134
  this.#reservedNestingDepth = reservedNestingDepth;
104
135
  this.#state = engineBinding.createDeclarationState(context, ...nativeBudgetArguments(resourceBudget));
105
136
  }
@@ -131,9 +162,59 @@ var NativeDeclarationBlock = class {
131
162
  this.#invalidateSerialization();
132
163
  return;
133
164
  }
134
- const normalizedName = name.startsWith("--") ? name : name.toLowerCase();
135
- const [code, input] = outcome === "invalid-priority" ? ["INVALID_PRIORITY", priority] : outcome === "unsupported-shorthand" ? ["UNSUPPORTED_SHORTHAND_VALUE", value] : ["INVALID_PROPERTY_VALUE", value];
136
- this.#reportDiagnostic(code, normalizedName, input);
165
+ this.#diagnosticForOutcome(outcome, name, value, priority);
166
+ }
167
+ applyMutations(mutations) {
168
+ const kinds = [];
169
+ const properties = [];
170
+ const values = [];
171
+ const priorities = [];
172
+ for (const mutation of mutations) {
173
+ kinds.push(mutation.kind === "set" ? 0 : 1);
174
+ properties.push(mutation.property);
175
+ values.push(mutation.kind === "set" ? mutation.value : "");
176
+ priorities.push(mutation.kind === "set" ? mutation.priority : "");
177
+ }
178
+ let outcomes;
179
+ try {
180
+ outcomes = this.#state.applyMutations(kinds, properties, values, priorities, this.#reservedNestingDepth());
181
+ } catch (error) {
182
+ rethrowResourceBudgetError(error);
183
+ throw error;
184
+ }
185
+ if (outcomes.length !== mutations.length) throw new Error("SheetOM engine returned an incomplete declaration mutation result.");
186
+ const results = [];
187
+ let mutated = false;
188
+ for (let index = 0; index < outcomes.length; index += 1) {
189
+ const mutation = mutations[index];
190
+ const outcome = outcomes[index];
191
+ if (!mutation || outcome === void 0) throw new Error("SheetOM engine returned a mismatched declaration mutation result.");
192
+ if (mutation.kind === "remove") {
193
+ mutated = true;
194
+ results.push({
195
+ kind: "remove",
196
+ value: outcome
197
+ });
198
+ continue;
199
+ }
200
+ if (outcome === "applied") {
201
+ mutated = true;
202
+ results.push({
203
+ kind: "set",
204
+ accepted: true,
205
+ diagnostic: null
206
+ });
207
+ continue;
208
+ }
209
+ if (!isEngineMutationOutcome(outcome)) throw new Error("SheetOM engine returned an invalid declaration mutation outcome.");
210
+ results.push({
211
+ kind: "set",
212
+ accepted: false,
213
+ diagnostic: this.#diagnosticForOutcome(outcome, mutation.property, mutation.value, mutation.priority)
214
+ });
215
+ }
216
+ if (mutated) this.#invalidateSerialization();
217
+ return results;
137
218
  }
138
219
  removeProperty(name) {
139
220
  const removed = this.#state.removeProperty(name);
@@ -149,19 +230,63 @@ var NativeDeclarationBlock = class {
149
230
  }
150
231
  this.#invalidateSerialization();
151
232
  }
152
- serialize(safe, indent, separator) {
153
- const cacheKey = `${safe ? "1" : "0"}\0${indent}\0${separator}`;
233
+ serialize(safe, indent, separator, strict = false) {
234
+ const cacheKey = `${safe ? "1" : "0"}${strict ? "1" : "0"}\0${indent}\0${separator}`;
154
235
  const cached = this.#serializationCache.get(cacheKey);
155
- if (cached !== void 0) return cached;
156
- const serialized = this.#state.serializeFormatted(safe, indent, separator);
157
- this.#serializationCache.set(cacheKey, serialized);
158
- return serialized;
236
+ if (cached !== void 0) {
237
+ if (typeof cached === "string") return cached;
238
+ this.#reportSerializationIssues(cached.issues);
239
+ return cached.serialized;
240
+ }
241
+ let result;
242
+ try {
243
+ if (strict) result = this.#state.serializeFormatted(safe, indent, separator);
244
+ else result = parseResilientSerializationResult(this.#state.serializeFormattedResilient(safe, indent, separator));
245
+ } catch (error) {
246
+ rethrowResourceBudgetError(error);
247
+ rethrowSerializationError(error);
248
+ throw error;
249
+ }
250
+ this.#serializationCache.set(cacheKey, result);
251
+ if (typeof result === "string") return result;
252
+ this.#reportSerializationIssues(result.issues);
253
+ return result.serialized;
159
254
  }
160
255
  #invalidateSerialization() {
161
256
  this.#observableCache = void 0;
162
257
  this.#serializationCache.clear();
163
258
  }
259
+ #diagnosticForOutcome(outcome, name, value, priority) {
260
+ const normalizedName = name.startsWith("--") ? name : name.toLowerCase();
261
+ const [code, input] = outcome === "invalid-priority" ? ["INVALID_PRIORITY", priority] : outcome === "unsupported-shorthand" ? ["UNSUPPORTED_SHORTHAND_VALUE", value] : ["INVALID_PROPERTY_VALUE", value];
262
+ return this.#reportDiagnostic(code, normalizedName, input);
263
+ }
264
+ #reportSerializationIssues(issues) {
265
+ for (const issue of issues) this.#reportSerializationDiagnostic(issue.shorthand, issue.conflictingLonghands);
266
+ }
164
267
  };
268
+ function parseResilientSerializationResult(output) {
269
+ const serialized = output[0];
270
+ if (serialized === void 0 || output.length % 2 === 0) throw new Error("SheetOM engine returned an invalid resilient serialization result.");
271
+ if (output.length === 1) return serialized;
272
+ const issues = [];
273
+ for (let index = 1; index < output.length; index += 2) {
274
+ const shorthand = output[index];
275
+ const conflicts = output[index + 1];
276
+ if (!shorthand || !conflicts) throw new Error("SheetOM engine returned an incomplete resilient serialization issue.");
277
+ issues.push({
278
+ shorthand,
279
+ conflictingLonghands: Object.freeze(conflicts.split(","))
280
+ });
281
+ }
282
+ return {
283
+ serialized,
284
+ issues
285
+ };
286
+ }
287
+ function isEngineMutationOutcome(value) {
288
+ return value === "invalid-name" || value === "invalid-priority" || value === "invalid-value" || value === "unsupported-shorthand";
289
+ }
165
290
  function toUnsignedLong$1(value) {
166
291
  const numeric = Number(value);
167
292
  if (!Number.isFinite(numeric) || numeric === 0) return 0;
@@ -353,13 +478,20 @@ var Serializer = class {
353
478
  this.#describe = describe;
354
479
  }
355
480
  serialize(node) {
481
+ return this.serializeMany([node]);
482
+ }
483
+ serializeMany(nodes) {
356
484
  const chunks = [];
357
- const pending = [{
358
- kind: "node",
359
- node,
360
- depth: 0,
361
- child: false
362
- }];
485
+ const pending = [];
486
+ for (let index = nodes.length - 1; index >= 0; index -= 1) {
487
+ const node = nodes[index];
488
+ if (node) pending.push({
489
+ kind: "node",
490
+ node,
491
+ depth: 0,
492
+ child: false
493
+ });
494
+ }
363
495
  while (pending.length > 0) {
364
496
  const frame = pending.pop();
365
497
  if (!frame) continue;
@@ -473,7 +605,8 @@ const ruleTree = new RuleTree((rule, parentRule, parentStyleSheet) => {
473
605
  parentStyleSheet
474
606
  });
475
607
  }, (rule) => rule.parentStyleSheet);
476
- const safeSerializer = new Serializer(describeRuleSafe);
608
+ const safeSerializer = new Serializer((rule) => describeRuleSafe(rule, false));
609
+ const strictSerializer = new Serializer((rule) => describeRuleSafe(rule, true));
477
610
  let activeConstructionResourceBudget = defaultResourceBudget;
478
611
  function constructWithResourceBudget(factory, resourceBudget) {
479
612
  const previous = activeConstructionResourceBudget;
@@ -1126,19 +1259,32 @@ var CSSStyleDeclaration = class {
1126
1259
  this.parentRule = parentRule;
1127
1260
  lockOwnProperties(this, "parentRule");
1128
1261
  const reportDeclarationDiagnostic = (code, property, input) => {
1129
- const priority = code === "INVALID_PRIORITY";
1130
- const unsupportedShorthand = code === "UNSUPPORTED_SHORTHAND_VALUE";
1131
- (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)({
1262
+ const diagnostic = {
1132
1263
  code,
1133
1264
  severity: "warning",
1134
1265
  operation: "setProperty",
1135
- message: priority ? `The mutation was ignored because ${input} is not a valid priority.` : unsupportedShorthand ? `The value was ignored because the shorthand codec for ${property} cannot expand it.` : `The value was ignored because it is invalid for ${property}.`,
1266
+ message: code === "INVALID_PRIORITY" ? `The mutation was ignored because ${input} is not a valid priority.` : code === "UNSUPPORTED_SHORTHAND_VALUE" ? `The value was ignored because the shorthand codec for ${property} cannot expand it.` : `The value was ignored because it is invalid for ${property}.`,
1136
1267
  property,
1137
1268
  input,
1138
1269
  location: null
1139
- });
1270
+ };
1271
+ (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)(diagnostic);
1272
+ return diagnostic;
1273
+ };
1274
+ const reportSerializationDiagnostic = (shorthand, conflictingLonghands) => {
1275
+ const diagnostic = {
1276
+ code: "UNREPRESENTABLE_PENDING_SHORTHAND",
1277
+ severity: "warning",
1278
+ operation: "serialize",
1279
+ message: `The pending ${shorthand} shorthand required a best-effort recovery during serialization.`,
1280
+ property: shorthand,
1281
+ input: "",
1282
+ location: null,
1283
+ conflictingLonghands: Object.freeze([...conflictingLonghands])
1284
+ };
1285
+ (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)(diagnostic);
1140
1286
  };
1141
- this.#block = new NativeDeclarationBlock(reportDeclarationDiagnostic, parentRule instanceof CSSFontFaceRule ? "font-face" : parentRule instanceof CSSFunctionDeclarations ? "function" : "style", ruleResourceBudgets.get(parentRule) ?? defaultResourceBudget, () => declarationSerializationDepth(parentRule));
1287
+ this.#block = new NativeDeclarationBlock(reportDeclarationDiagnostic, reportSerializationDiagnostic, parentRule instanceof CSSFontFaceRule ? "font-face" : parentRule instanceof CSSFunctionDeclarations ? "function" : "style", ruleResourceBudgets.get(parentRule) ?? defaultResourceBudget, () => declarationSerializationDepth(parentRule));
1142
1288
  return new Proxy(this, {
1143
1289
  get(target, property) {
1144
1290
  if (typeof property === "string" && arrayIndexPattern.test(property)) return target.item(Number(property)) || void 0;
@@ -1183,13 +1329,41 @@ var CSSStyleDeclaration = class {
1183
1329
  const stringPriority = priority === null ? "" : `${priority}`;
1184
1330
  this.#block.setProperty(stringName, stringValue, stringPriority);
1185
1331
  }
1332
+ /** Apply ordered declaration mutations through one engine boundary crossing. */
1333
+ applyMutations(mutations) {
1334
+ requireArguments(arguments.length, 1, "CSSStyleDeclaration", "applyMutations");
1335
+ if (!Array.isArray(mutations)) throw new TypeError("CSSStyleDeclaration.applyMutations requires an array.");
1336
+ const normalized = [];
1337
+ for (let index = 0; index < mutations.length; index += 1) {
1338
+ const mutation = mutations[index];
1339
+ if (typeof mutation !== "object" || mutation === null) throw new TypeError(`Declaration mutation at index ${index} must be an object.`);
1340
+ if (mutation.kind !== "set" && mutation.kind !== "remove") throw new TypeError(`Declaration mutation at index ${index} has an invalid kind.`);
1341
+ if (typeof mutation.property !== "string") throw new TypeError(`Declaration mutation at index ${index} requires a string property.`);
1342
+ if (mutation.kind === "remove") {
1343
+ normalized.push({
1344
+ kind: "remove",
1345
+ property: mutation.property
1346
+ });
1347
+ continue;
1348
+ }
1349
+ if (typeof mutation.value !== "string" && mutation.value !== null) throw new TypeError(`Declaration mutation at index ${index} requires a string or null value.`);
1350
+ if (mutation.priority !== void 0 && mutation.priority !== null && typeof mutation.priority !== "string") throw new TypeError(`Declaration mutation at index ${index} requires a string priority.`);
1351
+ normalized.push({
1352
+ kind: "set",
1353
+ property: mutation.property,
1354
+ value: mutation.value ?? "",
1355
+ priority: mutation.priority ?? ""
1356
+ });
1357
+ }
1358
+ return this.#block.applyMutations(normalized);
1359
+ }
1186
1360
  removeProperty(name) {
1187
1361
  requireArguments(arguments.length, 1, "CSSStyleDeclaration", "removeProperty");
1188
1362
  return this.#block.removeProperty(`${name}`);
1189
1363
  }
1190
1364
  /** @internal */
1191
- serializeSafe(indent) {
1192
- return this.#block.serialize(true, indent, "\n");
1365
+ serializeSafe(indent, strict = false) {
1366
+ return this.#block.serialize(true, indent, "\n", strict);
1193
1367
  }
1194
1368
  };
1195
1369
  /** A live `@font-face` descriptor rule. */
@@ -1678,26 +1852,23 @@ function replaceGroupingRules(group, rules) {
1678
1852
  function attachRuleTree(rule, parentRule, parentStyleSheet) {
1679
1853
  ruleTree.attach(rule, parentRule, parentStyleSheet);
1680
1854
  }
1681
- function serializeRuleSafe(rule) {
1682
- return safeSerializer.serialize(rule);
1683
- }
1684
- function describeRuleSafe(rule) {
1855
+ function describeRuleSafe(rule, strict) {
1685
1856
  if (rule instanceof CSSStyleRule) return {
1686
1857
  kind: "block",
1687
1858
  header: rule.selectorText,
1688
- declarations: rule.style.serializeSafe(" "),
1859
+ declarations: rule.style.serializeSafe(" ", strict),
1689
1860
  children: ruleTree.children(rule)
1690
1861
  };
1691
1862
  if (rule instanceof CSSNestedDeclarations || rule instanceof CSSFunctionDeclarations) return {
1692
1863
  kind: "declarations",
1693
- declarations: rule.style.serializeSafe("")
1864
+ declarations: rule.style.serializeSafe("", strict)
1694
1865
  };
1695
1866
  if (rule instanceof CSSFontFaceRule || rule instanceof CSSMarginRule || rule instanceof CSSPositionTryRule || rule instanceof CSSKeyframeRule) {
1696
1867
  const blockIndex = rule.cssText.indexOf("{");
1697
1868
  return {
1698
1869
  kind: "block",
1699
1870
  header: rule.cssText.slice(0, blockIndex).trimEnd(),
1700
- declarations: rule.style.serializeSafe(" ")
1871
+ declarations: rule.style.serializeSafe(" ", strict)
1701
1872
  };
1702
1873
  }
1703
1874
  if (rule instanceof CSSKeyframesRule) return {
@@ -1708,7 +1879,7 @@ function describeRuleSafe(rule) {
1708
1879
  if (rule instanceof CSSPageRule) return {
1709
1880
  kind: "block",
1710
1881
  header: `@page${rule.selectorText === "" ? "" : ` ${rule.selectorText}`}`,
1711
- declarations: rule.style.serializeSafe(" "),
1882
+ declarations: rule.style.serializeSafe(" ", strict),
1712
1883
  children: ruleTree.children(rule)
1713
1884
  };
1714
1885
  if (!(rule instanceof CSSGroupingRule)) return {
@@ -2103,9 +2274,13 @@ var CSSStyleSheet = class {
2103
2274
  }
2104
2275
  /** Serialize current state as reparsable CSS without mutating live objects. */
2105
2276
  serialize() {
2106
- return this.#rules.map(serializeRuleSafe).join("");
2277
+ return safeSerializer.serializeMany(this.#rules);
2278
+ }
2279
+ /** Serialize current state only when its CSS semantics can be preserved exactly. */
2280
+ serializeStrict() {
2281
+ return strictSerializer.serializeMany(this.#rules);
2107
2282
  }
2108
- /** Drain opt-in mutation diagnostics without affecting CSSOM behavior. */
2283
+ /** Drain opt-in mutation and serialization diagnostics without affecting CSSOM behavior. */
2109
2284
  takeDiagnostics() {
2110
2285
  if (!this.#diagnostics) return [];
2111
2286
  return this.#diagnostics.splice(0);
@@ -2123,6 +2298,6 @@ function parseStyleSheet(cssText, options = {}) {
2123
2298
  return sheet;
2124
2299
  }
2125
2300
  //#endregion
2126
- export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSSupportsRule, CSSViewTransitionRule, MediaList, parseStyleSheet };
2301
+ export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSSupportsRule, CSSViewTransitionRule, MediaList, SheetOMSerializationError, parseStyleSheet };
2127
2302
 
2128
2303
  //# sourceMappingURL=index.js.map