@osdlc/yaml-splice 0.1.4 → 0.2.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/CHANGELOG.md +9 -0
- package/README.md +15 -3
- package/dist/index.d.ts +6 -5
- package/dist/index.js +34 -22
- package/dist/wasm/yaml_splice_wasm_bg.wasm +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2f3fc7c: A malformed edit is now refused as the new `EditErrorCode` `InvalidEdit`, and an error that is not a refusal is passed through instead of being reported as `InvalidResult`.
|
|
8
|
+
|
|
9
|
+
- `InvalidEdit`: a `Set` with no `value` (or `value: undefined`), an unknown tag, or a `Set` value nested more than 124 levels deep (the JSON decoder's recursion limit). These were reported as `InvalidResult`, which says the edit would damage the document, when the edit itself was bad.
|
|
10
|
+
- **Breaking:** `editYaml` rethrows any error without a known code unchanged. Before, it wrapped it in `EditError('InvalidResult', …)`. A Rust panic in the WebAssembly module (`RuntimeError: unreachable`) now reaches the caller as a `RuntimeError`, so a crashed module no longer looks like a normal refusal. Code that catches only `EditError` now sees these errors propagate.
|
|
11
|
+
|
|
3
12
|
## 0.1.4
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ ESM only. Node.js 22 or later, or Bun.
|
|
|
40
40
|
## Quick start
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
+
import assert from 'node:assert/strict'
|
|
44
|
+
|
|
43
45
|
import { editYaml } from '@osdlc/yaml-splice'
|
|
44
46
|
|
|
45
47
|
const source = new TextEncoder().encode("title: 'Draft' # shown in the header\nstatus: draft\n")
|
|
@@ -49,8 +51,8 @@ const out = editYaml(source, [
|
|
|
49
51
|
{ Rename: { from: 'title', to: 'name' } },
|
|
50
52
|
])
|
|
51
53
|
|
|
52
|
-
new TextDecoder().decode(out.bytes)
|
|
53
|
-
out.changed
|
|
54
|
+
assert.equal(new TextDecoder().decode(out.bytes), "name: 'Draft' # shown in the header\nstatus: done\n")
|
|
55
|
+
assert.equal(out.changed, true)
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
More in `examples/`: setting values, removing and renaming in one batch, and
|
|
@@ -70,10 +72,13 @@ handling refusals.
|
|
|
70
72
|
- `EditError` is the refusal. Its `code` (an `EditErrorCode`) says why, and
|
|
71
73
|
`name` is the same code. `span`, an `EditSpan`, is the byte range
|
|
72
74
|
`[start, end)` in the input that the refusal is about, when there is one.
|
|
73
|
-
- `EditErrorCode` is one of `InvalidUtf8`, `MalformedYaml`,
|
|
75
|
+
- `EditErrorCode` is one of `InvalidEdit`, `InvalidUtf8`, `MalformedYaml`,
|
|
74
76
|
`UnsupportedDocument`, `RootNotMapping`, `AmbiguousKey`, `MissingKey`,
|
|
75
77
|
`KeyConflict`, `UnrenderableValue` and `InvalidResult`. A minor release may
|
|
76
78
|
add a code, so give a `switch` over it a `default` branch.
|
|
79
|
+
- Any other error `editYaml` throws is not a refusal and is passed through
|
|
80
|
+
as is. A `RuntimeError` from the WebAssembly module means it crashed; do
|
|
81
|
+
not keep calling it as if the edit had been refused.
|
|
77
82
|
- `EditSpan` is `{ start, end }`, in bytes. `RootNotMapping`, `AmbiguousKey`
|
|
78
83
|
and `KeyConflict` always carry one; `MalformedYaml` and
|
|
79
84
|
`UnsupportedDocument` carry one when the parser knows the location.
|
|
@@ -94,6 +99,11 @@ Each export has TSDoc in the source. Your editor shows it.
|
|
|
94
99
|
byte zero.
|
|
95
100
|
- `NaN` and `±Infinity` anywhere in a `Set` value are refused as
|
|
96
101
|
`UnrenderableValue` before any byte is written.
|
|
102
|
+
- An edit the editor cannot read is refused as `InvalidEdit`: a `Set` with
|
|
103
|
+
no `value` (or `value: undefined`), an unknown tag, or a `Set` value
|
|
104
|
+
nested more than 124 levels deep. The last is the JSON decoder's recursion
|
|
105
|
+
limit (serde_json's 128 levels, less the levels the edit list itself
|
|
106
|
+
takes).
|
|
97
107
|
- Each edit is checked against what it asked for: the addressed key must read
|
|
98
108
|
back as set, removed or renamed, and no other key may change value or
|
|
99
109
|
order. If not, the batch is refused as `InvalidResult` instead of returning
|
|
@@ -138,6 +148,8 @@ The experimental exports:
|
|
|
138
148
|
| An edit that changes no value returns the input bytes | `tests/index.test.ts`, "a semantic no-op …" |
|
|
139
149
|
| Non-finite numbers are refused before any write | `tests/index.test.ts`, "a non-finite number in a Set value" |
|
|
140
150
|
| `EditErrorCode` lists every refusal the crate has | `tests/index.test.ts`, "EditErrorCode" |
|
|
151
|
+
| A malformed or too-deeply nested edit is `InvalidEdit` | `tests/index.test.ts`, "an edit the editor cannot read" |
|
|
152
|
+
| An error that is not a refusal is passed through | `tests/index.test.ts`, "an error that is not a refusal" |
|
|
141
153
|
|
|
142
154
|
## Runtimes and platforms
|
|
143
155
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,11 +35,12 @@ export interface EditOutcome {
|
|
|
35
35
|
readonly changed: boolean;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* Why an edit was refused: one code per variant of the crate's `EditError
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* Why an edit was refused: one code per variant of the crate's `EditError`,
|
|
39
|
+
* plus `InvalidEdit` for an edit the editor could not read: a missing `value`,
|
|
40
|
+
* an unknown tag, or a `Set` value nested more than 124 levels deep. A minor
|
|
41
|
+
* release may add a code, so a `switch` over it needs a `default` branch.
|
|
41
42
|
*/
|
|
42
|
-
export type EditErrorCode = 'InvalidUtf8' | 'MalformedYaml' | 'UnsupportedDocument' | 'RootNotMapping' | 'AmbiguousKey' | 'MissingKey' | 'KeyConflict' | 'UnrenderableValue' | 'InvalidResult';
|
|
43
|
+
export type EditErrorCode = 'InvalidEdit' | 'InvalidUtf8' | 'MalformedYaml' | 'UnsupportedDocument' | 'RootNotMapping' | 'AmbiguousKey' | 'MissingKey' | 'KeyConflict' | 'UnrenderableValue' | 'InvalidResult';
|
|
43
44
|
/**
|
|
44
45
|
* A byte span `[start, end)` into the input. `RootNotMapping`, `AmbiguousKey`
|
|
45
46
|
* and `KeyConflict` always carry one; `MalformedYaml` and `UnsupportedDocument`
|
|
@@ -50,7 +51,7 @@ export interface EditSpan {
|
|
|
50
51
|
readonly end: number;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
* A typed
|
|
54
|
+
* A typed refusal of an edit batch. `name`/`code` is the variant
|
|
54
55
|
* and `span` carries the byte range where one is known. Thrown by
|
|
55
56
|
* {@link editYaml} in place of the raw bindgen error.
|
|
56
57
|
*/
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { editYaml as editYamlWasm, } from './wasm/yaml_splice_wasm.js';
|
|
16
16
|
/**
|
|
17
|
-
* A typed
|
|
17
|
+
* A typed refusal of an edit batch. `name`/`code` is the variant
|
|
18
18
|
* and `span` carries the byte range where one is known. Thrown by
|
|
19
19
|
* {@link editYaml} in place of the raw bindgen error.
|
|
20
20
|
*/
|
|
@@ -34,17 +34,20 @@ export class EditError extends Error {
|
|
|
34
34
|
this.span = span;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// Every code, so the type check fails when a code is missing here.
|
|
38
|
+
const ERROR_CODE_TABLE = {
|
|
39
|
+
InvalidEdit: true,
|
|
40
|
+
InvalidUtf8: true,
|
|
41
|
+
MalformedYaml: true,
|
|
42
|
+
UnsupportedDocument: true,
|
|
43
|
+
RootNotMapping: true,
|
|
44
|
+
AmbiguousKey: true,
|
|
45
|
+
MissingKey: true,
|
|
46
|
+
KeyConflict: true,
|
|
47
|
+
UnrenderableValue: true,
|
|
48
|
+
InvalidResult: true,
|
|
49
|
+
};
|
|
50
|
+
const ERROR_CODES = new Set(Object.keys(ERROR_CODE_TABLE));
|
|
48
51
|
/**
|
|
49
52
|
* Edit `source` (raw UTF-8 YAML bytes) with an ordered batch of `edits`,
|
|
50
53
|
* returning new bytes plus a `changed` flag, or throwing a typed
|
|
@@ -67,7 +70,7 @@ export function editYaml(source, edits) {
|
|
|
67
70
|
result = editYamlWasm(source, editsJson);
|
|
68
71
|
}
|
|
69
72
|
catch (raw) {
|
|
70
|
-
throw toEditError(raw);
|
|
73
|
+
throw isRefusal(raw) ? toEditError(raw) : raw;
|
|
71
74
|
}
|
|
72
75
|
// Read both fields into a plain object, THEN free the handle. The glue's
|
|
73
76
|
// `bytes` getter returns an owned copy (`.slice()`), so it survives `free()`.
|
|
@@ -106,16 +109,25 @@ function assertFiniteValue(value, key) {
|
|
|
106
109
|
assertFiniteValue(item, key);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* The `.name` the wasm gives a crate `EditError` variant it has no name for
|
|
114
|
+
* (the crate's enum is `#[non_exhaustive]`). Still a refusal: reported as
|
|
115
|
+
* `InvalidResult`, as before 0.2.0.
|
|
116
|
+
*/
|
|
117
|
+
const UNNAMED_REFUSAL = 'EditError';
|
|
118
|
+
/**
|
|
119
|
+
* Whether a thrown value is one of the wasm side's refusals: an `Error` whose
|
|
120
|
+
* `name` is a known code, or {@link UNNAMED_REFUSAL}. Anything else, such as a
|
|
121
|
+
* Rust panic (`RuntimeError: unreachable`), is rethrown as is: it means the
|
|
122
|
+
* module is broken, not that the edit was refused.
|
|
123
|
+
*/
|
|
124
|
+
function isRefusal(raw) {
|
|
125
|
+
return raw instanceof Error && (ERROR_CODES.has(raw.name) || raw.name === UNNAMED_REFUSAL);
|
|
126
|
+
}
|
|
127
|
+
/** Map a bindgen refusal (`.name` = code, `.span`) to `EditError`. */
|
|
110
128
|
function toEditError(raw) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
? raw.name
|
|
114
|
-
: 'InvalidResult';
|
|
115
|
-
const span = readSpan(raw.span);
|
|
116
|
-
return new EditError(code, raw.message, span);
|
|
117
|
-
}
|
|
118
|
-
return new EditError('InvalidResult', String(raw));
|
|
129
|
+
const code = ERROR_CODES.has(raw.name) ? raw.name : 'InvalidResult';
|
|
130
|
+
return new EditError(code, raw.message, readSpan(raw.span));
|
|
119
131
|
}
|
|
120
132
|
function readSpan(span) {
|
|
121
133
|
if (span !== null &&
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osdlc/yaml-splice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Edit top-level fields of a YAML mapping in place, on bytes: set, remove or rename a field, and every byte outside the edited span stays as it was, including comments, key order, quoting, a BOM and line endings. The yaml-splice Rust crate, built to WebAssembly. Node.js and Bun.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"author": "sksizer",
|