sheetom 0.1.0-rc.0 → 0.1.0-rc.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.
package/README.md CHANGED
@@ -1,39 +1,36 @@
1
1
  # SheetOM
2
2
 
3
- SheetOM is a mutable, browser-shaped CSS authoring object model for Node.js,
4
- Bun, and Deno. It uses Lightning CSS for property parsing and canonicalization
5
- while keeping declaration state and CSSOM serialization under its own control.
3
+ **A browser-compatible, mutable CSSOM for JavaScript outside the browser.**
6
4
 
7
- It is intended for server-side stylesheet editing and serialization. It does
8
- not implement the DOM, cascade, selector matching, layout, or computed styles.
5
+ SheetOM lets build tools, publishers, and server applications parse, inspect,
6
+ mutate, and serialize stylesheets with familiar browser APIs. It preserves
7
+ browser recovery behavior for malformed-but-accepted CSS while still producing
8
+ deterministic, reparsable stylesheet output.
9
+
10
+ - Browser-shaped `CSSStyleSheet`, rules, and declarations
11
+ - Transactional `setProperty()` behavior and correct shorthand/longhand state
12
+ - Forgiving whole-sheet parsing without a DOM or global installation
13
+ - One repository-owned Rust engine for native and WebAssembly backends
14
+ - ESM, CommonJS, TypeScript, Node.js, Bun, Deno, browsers, and workers
15
+
16
+ SheetOM focuses on the **Authoring CSSOM**. It does not implement the cascade,
17
+ selector matching, layout, computed styles, fetching, or DOM attachment.
9
18
 
10
19
  ## Install
11
20
 
12
- SheetOM is not published yet. The first registry release will be
13
- `0.1.0-rc.0` under the `next` dist-tag after the complete conformance gate
14
- passes. Once published:
21
+ For Node.js, Bun, or Deno:
15
22
 
16
23
  ```sh
17
- npm install sheetom@next
24
+ npm install sheetom
18
25
  ```
19
26
 
20
- `lightningcss`, `css-tree`, `cssstyle`, and the CSS Tools tokenizer are regular
21
- dependencies; consumers do not need to install peer dependencies or globals.
22
- SheetOM validates values before using `cssstyle` only as a static shorthand
23
- expander, and uses the tokenizer for lossless recovered-rule spans.
24
-
25
- Node.js 22 and 24 are tested on Linux x64, Windows x64, and macOS arm64. Bun
26
- 1.3.1 and Deno 2.9.5 are tested on Linux x64. Deno must use a local
27
- `node_modules` directory and grant the native Lightning CSS binding FFI and
28
- system-information permissions:
27
+ For browsers and compatible JavaScript isolates:
29
28
 
30
29
  ```sh
31
- deno run --node-modules-dir=manual --allow-ffi --allow-sys app.ts
30
+ npm install @sheetom/wasm
32
31
  ```
33
32
 
34
- SheetOM exports browser-named classes but never modifies `globalThis`.
35
-
36
- ## Use
33
+ ## Quick start
37
34
 
38
35
  ```ts
39
36
  import { CSSStyleRule, CSSStyleSheet, parseStyleSheet } from "sheetom";
@@ -43,33 +40,68 @@ sheet.replaceSync(".card { width: 12px; }");
43
40
 
44
41
  const rule = sheet.cssRules[0];
45
42
  if (rule instanceof CSSStyleRule) {
46
- rule.style.setProperty("padding", "8px 16px");
47
- Reflect.set(rule.style, "backgroundColor", "rebeccapurple");
43
+ rule.style.padding = "8px 16px";
44
+ rule.style.backgroundColor = "rebeccapurple";
45
+ rule.style.setProperty("--card-gap", "12px");
48
46
  }
49
47
 
50
48
  console.log(sheet.serialize());
51
49
 
52
- const existing = parseStyleSheet('@import "theme.css"; .card { color: red; }', {
53
- href: "https://example.com/assets/app.css",
54
- });
50
+ const existing = parseStyleSheet(
51
+ '@import "theme.css"; .card { color: red; }',
52
+ { href: "https://example.com/assets/app.css" },
53
+ );
54
+
55
55
  console.log(existing.cssRules.length); // 2
56
56
  ```
57
57
 
58
- Constructed sheets follow browser replacement behavior and strip `@import`.
59
- `parseStyleSheet` creates a regular authoring sheet and preserves valid imports
60
- without loading them.
58
+ Named CSS properties are typed and assignable directly. Use `setProperty()`
59
+ when a property name is dynamic, custom, or hyphenated. `CSSStyleSheet` is the
60
+ only public CSSOM constructor; rules and declarations come from a sheet, just
61
+ as they do in browsers.
61
62
 
62
- ## Recovered values and safe serialization
63
+ Constructed sheets strip `@import` during replacement. `parseStyleSheet()` is a
64
+ SheetOM extension for existing regular stylesheets and preserves valid imports
65
+ without fetching them.
63
66
 
64
- SheetOM distinguishes the browser-facing CSSOM text from safe output. Chromium
65
- accepts some values using CSS Syntax end-of-input recovery while preserving the
66
- unclosed input in `getPropertyValue()` and `cssText`:
67
+ Compilers can apply all known mutations for one rule with one engine crossing
68
+ and still receive one acceptance result per operation:
69
+
70
+ ```ts
71
+ function applyCompilerDeclarations(rule: CSSStyleRule, dynamicWidth: string) {
72
+ const results = rule.style.applyMutations([
73
+ { kind: "set", property: "display", value: "grid" },
74
+ { kind: "set", property: "width", value: dynamicWidth },
75
+ { kind: "remove", property: "margin-left" },
76
+ ]);
77
+
78
+ for (const result of results) {
79
+ if (result.kind === "set" && !result.accepted) {
80
+ console.warn(result.diagnostic);
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ Operations run in order through the same validation and shorthand/longhand
87
+ state machine as individual calls. Group one batch per declaration block;
88
+ never combine values into declaration text. Batch results already include
89
+ rejection diagnostics, so high-volume callers can leave the sheet diagnostic
90
+ queue disabled unless they also need diagnostics from other APIs.
91
+
92
+ ## Browser recovery, safe output
93
+
94
+ Browsers accept some CSS using end-of-input recovery and expose the incomplete
95
+ spelling through CSSOM. SheetOM keeps that observable behavior separate from
96
+ whole-sheet output:
67
97
 
68
98
  ```ts
69
99
  const sheet = new CSSStyleSheet();
70
100
  sheet.insertRule(".card {}");
71
101
 
72
- const rule = sheet.cssRules[0] as CSSStyleRule;
102
+ const rule = sheet.cssRules[0];
103
+ if (!(rule instanceof CSSStyleRule)) throw new TypeError("Expected a style rule");
104
+
73
105
  rule.style.setProperty("padding", "72px var(--space, var(--space,");
74
106
 
75
107
  rule.style.getPropertyValue("padding");
@@ -79,76 +111,105 @@ rule.style.cssText;
79
111
  // "padding: 72px var(--space, var(--space,;"
80
112
 
81
113
  sheet.serialize();
82
- // reparsable CSS with the missing syntax repaired
114
+ // reparsable CSS with the missing syntax safely confined and repaired
115
+ ```
116
+
117
+ | Surface | Purpose |
118
+ | --- | --- |
119
+ | `style.cssText` | Browser-compatible declaration text, including measured recovery quirks. |
120
+ | `rule.cssText` | Browser-compatible text for the current rule. |
121
+ | `sheet.serialize()` | Deterministic, reparsable CSS for a complete stylesheet. |
122
+
123
+ Invalid property values and priorities are atomic no-ops: the previous state
124
+ survives unchanged. Static shorthands are stored as canonical longhands, so
125
+ editing and then removing one longhand cannot accidentally reactivate an old
126
+ shorthand value.
127
+
128
+ Reparsable output prevents malformed input from leaking into a following
129
+ declaration or rule. It is not a CSS sanitizer, URL policy, CSP, or rendering
130
+ sandbox.
131
+
132
+ ## Native and WebAssembly runtimes
133
+
134
+ The `sheetom` package contains the JavaScript facade and selects one exact
135
+ `@sheetom/native-*` optional package for the current platform. It installs no
136
+ JavaScript parser dependency, downloads no binary at install time, and never
137
+ falls back to a different engine. Omitting optional dependencies is unsupported
138
+ and fails explicitly.
139
+
140
+ | Runtime | Entry point | Notes |
141
+ | --- | --- | --- |
142
+ | Node.js 22+ | `sheetom` | ESM and CommonJS; prebuilt native packages for macOS, Windows, GNU Linux, and musl Linux targets. |
143
+ | Bun | `sheetom` | Tested through the npm package on Linux x64. |
144
+ | Deno | `sheetom` | Uses npm resolution and requires native FFI/system permissions. |
145
+ | Browsers and workers | `@sheetom/wasm` | ESM-only asynchronous factory; no WASI or global installation. |
146
+
147
+ Deno example:
148
+
149
+ ```sh
150
+ deno run --node-modules-dir=manual --allow-ffi --allow-sys app.ts
83
151
  ```
84
152
 
85
- Invalid `setProperty` values and priorities are atomic no-ops, matching browser
86
- behavior. Opt into mutation diagnostics with `{ diagnostics: true }` and drain
87
- them using `takeDiagnostics()`.
153
+ WebAssembly example:
88
154
 
89
- ## API scope
155
+ ```ts
156
+ import { createSheetOM } from "@sheetom/wasm";
90
157
 
91
- - `CSSStyleSheet`, live `CSSRuleList`, `CSSRule`, and `MediaList`
92
- - nested `CSSStyleRule`, `CSSGroupingRule`, and `CSSConditionRule`
93
- - media, supports, container, layer, scope, and starting-style rules
94
- - import, font-face, page/margin, nested-declaration, and position-try rules
95
- - keyframes and mutable keyframe rules
96
- - counter-style descriptors and font-feature-value maps
97
- - live indexed and named `CSSStyleDeclaration`
98
- - `insertRule`, `deleteRule`, `replace`, and `replaceSync`
99
- - `parseStyleSheet` for forgiving regular-sheet parsing
100
- - browser-facing `cssText` and reparsable `serialize()` output
101
- - generic retention of read-only metadata rules and experimental/future rules
158
+ const { CSSStyleSheet } = await createSheetOM();
159
+ const sheet = new CSSStyleSheet();
160
+ sheet.replaceSync(".card { color: red; }");
161
+ ```
102
162
 
103
- SheetOM targets standards and shared browser behavior first, with Chromium
104
- behavior used only as the final measured divergence fallback. Versioned JSON
105
- Operation Fixtures execute through SheetOM and native browser adapters;
106
- applicable WPT subtests retain their pinned source path, title, and blob SHA.
107
- Every release ships its machine-readable Compatibility Report.
163
+ The native and WebAssembly packages execute the same Rust engine and verify an
164
+ exact engine ABI identity before exposing CSSOM objects. `createSheetOM()`
165
+ shares default initialization within one JavaScript realm; passing an explicit
166
+ `URL`, `Response`, `ArrayBuffer`, or `WebAssembly.Module` creates an isolated
167
+ facade with its own class identity.
108
168
 
109
- See [the behavioral API reference](./docs/api.md) for return, exception,
110
- identity, and detachment contracts. Maintainers use the separately reviewed
111
- [release procedure](./docs/releasing.md); Changesets never publishes by itself.
169
+ ## Compatibility contract
112
170
 
113
- The checked-in ordinary-property manifest was generated against Chromium
114
- 151.0.7922.34. Run `npm run generate:properties` only when intentionally
115
- advancing the Compatibility Baseline.
171
+ SheetOM tests complete observable state—not only whether serialized CSS looks
172
+ similar. Its versioned evidence covers getters, `cssText`, indexed property
173
+ order, priorities, invalid-mutation atomicity, live identity, shorthand
174
+ sequences, recovery, native-browser reparsing, subprocess crash safety,
175
+ grammar-oriented fuzzing, and Publisher-shaped performance workloads.
116
176
 
117
- ## Security and resource boundaries
177
+ Standards and shared browser behavior take precedence. When browser engines
178
+ genuinely differ, SheetOM records the result and follows the pinned Chromium
179
+ baseline as its final fallback. Browser observations and WPT mappings remain
180
+ test evidence; they are never imported as runtime authority.
118
181
 
119
- SheetOM does not fetch `@import` targets or URLs and does not execute CSS. It is
120
- also not a sanitizer: valid authored URLs, imports, and browser features remain
121
- in serialized output. Callers must sanitize untrusted output before attaching
122
- it to a document or another rendering environment.
182
+ See [Compatibility](./docs/compatibility.md) for the exact promise, evidence,
183
+ and exclusions.
123
184
 
124
- The browser-shaped interface has no implicit input-size, nesting-depth, or
125
- mutation-count limits. Isolate or bound untrusted workloads according to your
126
- application's resource policy. See [SECURITY.md](./SECURITY.md) for private
127
- vulnerability reporting and supported-release policy.
185
+ ## Resource limits and diagnostics
128
186
 
129
- ## Development
187
+ Each sheet has configurable limits for stylesheet bytes, declaration-value
188
+ bytes, syntax depth, rule count, and declarations per block. A limit violation
189
+ throws `RangeError` before mutation. These budgets protect the host process;
190
+ they are not content policy.
130
191
 
131
- ```sh
132
- npm test
133
- npm run typecheck
134
- npm run build
135
- npm run conformance:validate
136
- npm run conformance:drift
137
- npm run fuzz
138
- npm run benchmark
139
- npm run pack:test
192
+ Mutation diagnostics are opt-in:
193
+
194
+ ```ts
195
+ const sheet = new CSSStyleSheet({ diagnostics: true });
196
+ // ...mutate the sheet...
197
+ const diagnostics = sheet.takeDiagnostics();
140
198
  ```
141
199
 
142
- `npm test` runs unit, deterministic fuzz, and local Chromium projects. Use
143
- `npm run test:browser:matrix` on a machine with all Playwright dependencies to
144
- run Chromium, Firefox, and WebKit. `npm run docs:build` generates the TypeDoc
145
- reference under `site/api`.
200
+ Diagnostics retain rejected inputs until drained. Leave them disabled or drain
201
+ them promptly when handling untrusted or unusually large source.
202
+
203
+ ## Documentation
146
204
 
147
- Only the latest published `0.x` minor and its active prereleases receive fixes
148
- before 1.0. During `0.x`, observable compatibility corrections and interface
149
- breaks require a new minor version and migration note; patches remain backward
150
- compatible.
205
+ - [API behavior](./docs/api.md)
206
+ - [Architecture](https://github.com/leo91000/sheetom/blob/main/docs/architecture.md)
207
+ - [Compatibility](https://github.com/leo91000/sheetom/blob/main/docs/compatibility.md)
208
+ - [Contributing](https://github.com/leo91000/sheetom/blob/main/CONTRIBUTING.md)
209
+ - [Release process](https://github.com/leo91000/sheetom/blob/main/docs/releasing.md)
210
+ - [Architecture decisions](https://github.com/leo91000/sheetom/blob/main/docs/adr/README.md)
211
+ - [Security policy](./SECURITY.md)
151
212
 
152
213
  ## License
153
214
 
154
- MIT
215
+ MIT. Vendored parser sources retain their upstream licenses and provenance.