diff --git a/.github/vitest.browserstack.config.js b/.github/vitest.browserstack.config.js index 8c35ad4c..f8daf185 100644 --- a/.github/vitest.browserstack.config.js +++ b/.github/vitest.browserstack.config.js @@ -25,15 +25,11 @@ const instances = env.BROWSERS.split(' ').map(name => { export default defineConfig({ test: { - alias: [ - { - find: /^yaml/, - customResolver(path) { - const name = path.split('/')[1] ?? 'index' - return resolve(import.meta.dirname, '..', 'dist', `${name}.js`) - } - } - ], + alias: { + 'yaml/cli': resolve(import.meta.dirname, '..', 'dist', 'cli.js'), + 'yaml/util': resolve(import.meta.dirname, '..', 'dist', 'util.js'), + yaml: resolve(import.meta.dirname, '..', 'dist', 'index.js') + }, api: { host: '0.0.0.0' }, browser: { enabled: true, @@ -42,6 +38,7 @@ export default defineConfig({ instances }, globals: true, + setupFiles: ['tests/_setup.ts'], include: ['tests/**/*.{js,ts}'], exclude: [ 'tests/_*', diff --git a/.github/workflows/browsers.yml b/.github/workflows/browsers.yml index cb462b4b..6c574617 100644 --- a/.github/workflows/browsers.yml +++ b/.github/workflows/browsers.yml @@ -26,7 +26,7 @@ jobs: - run: npm ci - run: npm run build - - run: npm install --no-save @vitest/browser-playwright@4 + - run: npm install --no-save @vitest/browser-playwright@4.1.4 - run: npx vitest run --config=.github/vitest.browserstack.config.js env: BROWSERS: chrome:93 chrome:latest firefox:latest @@ -55,7 +55,7 @@ jobs: - run: npm ci - run: npm run build - - run: npm install --no-save @vitest/browser-playwright@4 + - run: npm install --no-save @vitest/browser-playwright@4.1.4 - run: npx vitest run --config=.github/vitest.browserstack.config.js env: BROWSERS: firefox:94 diff --git a/docs/03_options.md b/docs/03_options.md index 625c709a..297e0499 100644 --- a/docs/03_options.md +++ b/docs/03_options.md @@ -22,15 +22,14 @@ Parse options affect the parsing and composition of a YAML Document from it sour Used by: `parse()`, `parseDocument()`, `parseAllDocuments()`, `new Composer()`, and `new Document()` -| Name | Type | Default | Description | -| ---------------- | ----------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| intAsBigInt | `boolean` | `false` | Whether integers should be parsed into [BigInt] rather than `number` values. | -| keepSourceTokens | `boolean` | `false` | Include a `srcToken` value on each parsed `Node`, containing the [CST token](#working-with-cst-tokens) that was composed into this node. | -| lineCounter | `LineCounter` | | If set, newlines will be tracked, to allow for `lineCounter.linePos(offset)` to provide the `{ line, col }` positions within the input. | -| prettyErrors | `boolean` | `true` | Include line/col position in errors, along with an extract of the source string. | -| strict | `boolean` | `true` | When parsing, do not ignore errors [required](#silencing-errors-and-warnings) by the YAML 1.2 spec, but caused by unambiguous content. | -| stringKeys | `boolean` | `false` | Parse all mapping keys as strings. Treat all non-scalar keys as errors. | -| uniqueKeys | `boolean ⎮ (a, b) => boolean` | `true` | Whether key uniqueness is checked, or customised. If set to be a function, it will be passed two parsed nodes and should return a boolean value indicating their equality. | +| Name | Type | Default | Description | +| ---------------- | ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| intAsBigInt | `boolean` | `false` | Whether integers should be parsed into [BigInt] rather than `number` values. | +| keepSourceTokens | `boolean` | `false` | Include a `srcToken` value on each parsed `Node`, containing the [CST token](#working-with-cst-tokens) that was composed into this node. | +| lineCounter | `LineCounter` | | If set, newlines will be tracked, to allow for `lineCounter.linePos(offset)` to provide the `{ line, col }` positions within the input. | +| prettyErrors | `boolean` | `true` | Include line/col position in errors, along with an extract of the source string. | +| strict | `boolean` | `true` | When parsing, do not ignore errors [required](#silencing-errors-and-warnings) by the YAML 1.2 spec, but caused by unambiguous content. | +| stringKeys | `boolean` | `false` | Parse all mapping keys as strings. Treat all non-scalar keys as errors. | [bigint]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/BigInt @@ -72,15 +71,15 @@ The `!!value` and `!!yaml` types are not supported. Used by: `parse()`, `parseDocument()`, `parseAllDocuments()`, `stringify()`, `new Composer()`, `new Document()`, and `doc.setSchema()` -| Name | Type | Default | Description | -| ---------------- | ------------------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| compat | `string ⎮ Tag[] ⎮ null` | `null` | When parsing, warn about compatibility issues with the given schema. When stringifying, use scalar styles that are parsed correctly by the `compat` schema as well as the actual schema. | -| customTags | `Tag[] ⎮ function` | | Array of [additional tags](#custom-data-types) to include in the schema | -| merge | `boolean` | 1.1: `true` 1.2: `false` | Enable support for `<<` merge keys. Default value depends on YAML version. | -| resolveKnownTags | `boolean` | `true` | When using the `'core'` schema, support parsing values with these explicit [YAML 1.1 tags]: `!!binary`, `!!omap`, `!!pairs`, `!!set`, `!!timestamp`. By default `true`. | -| schema | `string ⎮ Schema` | 1.1: `'yaml-1.1'` 1.2: `'core'` | The base schema to use. Default value depends on YAML version. Built-in support is provided for `'core'`, `'failsafe'`, `'json'`, and `'yaml-1.1'`. If using another string value, `customTags` must be an array of tags. | -| sortMapEntries | `boolean ⎮` `(a, b: Pair) => number` | `false` | When stringifying, sort map entries. If `true`, sort by comparing key values using the native less-than `<` operator. | -| toStringDefaults | `ToStringOptions` | | Override default values for `toString()` options. | +| Name | Type | Default | Description | +| ---------------- | ----------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| compat | `string ⎮ Tag[] ⎮ null` | `null` | When parsing, warn about compatibility issues with the given schema. When stringifying, use scalar styles that are parsed correctly by the `compat` schema as well as the actual schema. | +| customTags | `Tag[] ⎮ function` | | Array of [additional tags](#custom-data-types) to include in the schema | +| mapKey | `(key: any) => any` | | Determine an internal key representation for map and set values, which is used for detecting duplicates and to identify values. | +| merge | `boolean` | 1.1: `true` 1.2: `false` | Enable support for `<<` merge keys. Default value depends on YAML version. | +| resolveKnownTags | `boolean` | `true` | When using the `'core'` schema, support parsing values with these explicit [YAML 1.1 tags]: `!!binary`, `!!omap`, `!!pairs`, `!!set`, `!!timestamp`. By default `true`. | +| schema | `string ⎮ Schema` | 1.1: `'yaml-1.1'` 1.2: `'core'` | The base schema to use. Default value depends on YAML version. Built-in support is provided for `'core'`, `'failsafe'`, `'json'`, and `'yaml-1.1'`. If using another string value, `customTags` must be an array of tags. | +| toStringDefaults | `ToStringOptions` | | Override default values for `toString()` options. | [yaml 1.1 tags]: https://yaml.org/type/ @@ -166,5 +165,6 @@ Used by: `stringify()` and `doc.toString()` | nullStr | `string` | `'null'` | String representation for `null` values. | | simpleKeys | `boolean` | `false` | Require keys to be scalars and always use implicit rather than explicit notation. | | singleQuote | `boolean ⎮ null` | `null` | Use 'single quote' rather than "double quote" where applicable. Set to `false` to disable single quotes completely. | +| sortMapEntries | `boolean ⎮` `(a, b: Pair) => number` | `false` | Sort map and set entries. If `true`, sort by comparing key values using the native less-than `<` operator. | | trailingComma | `boolean` | `false` | Add a trailing comma after the last entry in a flow collection that's split across multiple lines. | | trueStr | `string` | `'true'` | String representation for `true` values. | diff --git a/docs/04_documents.md b/docs/04_documents.md index 2332e41c..ed04ce30 100644 --- a/docs/04_documents.md +++ b/docs/04_documents.md @@ -11,33 +11,27 @@ import { parseAllDocuments, parseDocument } from 'yaml' const file = fs.readFileSync('./file.yml', 'utf8') const doc = parseDocument(file) doc.value + +// Eliding the .range, .source, and .type attributes: +// // YAMLMap { -// items: -// [ Pair { -// key: Scalar { value: 'YAML', range: [ 0, 4, 4 ] }, -// value: -// YAMLSeq { -// items: -// [ Scalar { -// value: 'A human-readable data serialization language', -// range: [ 10, 54, 55 ] }, -// Scalar { -// value: 'https://en.wikipedia.org/wiki/YAML', -// range: [ 59, 93, 94 ] } ], -// range: [ 8, 94, 94 ] } }, -// Pair { -// key: Scalar { value: 'yaml', range: [ 94, 98, 98 ] }, -// value: -// YAMLSeq { -// items: -// [ Scalar { -// value: 'A complete JavaScript implementation', -// range: [ 104, 140, 141 ] }, -// Scalar { -// value: 'https://www.npmjs.com/package/yaml', -// range: [ 145, 180, 180 ] } ], -// range: [ 102, 180, 180 ] } } ], -// range: [ 0, 180, 180 ] } +// values: Map(2) { +// 'YAML' => Pair { +// key: Scalar { value: 'YAML' }, +// value: YAMLSeq(2) [ +// Scalar { value: 'A human-readable data serialization language' }, +// Scalar { value: 'https://en.wikipedia.org/wiki/YAML' } +// ] +// }, +// 'yaml' => Pair { +// key: Scalar { value: 'yaml' }, +// value: YAMLSeq(2) [ +// Scalar { value: 'A complete JavaScript implementation' }, +// Scalar { value: 'https://www.npmjs.com/package/yaml' } +// ] +// } +// } +// } ``` These functions should never throw, @@ -70,7 +64,8 @@ See [Options](#options) for more information on the second parameter. Creates a new document. The document `value` is initialised with `value`, wrapped recursively in appropriate [content nodes](#content-nodes). -If defined, a `replacer` may filter or modify the initial document value, following the same algorithm as the [JSON implementation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter). +If defined, a `replacer` may filter or modify the initial document value, +following the same algorithm as the [JSON implementation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter). See [Options](#options) for more information on the last argument. | Member | Type | Description | @@ -103,30 +98,32 @@ which is expected to always contain a `YAMLMap`, `YAMLSeq`, or `Scalar` value. ## Document Methods -| Method | Returns | Description | -| ------------------------------------------ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| clone() | `Document` | Create a deep copy of this Document and its value. Custom Node values that inherit from `Object` still refer to their original instances. | -| createAlias(node: Node, name?: string) | `Alias` | Create a new `Alias` node, adding the required anchor for `node`. If `name` is empty, a new anchor name will be generated. | -| createNode(value, options?) | `Node` | Recursively wrap any input with appropriate `Node` containers. See [Creating Nodes](#creating-nodes) for more information. | -| createPair(key, value, options?) | `Pair` | Recursively wrap `key` and `value` into a `Pair` object. See [Creating Nodes](#creating-nodes) for more information. | -| setSchema(version, options?) | `void` | Change the YAML version and schema used by the document. `version` must be either `'1.1'` or `'1.2'`; accepts all Schema options. | -| toJS(options?) | `any` | A plain JavaScript representation of the document `value`. | -| toJSON() | `any` | A JSON representation of the document `value`. | -| toString(options?) | `string` | A YAML representation of the document. | +| Method | Returns | Description | +| ------------------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| clone() | `Document` | Create a deep copy of this Document and its value. Custom Node values that inherit from `Object` still refer to their original instances. | +| createAlias(node: Node, name?: string) | `Alias` | Create a new `Alias` node, adding the required anchor for `node`. If `name` is empty, a new anchor name will be generated. | +| createNode(value, options?) | `Node` | Recursively wrap any input with appropriate `Node` containers. See [Creating Nodes](#creating-nodes) for more information. | +| createPair(key, value, options?) | `Pair` | Recursively wrap `key` and `value` into a `Pair` object. See [Creating Nodes](#creating-nodes) for more information. | +| get(key) | `Node ⎮ Pair` | Returns top-level collection item at `key`, or `undefined` if not found. | +| getPair(key) | `Pair` | Returns top-level collection pair at `key`, or `undefined` if not found. | +| set(key) | `void` | Sets a value in this document's top-level collection. For `!!set`, `value` is ignored. | +| setSchema(version, options?) | `void` | Change the YAML version and schema used by the document. `version` must be either `'1.1'` or `'1.2'`; accepts all Schema options. | +| toJS(options?) | `any` | A plain JavaScript representation of the document `value`. | +| toJSON() | `any` | A JSON representation of the document `value`. | +| toString(options?) | `string` | A YAML representation of the document. | ```js const doc = parseDocument('a: 1\nb: [2, 3]\n') -doc.get('a') // 1 -doc.getIn([]) // YAMLMap { items: [Pair, Pair], ... } -doc.hasIn(['b', 0]) // true -doc.addIn(['b'], 4) // -> doc.get('b').items.length === 3 -doc.deleteIn(['b', 1]) // true -doc.getIn(['b', 1]) // 4 +doc.get('a') // Scalar { value: 1 } +doc.getPair('a') // Pair { key: Scalar { value: 'a' }, value: Scalar { value: 1 } } +doc.get('b')[0] // Scalar { value: 2 } +doc.get('b').push(4) // 3 +doc.get('b').splice(1, 1) // YAMLSeq(1) [ Scalar { value: 3 } ] +doc.get('b').at(1) // 4 ``` -In addition to the above, the document object also provides the same **accessor methods** as [collections](#collections), based on the top-level collection: -`add`, `delete`, `get`, `has`, and `set`, along with their deeper variants `addIn`, `deleteIn`, `getIn`, `hasIn`, and `setIn`. -For the `*In` methods using an empty `path` value (i.e. `[]`) will refer to the document's top-level `value`. +In addition to the above, the document object also provides the following **accessor methods**: +`delete`, `get`, `has`, and `set`. #### `Document#toJS()`, `Document#toJSON()` and `Document#toString()` diff --git a/docs/05_content_nodes.md b/docs/05_content_nodes.md index ddcb8db2..c598123a 100644 --- a/docs/05_content_nodes.md +++ b/docs/05_content_nodes.md @@ -54,88 +54,88 @@ class Pair { value: Node | null } -class Collection implements NodeBase { - anchor?: string // an anchor associated with this node - flow?: boolean // use flow style when stringifying this - schema?: Schema - addIn(path: unknown[], value: unknown): void +interface CollectionBase extends NodeBase { + anchor?: string // an anchor associated with this node clone(schema?: Schema): this // a deep copy of this collection - deleteIn(path: unknown[]): boolean - getIn(path: unknown[], keepScalar?: boolean): unknown - hasIn(path: unknown[]): boolean - setIn(path: unknown[], value: unknown): void + flow?: boolean // use flow style when stringifying this + schema: Schema + readonly size: number // the number of items in this collection } -class YAMLMap extends Collection { - items: Pair[] - add(pair: Pair | { key: K; value: V }, overwrite?: boolean): void - delete(key: K): boolean - get(key: K, keepScalar?: boolean): unknown - has(key: K): boolean - set(key: K, value: V): void +class YAMLMap implements CollectionBase { + constructor(schema: Schema, elements?: Pair[]) + delete(key: K | NodeOf): boolean + get(key: K | NodeOf): NodeOf + getPair(key: K | NodeOf): Pair + has(key: K | NodeOf): boolean + set(pair: Pair): void + set(key: K | NodeOf, value?: V | NodeOf | null): void + values: Map> } -class YAMLSeq extends Collection { - items: T[] - add(value: T): void - delete(key: number | Scalar): boolean - get(key: number | Scalar, keepScalar?: boolean): unknown - has(key: number | Scalar): boolean - set(key: number | Scalar, value: T): void +class YAMLSeq extends Array> implements CollectionBase { + constructor(schema: Schema, elements?: (T | NodeOf)[]) + set(idx: number, value: T | NodeOf): void } ``` Within all YAML documents, two forms of collections are supported: sequential `YAMLSeq` collections and key-value `YAMLMap` collections. -The JavaScript representations of these collections both have an `items` array, which may (`YAMLSeq`) or must (`YAMLMap`) consist of `Pair` objects that contain a `key` and a `value` of any node type, or `null` for `value`. -The `items` array of a `YAMLSeq` object may contain any node values. + +`YAMLSeq` extends `Array`, overriding its `.fill`, `.push`, `.splice`, and `.unshift` methods to wrap all inserted values as `Node` values. +Indexed access (as in `seq[2]`) is supported, but setting values with it will not wrap values as expected. +To set values, a `.set` method is provided. + +| YAMLSeq Method | Returns | Description | +| --------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| set(idx, value) | `this` | Sets a value in this sequence. `idx` must be an integer. With a non-Node `value`, `doc.createNode(value)` will be called internally. When overwriting a `Scalar` value with a scalar, the original node is retained. | + +`YAMLMap` represents mapping values using an internal `.values` Map, with `Pair` instances as values. +For all of the following YAMLMap methods, `key` may be a Pair, a Node, +or a bare scalar value (i.e. `42` will match `Scalar { value: 42 }`). +To customize key (and thereby equality) handling, use the `mapKey` [Schema option](#schema-options). + +| YAMLMap Method | Returns | Description | +| --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| delete(key) | `boolean` | Removes a value from the map. Returns `true` if the item was found and removed. | +| get(key) | `Node` | Returns value at `key`, or `undefined` if not found. | +| getPair(key) | `Pair` | Returns pair at `key`, or `undefined` if not found. | +| has(key) | `boolean` | Checks if the map includes a value with the key `key`. | +| set(key, value) | `this` | Sets a value in this map. With a non-Node `value`, `doc.createNode(value)` will be called internally. When overwriting a `Scalar` value with a scalar, the original node is retained. | When stringifying collections, by default block notation will be used. Flow notation will be selected if `flow` is `true`, the collection is within a surrounding flow collection, or if the collection is in an implicit key. -The `yaml-1.1` schema includes [additional collections](https://yaml.org/type/index.html) that are based on `YAMLMap` and `YAMLSeq`: `OMap` and `Pairs` are sequences of `Pair` objects (`OMap` requires unique keys & corresponds to the JS Map object), and `Set` is a map of keys with null values that corresponds to the JS Set object. +The `yaml-1.1` schema includes [additional collections](https://yaml.org/type/index.html) that are based on `YAMLMap` and `YAMLSeq`: +`OMap` and `Pairs` are `YAMLSeq` instances (`OMap` requires unique keys & corresponds to the JS Map object), while +`YAMLSet` is a separately implemented collection with an internal `.values` Map, in JS corresponding to the Set object. -All of the collections provide the following accessor methods: +For all of the following YAMLSet methods, `value` may be a Node +or a bare scalar value (i.e. `42` will match `Scalar { value: 42 }`). +To customize key (and thereby equality) handling, use the `mapKey` [Schema option](#schema-options). -| Method | Returns | Description | -| ----------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| add(value), addIn(path, value) | `void` | Adds a value to the collection. For `!!map` and `!!omap` the value must be a Pair instance or a `{ key, value }` object, which may not have a key that already exists in the map. | -| delete(key), deleteIn(path) | `boolean` | Removes a value from the collection. Returns `true` if the item was found and removed. | -| get(key, [keep]), getIn(path, [keep]) | `any` | Returns value at `key`, or `undefined` if not found. By default unwraps scalar values from their surrounding node; to disable set `keep` to `true` (collections are always returned intact). | -| has(key), hasIn(path) | `boolean` | Checks if the collection includes a value with the key `key`. | -| set(key, value), setIn(path, value) | `any` | Sets a value in this collection. For `!!set`, `value` needs to be a boolean to add/remove the item from the set. When overwriting a `Scalar` value with a scalar, the original node is retained. | +| YAMLSet Method | Returns | Description | +| -------------- | --------- | ------------------------------------------------------------------------------- | +| add(value) | `any` | Sets a value in this set. | +| delete(value) | `boolean` | Removes a value from the set. Returns `true` if the item was found and removed. | +| get(value) | `Node` | Returns value matching `value`, or `undefined` if not found. | +| has(value) | `boolean` | Checks if the set includes `value`. | ```js const doc = new YAML.Document({ a: 1, b: [2, 3] }) // { a: 1, b: [ 2, 3 ] } -doc.add({ key: 'c', value: 4 }) // { a: 1, b: [ 2, 3 ], c: 4 } -doc.addIn(['b'], 5) // { a: 1, b: [ 2, 3, 5 ], c: 4 } -doc.set('c', 42) // { a: 1, b: [ 2, 3, 5 ], c: 42 } -doc.setIn(['c', 'x']) // Error: Expected YAML collection at c. Remaining path: x -doc.delete('c') // { a: 1, b: [ 2, 3, 5 ] } -doc.deleteIn(['b', 1]) // { a: 1, b: [ 2, 5 ] } - -doc.get('a') // 1 -doc.get('a', true) // Scalar { value: 1 } -doc.getIn(['b', 1]) // 5 -doc.has(doc.createNode('a')) // true -doc.has('c') // false -doc.hasIn(['b', '0']) // true +doc.set('c', 4) // { a: 1, b: [ 2, 3 ], c: 4 } +doc.get('b').push(5) // { a: 1, b: [ 2, 3, 5 ], c: 4 } +doc.set('c', 42) // { a: 1, b: [ 2, 3, 5 ], c: 42 } +doc.get('c').set('x') // TypeError: doc.get(...).set is not a function +doc.value.delete('c') // { a: 1, b: [ 2, 3, 5 ] } +doc.get('b').splice(1, 1) // { a: 1, b: [ 2, 5 ] } + +doc.get('a') // Scalar { value: 1 } +doc.get(doc.createNode('a')) // Scalar { value: 1 } +doc.get('b')[1] // Scalar { value: 5 } +doc.get('c') // undefined ``` -For all of these methods, the keys may be nodes or their wrapped scalar values (i.e. `42` will match `Scalar { value: 42 }`). -Keys for `!!seq` should be positive integers, or their string representations. -`add()` and `set()` do not automatically call `doc.createNode()` to wrap the value. - -Each of the methods also has a variant that requires an iterable as the first parameter, and allows fetching or modifying deeper collections. -If any intermediate node in `path` is a scalar rather than a collection, an error will be thrown. -If any of the intermediate collections is not found: - -- `getIn` and `hasIn` will return `undefined` or `false` (respectively) -- `addIn` and `setIn` will create missing collections; non-negative integer keys will create sequences, all other keys create maps -- `deleteIn` will throw an error - -Note that for `addIn` the path argument points to the collection rather than the item; for maps its `value` should be a `Pair` or an object with `{ key, value }` fields. - ## Alias Nodes @@ -181,8 +181,8 @@ const map = doc.createNode({ balloons: 99 }) // key: Scalar { value: 'balloons' }, // value: Scalar { value: 99 } } ] } -doc.add(map) -doc.get(0, true).comment = ' A commented item' +doc.value.push(map) +doc.get(0).comment = ' A commented item' String(doc) // - some # A commented item // - values @@ -213,8 +213,8 @@ To that end, you'll need to assign its return value to the `value` of a document

doc.createAlias(node, name?): Alias

```js -const alias = doc.createAlias(doc.get(1, true), 'foo') -doc.add(alias) +const alias = doc.createAlias(doc.get(1), 'foo') +doc.value.push(alias) String(doc) // - some # A commented item // - &foo values @@ -239,7 +239,7 @@ const doc = new Document([ 42, { including: 'objects', 3: 'a string' } ]) -doc.add(doc.createPair(1, 'a number')) +doc.value.push(doc.createPair(1, 'a number')) doc.toString() // - some values @@ -266,12 +266,12 @@ const doc = YAML.parseDocument(` - 1: a number `) -const obs = doc.getIn([2, 'including'], true) +const obs = doc.get(2).get('including') obs.type = 'QUOTE_DOUBLE' YAML.visit(doc, { Pair(_, pair) { - if (pair.key && pair.key.value === '3') return YAML.visit.REMOVE + if (pair.key?.value === '3') return YAML.visit.REMOVE }, Scalar(key, node) { if ( @@ -292,7 +292,7 @@ String(doc) ``` In general, it's safe to modify nodes manually, e.g. splicing the `items` array of a `YAMLMap` or setting its `flow` value to `true`. -For operations on nodes at a known location in the tree, it's probably easiest to use `doc.getIn(path, true)` to access them. +For operations on nodes at a known location in the tree, it's probably easiest to use `doc.get(...)` to access them. For more complex or general operations, a visitor API is provided: #### `YAML.visit(node, visitor): void` diff --git a/docs/06_custom_tags.md b/docs/06_custom_tags.md index 6b047f69..54f89485 100644 --- a/docs/06_custom_tags.md +++ b/docs/06_custom_tags.md @@ -110,6 +110,9 @@ const nullObject = { tag: '!nullobject', collection: 'map', nodeClass: YAMLNullObject, + createNode(nc, obj) { + return YAMLNullObject.create(nc, obj) + }, identify: v => !!(typeof v === 'object' && v && !Object.getPrototypeOf(v)) } @@ -147,18 +150,17 @@ class YAMLError extends YAMLMap { }) return Object.assign(er, rest) } - - static from(schema, obj, ctx) { - const { name, message, stack } = obj - // ensure these props remain, even if not enumerable - return super.from(schema, { ...obj, name, message, stack }, ctx) - } } const error = { tag: '!error', collection: 'map', nodeClass: YAMLError, + createNode(nc, obj) { + const { name, message, stack } = obj + // ensure these props remain, even if not enumerable + return YAMLNullObject.create(nc, { ...obj, name, message, stack }) + }, identify: v => !!(typeof v === 'object' && v && v instanceof Error) } @@ -240,7 +242,7 @@ import { To define your own tag, you'll need to define an object comprising of some of the following fields. Those in bold are required: -- `createNode(schema, value, ctx): Node` is an optional factory function, used e.g. by collections when wrapping JS objects as AST nodes. +- `createNode(nodeCreator, value): Node` is a factory function, required by collection tags for wrapping JS objects as AST nodes. - `format: string` If a tag has multiple forms that should be parsed and/or stringified differently, use `format` to identify them. Used by `!!int` and `!!float`. - **`identify(value): boolean`** is used by `doc.createNode()` to detect your data type, e.g. using `typeof` or `instanceof`. Required. - `nodeClass: Node` is the `Node` child class that implements this tag. Required for collections and tags that have overlapping JS representations. diff --git a/package-lock.json b/package-lock.json index 1a50bfb9..d882d7f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,14 +32,14 @@ } }, "node_modules/@babel/generator": { - "version": "8.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.1.tgz", - "integrity": "sha512-3ypWOOiC4AYHKr8vYRVtWtWmyvcoItHtVqF8paFax+ydpmUdPsJpLBkBBs5ItmhdrwC3a0ZSqqFAdzls4ODP3w==", + "version": "8.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.2.tgz", + "integrity": "sha512-oCQ1IKPwkzCeJzAPb7Fv8rQ9k5+1sG8mf2uoHiMInPYvkRfrDJxbTIbH51U+jstlkghus0vAi3EBvkfvEsYNLQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^8.0.0-rc.1", - "@babel/types": "^8.0.0-rc.1", + "@babel/parser": "^8.0.0-rc.2", + "@babel/types": "^8.0.0-rc.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "@types/jsesc": "^2.5.0", @@ -50,9 +50,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "8.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.1.tgz", - "integrity": "sha512-vi/pfmbrOtQmqgfboaBhaCU50G7mcySVu69VU8z+lYoPPB6WzI9VgV7WQfL908M4oeSH5fDkmoupIqoE0SdApw==", + "version": "8.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.3.tgz", + "integrity": "sha512-AmwWFx1m8G/a5cXkxLxTiWl+YEoWuoFLUCwqMlNuWO1tqAYITQAbCRPUkyBHv1VOFgfjVOqEj6L3u15J5ZCzTA==", "dev": true, "license": "MIT", "engines": { @@ -60,9 +60,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "8.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.1.tgz", - "integrity": "sha512-I4YnARytXC2RzkLNVnf5qFNFMzp679qZpmtw/V3Jt2uGnWiIxyJtaukjG7R8pSx8nG2NamICpGfljQsogj+FbQ==", + "version": "8.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.2.tgz", + "integrity": "sha512-xExUBkuXWJjVuIbO7z6q7/BA9bgfJDEhVL0ggrggLMbg0IzCUWGT1hZGE8qUH7Il7/RD/a6cZ3AAFrrlp1LF/A==", "dev": true, "license": "MIT", "engines": { @@ -70,13 +70,13 @@ } }, "node_modules/@babel/parser": { - "version": "8.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.1.tgz", - "integrity": "sha512-6HyyU5l1yK/7h9Ki52i5h6mDAx4qJdiLQO4FdCyJNoB/gy3T3GGJdhQzzbZgvgZCugYBvwtQiWRt94QKedHnkA==", + "version": "8.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.2.tgz", + "integrity": "sha512-29AhEtcq4x8Dp3T72qvUMZHx0OMXCj4Jy/TEReQa+KWLln524Cj1fWb3QFi0l/xSpptQBR6y9RNEXuxpFvwiUQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^8.0.0-rc.1" + "@babel/types": "^8.0.0-rc.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -86,35 +86,35 @@ } }, "node_modules/@babel/types": { - "version": "8.0.0-rc.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.1.tgz", - "integrity": "sha512-ubmJ6TShyaD69VE9DQrlXcdkvJbmwWPB8qYj0H2kaJi29O7vJT9ajSdBd2W8CG34pwL9pYA74fi7RHC1qbLoVQ==", + "version": "8.0.0-rc.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.2.tgz", + "integrity": "sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^8.0.0-rc.1", - "@babel/helper-validator-identifier": "^8.0.0-rc.1" + "@babel/helper-string-parser": "^8.0.0-rc.2", + "@babel/helper-validator-identifier": "^8.0.0-rc.2" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.1.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "dev": true, "license": "MIT", "optional": true, @@ -123,456 +123,14 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -618,15 +176,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -659,20 +217,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -683,9 +241,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -720,29 +278,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -811,26 +383,28 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@oxc-project/types": { - "version": "0.113.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.113.0.tgz", - "integrity": "sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==", + "version": "0.126.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", + "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", "dev": true, "license": "MIT", "funding": { @@ -838,9 +412,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==", "cpu": [ "arm64" ], @@ -855,9 +429,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==", "cpu": [ "arm64" ], @@ -872,9 +446,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.4.tgz", - "integrity": "sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==", "cpu": [ "x64" ], @@ -889,9 +463,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.4.tgz", - "integrity": "sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==", "cpu": [ "x64" ], @@ -906,9 +480,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.4.tgz", - "integrity": "sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.16.tgz", + "integrity": "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==", "cpu": [ "arm" ], @@ -923,9 +497,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.4.tgz", - "integrity": "sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==", "cpu": [ "arm64" ], @@ -940,9 +514,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.4.tgz", - "integrity": "sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==", "cpu": [ "arm64" ], @@ -956,371 +530,44 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.4.tgz", - "integrity": "sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.4.tgz", - "integrity": "sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.4.tgz", - "integrity": "sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.4.tgz", - "integrity": "sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.4.tgz", - "integrity": "sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz", - "integrity": "sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", - "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==", "cpu": [ - "s390x" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==", "cpu": [ - "x64" + "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==", "cpu": [ "x64" ], @@ -1329,12 +576,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==", "cpu": [ "x64" ], @@ -1342,13 +592,16 @@ "license": "MIT", "optional": true, "os": [ - "openbsd" - ] + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==", "cpu": [ "arm64" ], @@ -1357,40 +610,51 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.16.tgz", + "integrity": "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==", "cpu": [ - "arm64" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==", "cpu": [ "x64" ], @@ -1399,21 +663,17 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", - "cpu": [ - "x64" ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz", + "integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", @@ -1473,9 +733,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", - "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", "dev": true, "license": "MIT", "dependencies": { @@ -1483,20 +743,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", - "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/type-utils": "8.55.0", - "@typescript-eslint/utils": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1506,9 +766,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.55.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.58.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -1522,16 +782,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", - "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3" }, "engines": { @@ -1542,19 +802,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", - "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.55.0", - "@typescript-eslint/types": "^8.55.0", + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", "debug": "^4.4.3" }, "engines": { @@ -1565,18 +825,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", - "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0" + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1587,9 +847,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", - "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", "dev": true, "license": "MIT", "engines": { @@ -1600,21 +860,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", - "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1624,14 +884,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", "dev": true, "license": "MIT", "engines": { @@ -1643,21 +903,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", - "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.55.0", - "@typescript-eslint/tsconfig-utils": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1667,46 +927,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", - "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0" + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1716,19 +989,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", - "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.58.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1738,32 +1011,45 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", + "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@vitest/spy": "4.1.4", + "@vitest/utils": "4.1.4", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", + "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", + "@vitest/spy": "4.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1772,7 +1058,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -1784,26 +1070,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", + "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", + "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.4", "pathe": "^2.0.3" }, "funding": { @@ -1811,13 +1097,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", + "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.4", + "@vitest/utils": "4.1.4", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1826,9 +1113,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", + "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", "dev": true, "license": "MIT", "funding": { @@ -1836,23 +1123,24 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", + "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "tinyrainbow": "^3.0.3" + "@vitest/pretty-format": "4.1.4", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -1873,9 +1161,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -1958,9 +1246,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2032,6 +1320,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2072,6 +1367,16 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dts-resolver": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-2.1.3.tgz", @@ -2094,54 +1399,12 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2156,25 +1419,25 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", + "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -2193,7 +1456,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2446,9 +1709,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -2468,9 +1731,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", "dev": true, "license": "MIT", "dependencies": { @@ -2651,7 +1914,268 @@ "type-check": "~0.4.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/locate-path": { @@ -2688,9 +2212,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -2842,9 +2366,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -2855,9 +2379,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -2894,9 +2418,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -2957,14 +2481,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.4.tgz", - "integrity": "sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz", + "integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.113.0", - "@rolldown/pluginutils": "1.0.0-rc.4" + "@oxc-project/types": "=0.126.0", + "@rolldown/pluginutils": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" @@ -2973,36 +2497,38 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.4", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.4", - "@rolldown/binding-darwin-x64": "1.0.0-rc.4", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.4", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.4", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.4", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.4", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.4", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.4", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.4", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.4", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.4" + "@rolldown/binding-android-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-x64": "1.0.0-rc.16", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" } }, "node_modules/rolldown-plugin-dts": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.22.1.tgz", - "integrity": "sha512-5E0AiM5RSQhU6cjtkDFWH6laW4IrMu0j1Mo8x04Xo1ALHmaRMs9/7zej7P3RrryVHW/DdZAp85MA7Be55p0iUw==", + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.22.5.tgz", + "integrity": "sha512-M/HXfM4cboo+jONx9Z0X+CUf3B5tCi7ni+kR5fUW50Fp9AlZk0oVLesibGWgCXDKFp5lpgQ9yhKoImUFjl3VZw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/generator": "8.0.0-rc.1", - "@babel/helper-validator-identifier": "8.0.0-rc.1", - "@babel/parser": "8.0.0-rc.1", - "@babel/types": "8.0.0-rc.1", + "@babel/generator": "8.0.0-rc.2", + "@babel/helper-validator-identifier": "8.0.0-rc.2", + "@babel/parser": "8.0.0-rc.2", + "@babel/types": "8.0.0-rc.2", "ast-kit": "^3.0.0-beta.1", "birpc": "^4.0.0", "dts-resolver": "^2.1.3", - "get-tsconfig": "^4.13.1", + "get-tsconfig": "^4.13.6", "obug": "^2.1.1" }, "engines": { @@ -3015,7 +2541,7 @@ "@ts-macro/tsc": "^0.3.6", "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-rc.3", - "typescript": "^5.0.0", + "typescript": "^5.0.0 || ^6.0.0-beta", "vue-tsc": "~3.2.0" }, "peerDependenciesMeta": { @@ -3033,51 +2559,6 @@ } } }, - "node_modules/rollup": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", - "fsevents": "~2.3.2" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -3139,9 +2620,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -3179,9 +2660,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, "license": "MIT", "engines": { @@ -3189,14 +2670,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -3206,9 +2687,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -3216,9 +2697,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -3264,16 +2745,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.55.0.tgz", - "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.55.0", - "@typescript-eslint/parser": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0" + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3283,8 +2764,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/undici-types": { @@ -3305,17 +2786,16 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "bin": { @@ -3332,9 +2812,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -3347,13 +2828,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -3379,32 +2863,340 @@ } } }, + "node_modules/vite/node_modules/@oxc-project/types": { + "version": "0.124.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", + "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", + "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", + "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", + "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", + "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", + "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", + "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", + "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", + "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite/node_modules/rolldown": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", + "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", + "@rolldown/binding-darwin-x64": "1.0.0-rc.15", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + } + }, "node_modules/vitest": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", - "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", + "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.18", - "@vitest/mocker": "4.0.18", - "@vitest/pretty-format": "4.0.18", - "@vitest/runner": "4.0.18", - "@vitest/snapshot": "4.0.18", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.4", + "@vitest/mocker": "4.1.4", + "@vitest/pretty-format": "4.1.4", + "@vitest/runner": "4.1.4", + "@vitest/snapshot": "4.1.4", + "@vitest/spy": "4.1.4", + "@vitest/utils": "4.1.4", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -3420,12 +3212,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.18", - "@vitest/browser-preview": "4.0.18", - "@vitest/browser-webdriverio": "4.0.18", - "@vitest/ui": "4.0.18", + "@vitest/browser-playwright": "4.1.4", + "@vitest/browser-preview": "4.1.4", + "@vitest/browser-webdriverio": "4.1.4", + "@vitest/coverage-istanbul": "4.1.4", + "@vitest/coverage-v8": "4.1.4", + "@vitest/ui": "4.1.4", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -3446,6 +3241,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -3454,6 +3255,9 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, diff --git a/src/compose/compose-collection.ts b/src/compose/compose-collection.ts index eb1e7f83..80ff7fe8 100644 --- a/src/compose/compose-collection.ts +++ b/src/compose/compose-collection.ts @@ -1,8 +1,9 @@ import { isNode } from '../nodes/identity.ts' -import type { Node } from '../nodes/Node.ts' +import type { Node } from '../nodes/types.ts' import { Scalar } from '../nodes/Scalar.ts' import { YAMLMap } from '../nodes/YAMLMap.ts' import { YAMLSeq } from '../nodes/YAMLSeq.ts' +import type { YAMLSet } from '../nodes/YAMLSet.ts' import type { BlockMap, BlockSequence, @@ -31,7 +32,10 @@ function resolveCollection( ? resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection(CN, ctx, token, onError, tag) - const Coll = coll.constructor as typeof YAMLMap | typeof YAMLSeq + const Coll = coll.constructor as + | typeof YAMLMap + | typeof YAMLSeq + | typeof YAMLSet // If we got a tagName matching the class, or the tag name is '!', // then use the tagName from the node class used to create it. diff --git a/src/compose/compose-node.ts b/src/compose/compose-node.ts index 4b268aec..a092252b 100644 --- a/src/compose/compose-node.ts +++ b/src/compose/compose-node.ts @@ -1,6 +1,6 @@ import type { Directives } from '../doc/directives.ts' import { Alias } from '../nodes/Alias.ts' -import type { Node } from '../nodes/Node.ts' +import type { Node } from '../nodes/types.ts' import { Scalar } from '../nodes/Scalar.ts' import type { ParseOptions } from '../options.ts' import type { FlowScalar, SourceToken, Token } from '../parse/cst.ts' diff --git a/src/compose/composer.ts b/src/compose/composer.ts index 990a4846..1cef2851 100644 --- a/src/compose/composer.ts +++ b/src/compose/composer.ts @@ -2,8 +2,8 @@ import { Directives } from '../doc/directives.ts' import { Document, type DocValue } from '../doc/Document.ts' import type { ErrorCode } from '../errors.ts' import { YAMLParseError, YAMLWarning } from '../errors.ts' -import { Collection } from '../nodes/Collection.ts' -import type { Range } from '../nodes/Node.ts' +import { isCollection } from '../nodes/identity.ts' +import type { Range } from '../nodes/types.ts' import { Pair } from '../nodes/Pair.ts' import type { DocumentOptions, @@ -104,15 +104,14 @@ export class Composer< private decorate(doc: Document.Parsed, afterDoc: boolean) { const { comment, afterEmptyLine } = parsePrelude(this.prelude) - //console.log({ dc: doc.comment, prelude, comment }) if (comment) { const dc = doc.value if (afterDoc) { doc.comment = doc.comment ? `${doc.comment}\n${comment}` : comment } else if (afterEmptyLine || doc.directives.docStart) { doc.commentBefore = comment - } else if (dc instanceof Collection && !dc.flow && dc.items.length > 0) { - let it = dc.items[0] + } else if (isCollection(dc) && !dc.flow && dc.size > 0) { + let it = Array.isArray(dc) ? dc[0] : dc.values.values().next().value! if (it instanceof Pair) it = it.key const cb = it.commentBefore it.commentBefore = cb ? `${comment}\n${cb}` : comment diff --git a/src/compose/resolve-block-map.ts b/src/compose/resolve-block-map.ts index 8cdb03ff..f750be29 100644 --- a/src/compose/resolve-block-map.ts +++ b/src/compose/resolve-block-map.ts @@ -1,5 +1,6 @@ import { Pair } from '../nodes/Pair.ts' import { YAMLMap } from '../nodes/YAMLMap.ts' +import { YAMLSet } from '../nodes/YAMLSet.ts' import type { BlockMap } from '../parse/cst.ts' import type { CollectionTag } from '../schema/types.ts' import type { ComposeContext, ComposeNode } from './compose-node.ts' @@ -7,7 +8,6 @@ import type { ComposeErrorHandler } from './composer.ts' import { resolveProps } from './resolve-props.ts' import { containsNewline } from './util-contains-newline.ts' import { flowIndentCheck } from './util-flow-indent-check.ts' -import { mapIncludes } from './util-map-includes.ts' const startColMsg = 'All mapping items must start at the same column' @@ -17,9 +17,9 @@ export function resolveBlockMap( bm: BlockMap, onError: ComposeErrorHandler, tag?: CollectionTag -): YAMLMap { +): YAMLMap | YAMLSet { const NodeClass = tag?.nodeClass ?? YAMLMap - const map = new NodeClass(ctx.schema) as YAMLMap + const map = new NodeClass(ctx.schema) as YAMLMap | YAMLSet if (ctx.atRoot) ctx.atRoot = false let offset = bm.offset @@ -76,7 +76,7 @@ export function resolveBlockMap( if (ctx.schema.compat) flowIndentCheck(bm.indent, key, onError) ctx.atKey = false - if (mapIncludes(ctx, map.items, keyNode)) + if (map.has(keyNode)) onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique') // value properties @@ -114,9 +114,22 @@ export function resolveBlockMap( : composeEmptyNode(ctx, offset, sep, null, valueProps, onError) if (ctx.schema.compat) flowIndentCheck(bm.indent, value, onError) offset = valueNode.range![2] - const pair = new Pair(keyNode, valueNode) - if (ctx.options.keepSourceTokens) pair.srcToken = collItem - map.items.push(pair) + if (map instanceof YAMLSet) { + if (value || valueProps.anchor || valueProps.tag) { + const msg = 'Set items must not have non-empty values' + onError(valueNode.range!, 'BAD_COLLECTION_TYPE', msg) + } + if (valueProps.comment) { + if (keyNode.comment) keyNode.comment += '\n' + valueProps.comment + else keyNode.comment = valueProps.comment + keyNode.range![2] = valueProps.end + } + map.add(keyNode) + } else { + const pair = new Pair(keyNode, valueNode) + if (ctx.options.keepSourceTokens) pair.srcToken = collItem + map.set(pair) + } } else { // key with no value if (implicitKey) @@ -129,9 +142,13 @@ export function resolveBlockMap( if (keyNode.comment) keyNode.comment += '\n' + valueProps.comment else keyNode.comment = valueProps.comment } - const pair = new Pair(keyNode) - if (ctx.options.keepSourceTokens) pair.srcToken = collItem - map.items.push(pair) + if (map instanceof YAMLSet) { + map.add(keyNode) + } else { + const pair = new Pair(keyNode) + if (ctx.options.keepSourceTokens) pair.srcToken = collItem + map.set(pair) + } } } diff --git a/src/compose/resolve-block-scalar.ts b/src/compose/resolve-block-scalar.ts index 1fb3036b..155722c3 100644 --- a/src/compose/resolve-block-scalar.ts +++ b/src/compose/resolve-block-scalar.ts @@ -1,4 +1,4 @@ -import type { Range } from '../nodes/Node.ts' +import type { Range } from '../nodes/types.ts' import { Scalar } from '../nodes/Scalar.ts' import type { BlockScalar } from '../parse/cst.ts' import type { ComposeContext } from './compose-node.ts' diff --git a/src/compose/resolve-block-seq.ts b/src/compose/resolve-block-seq.ts index a9617073..9717d1c4 100644 --- a/src/compose/resolve-block-seq.ts +++ b/src/compose/resolve-block-seq.ts @@ -50,7 +50,7 @@ export function resolveBlockSeq( : composeEmptyNode(ctx, props.end, start, null, props, onError) if (ctx.schema.compat) flowIndentCheck(bs.indent, value, onError) offset = node.range![2] - seq.items.push(node) + seq._push(node) } seq.range = [bs.offset, offset, commentEnd ?? offset] return seq diff --git a/src/compose/resolve-flow-collection.ts b/src/compose/resolve-flow-collection.ts index cecdc280..3ebc1739 100644 --- a/src/compose/resolve-flow-collection.ts +++ b/src/compose/resolve-flow-collection.ts @@ -1,6 +1,8 @@ +import type { Node } from '../nodes/types.ts' import { Pair } from '../nodes/Pair.ts' import { YAMLMap } from '../nodes/YAMLMap.ts' import { YAMLSeq } from '../nodes/YAMLSeq.ts' +import { YAMLSet } from '../nodes/YAMLSet.ts' import type { FlowCollection, Token } from '../parse/cst.ts' import type { CollectionTag } from '../schema/types.ts' import type { ComposeContext, ComposeNode } from './compose-node.ts' @@ -8,7 +10,6 @@ import type { ComposeErrorHandler } from './composer.ts' import { resolveEnd } from './resolve-end.ts' import { resolveProps } from './resolve-props.ts' import { containsNewline } from './util-contains-newline.ts' -import { mapIncludes } from './util-map-includes.ts' const blockMsg = 'Block collections are not allowed within flow collections' const isBlock = (token: Token | null | undefined) => @@ -20,11 +21,13 @@ export function resolveFlowCollection( fc: FlowCollection, onError: ComposeErrorHandler, tag?: CollectionTag -): YAMLMap | YAMLSeq { +): YAMLMap | YAMLSeq | YAMLSet { const isMap = fc.start.source === '{' const fcName = isMap ? 'flow map' : 'flow sequence' - const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap : YAMLSeq) - const coll = new NodeClass(ctx.schema) as YAMLMap | YAMLSeq + let coll + if (tag?.nodeClass) + coll = new tag.nodeClass(ctx.schema) as YAMLMap | YAMLSeq | YAMLSet + else coll = isMap ? new YAMLMap(ctx.schema) : new YAMLSeq(ctx.schema) coll.flow = true const atRoot = ctx.atRoot if (atRoot) ctx.atRoot = false @@ -93,7 +96,9 @@ export function resolveFlowCollection( } } if (prevItemComment) { - let prev = coll.items[coll.items.length - 1] + let prev!: Node | Pair + if (Array.isArray(coll)) prev = coll[coll.length - 1] + else for (const prev_ of coll.values.values()) prev = prev_ if (prev instanceof Pair) prev = prev.value ?? prev.key if (prev.comment) prev.comment += '\n' + prevItemComment else prev.comment = prevItemComment @@ -108,7 +113,7 @@ export function resolveFlowCollection( const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError) - ;(coll as YAMLSeq).items.push(valueNode) + ;(coll as YAMLSeq)._push(valueNode) offset = valueNode.range![2] if (isBlock(value)) onError(valueNode.range!, 'BLOCK_IN_FLOW', blockMsg) } else { @@ -189,17 +194,18 @@ export function resolveFlowCollection( const pair = new Pair(keyNode, valueNode) if (ctx.options.keepSourceTokens) pair.srcToken = collItem if (isMap) { - const map = coll as YAMLMap - if (mapIncludes(ctx, map.items, keyNode)) + const map = coll as YAMLMap | YAMLSet + if (map.has(keyNode)) onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique') - map.items.push(pair) + if (map instanceof YAMLSet) map.add(keyNode) + else map.set(pair) } else { const map = new YAMLMap(ctx.schema) map.flow = true - map.items.push(pair) + map.set(pair) const endRange = (valueNode ?? keyNode).range! map.range = [keyNode.range![0], endRange[1], endRange[2]] - ;(coll as YAMLSeq).items.push(map) + ;(coll as YAMLSeq)._push(map) } offset = valueNode ? valueNode.range![2] : valueProps.end } diff --git a/src/compose/resolve-flow-scalar.ts b/src/compose/resolve-flow-scalar.ts index 5568d63c..f1882913 100644 --- a/src/compose/resolve-flow-scalar.ts +++ b/src/compose/resolve-flow-scalar.ts @@ -1,5 +1,5 @@ import type { ErrorCode } from '../errors.ts' -import type { Range } from '../nodes/Node.ts' +import type { Range } from '../nodes/types.ts' import { Scalar } from '../nodes/Scalar.ts' import type { FlowScalar } from '../parse/cst.ts' import type { ComposeErrorHandler } from './composer.ts' diff --git a/src/compose/util-map-includes.ts b/src/compose/util-map-includes.ts deleted file mode 100644 index f853de94..00000000 --- a/src/compose/util-map-includes.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Node } from '../nodes/Node.ts' -import type { Pair } from '../nodes/Pair.ts' -import { Scalar } from '../nodes/Scalar.ts' -import type { ComposeContext } from './compose-node.ts' - -export function mapIncludes( - ctx: ComposeContext, - items: Pair[], - search: Node -): boolean { - const { uniqueKeys } = ctx.options - if (uniqueKeys === false) return false - const isEqual = - typeof uniqueKeys === 'function' - ? uniqueKeys - : (a: Node, b: Node) => - a === b || - (a instanceof Scalar && b instanceof Scalar && a.value === b.value) - return items.some(pair => isEqual(pair.key, search)) -} diff --git a/src/doc/Document.ts b/src/doc/Document.ts index 2fb90a6f..6ab10592 100644 --- a/src/doc/Document.ts +++ b/src/doc/Document.ts @@ -1,12 +1,12 @@ import type { YAMLError, YAMLWarning } from '../errors.ts' import { Alias } from '../nodes/Alias.ts' -import { Collection, type Primitive } from '../nodes/Collection.ts' -import type { Node, NodeType, Range } from '../nodes/Node.ts' -import type { Pair } from '../nodes/Pair.ts' -import { Scalar } from '../nodes/Scalar.ts' +import { Pair } from '../nodes/Pair.ts' +import type { Scalar } from '../nodes/Scalar.ts' import { ToJSContext } from '../nodes/toJS.ts' -import type { YAMLMap } from '../nodes/YAMLMap.ts' -import type { YAMLSeq } from '../nodes/YAMLSeq.ts' +import type { Node, NodeType, Primitive, Range } from '../nodes/types.ts' +import { YAMLMap } from '../nodes/YAMLMap.ts' +import { YAMLSeq } from '../nodes/YAMLSeq.ts' +import { YAMLSet } from '../nodes/YAMLSet.ts' import type { CreateNodeOptions, DocumentOptions, @@ -22,7 +22,7 @@ import { applyReviver } from './applyReviver.ts' import { Directives } from './directives.ts' import { NodeCreator } from './NodeCreator.ts' -export type DocValue = Scalar | YAMLSeq | YAMLMap +export type DocValue = Scalar | YAMLSeq | YAMLMap | YAMLSet export type Replacer = any[] | ((key: any, value: any) => unknown) @@ -115,7 +115,6 @@ export class Document< prettyErrors: true, strict: true, stringKeys: false, - uniqueKeys: true, version: '1.2' }, options @@ -150,16 +149,6 @@ export class Document< return copy } - /** Adds a value to the document. */ - add(value: any): void { - assertCollection(this.value).add(value) - } - - /** Adds a value to the document. */ - addIn(path: unknown[], value: unknown): void { - assertCollection(this.value).addIn(path, value) - } - /** * Create a new `Alias` node, ensuring that the target `node` has the required anchor. * @@ -237,76 +226,45 @@ export class Document< return pair } - /** - * Removes a value from the document. - * @returns `true` if the item was found and removed. - */ - delete(key: any): boolean { - return assertCollection(this.value).delete(key) - } - - /** - * Removes a value from the document. - * @returns `true` if the item was found and removed. - */ - deleteIn(path: unknown[]): boolean { - if (!path.length) { - this.value = new Scalar(null) as Value - return true - } - return assertCollection(this.value).deleteIn(path) - } - /** * Returns item at `key`, or `undefined` if not found. */ - get(key: any): Strict extends true ? Node | Pair | undefined : any { - return this.value instanceof Collection ? this.value.get(key) : undefined - } - - /** - * Returns item at `path`, or `undefined` if not found. - */ - getIn( - path: unknown[] - ): Strict extends true ? Node | Pair | null | undefined : any { - if (!path.length) return this.value - return this.value instanceof Collection ? this.value.getIn(path) : undefined - } - - /** - * Checks if the document includes a value with the key `key`. - */ - has(key: any): boolean { - return this.value instanceof Collection ? this.value.has(key) : false + get(key: any): Strict extends true ? Node | Pair | null | undefined : any { + if (this.value instanceof YAMLMap || this.value instanceof YAMLSet) { + return this.value.get(key) + } + if (this.value instanceof YAMLSeq) { + if (Number.isInteger(key)) return this.value.at(key) + throw new TypeError(`Expected an integer, not ${JSON.stringify(key)}.`) + } + return undefined } /** - * Checks if the document includes a value at `path`. + * Returns pair at `key`, or `undefined` if not found. */ - hasIn(path: unknown[]): boolean { - if (!path.length) return true - return this.value instanceof Collection ? this.value.hasIn(path) : false + getPair(key: any): Strict extends true ? Pair | undefined : any { + if (this.value instanceof YAMLMap) return this.value.getPair(key) + if (this.value instanceof YAMLSeq) { + if (!Number.isInteger(key)) { + throw new TypeError(`Expected an integer, not ${JSON.stringify(key)}.`) + } + const pair = this.value.at(key) + if (pair instanceof Pair) return pair + throw new TypeError(`Value at ${key} is not a Pair`) + } + return undefined } /** - * Sets a value in this document. For `!!set`, `value` needs to be a - * boolean to add/remove the item from the set. + * Sets a value in this document's top-level collection. For `!!set`, `value` is ignored. */ set(key: any, value: any): void { - assertCollection(this.value).set(key, value) - } - - /** - * Sets a value in this document. For `!!set`, `value` needs to be a - * boolean to add/remove the item from the set. - */ - setIn(path: unknown[], value: unknown): void { - if (!path.length) { - this.value = value as Value - } else { - assertCollection(this.value).setIn(path, value) - } + if (this.value instanceof YAMLSet) { + this.value.add(key) + } else if (this.value instanceof YAMLMap || this.value instanceof YAMLSeq) { + this.value.set(key, value) + } else throw new Error('Expected a YAML collection as document value') } /** @@ -387,8 +345,3 @@ export class Document< return stringifyDocument(this, options) } } - -function assertCollection(value: unknown) { - if (value instanceof Collection) return value as YAMLMap | YAMLSeq - throw new Error('Expected a YAML collection as document value') -} diff --git a/src/doc/NodeCreator.ts b/src/doc/NodeCreator.ts index 7cf5035f..5b1de550 100644 --- a/src/doc/NodeCreator.ts +++ b/src/doc/NodeCreator.ts @@ -1,10 +1,8 @@ import { Alias } from '../nodes/Alias.ts' -import { Collection } from '../nodes/Collection.ts' -import { isNode } from '../nodes/identity.ts' -import { type Node } from '../nodes/Node.ts' +import { isCollection, isNode } from '../nodes/identity.ts' +import { type Node } from '../nodes/types.ts' import { Pair } from '../nodes/Pair.ts' import { Scalar } from '../nodes/Scalar.ts' -import type { YAMLMap } from '../nodes/YAMLMap.ts' import type { CreateNodeOptions } from '../options.ts' import type { Schema } from '../schema/Schema.ts' import type { CollectionTag, ScalarTag } from '../schema/types.ts' @@ -18,7 +16,6 @@ export class NodeCreator { replacer?: Replacer schema: Schema - #aliasDuplicateObjects: boolean #anchorPrefix: string #aliasObjects: unknown[] = [] #doc?: Document @@ -33,10 +30,7 @@ export class NodeCreator { options?: CreateNodeOptions, replacer?: Replacer ) - constructor( - schema: Schema, - options: CreateNodeOptions & { aliasDuplicateObjects: false } - ) + constructor(schema: Schema, options?: CreateNodeOptions) constructor( docOrSchema: Document | Schema, options: CreateNodeOptions = {}, @@ -48,11 +42,10 @@ export class NodeCreator { this.#flow = options.flow ?? false this.#onTagObj = options.onTagObj - this.#aliasDuplicateObjects = options.aliasDuplicateObjects ?? true // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing this.#anchorPrefix = options.anchorPrefix || 'a' if (docOrSchema instanceof Document) { - this.#doc = docOrSchema + if (options.aliasDuplicateObjects ?? true) this.#doc = docOrSchema this.schema = docOrSchema.schema } else { this.schema = docOrSchema @@ -63,12 +56,8 @@ export class NodeCreator { if (value instanceof Document) value = value.value if (isNode(value)) return value if (value instanceof Pair) { - const map = (this.schema.map.nodeClass! as typeof YAMLMap).from( - this, - null - ) - map.items.push(value) - return map + const map = this.schema.map.createNode(this, null) + return map.set(value) } if ( value instanceof String || @@ -84,12 +73,12 @@ export class NodeCreator { // after first. The `ref` wrapper allows for circular references to resolve. let ref: { anchor: string | null; node: Node | null } | undefined = undefined - if (this.#aliasDuplicateObjects && value && typeof value === 'object') { + if (this.#doc && value && typeof value === 'object') { ref = this.#sourceObjects.get(value) if (ref) { if (!ref.anchor) { this.#aliasObjects.push(value) - this.#prevAnchors ??= anchorNames(this.#doc!) + this.#prevAnchors ??= anchorNames(this.#doc) ref.anchor = findNewAnchor(this.#anchorPrefix, this.#prevAnchors) this.#prevAnchors.add(ref.anchor) } @@ -132,15 +121,12 @@ export class NodeCreator { this.#onTagObj = undefined } - const node = - tagObj?.createNode?.(this, value) ?? - tagObj?.nodeClass?.from?.(this, value) ?? - new Scalar(value) + const node = tagObj?.createNode?.(this, value) ?? new Scalar(value) if (tagName) node.tag = tagName else if (!tagObj.default) node.tag = tagObj.tag if (ref) ref.node = node - if (this.#flow && node instanceof Collection) node.flow = true + if (this.#flow && isCollection(node)) node.flow = true return node } @@ -161,7 +147,7 @@ export class NodeCreator { if ( typeof ref === 'object' && ref.anchor && - (ref.node instanceof Scalar || ref.node instanceof Collection) + (ref.node instanceof Scalar || isCollection(ref.node)) ) { ref.node.anchor = ref.anchor } else { diff --git a/src/doc/anchors.ts b/src/doc/anchors.ts index 8bbe6926..04f3f2e9 100644 --- a/src/doc/anchors.ts +++ b/src/doc/anchors.ts @@ -1,4 +1,4 @@ -import type { Node } from '../nodes/Node.ts' +import type { Node } from '../nodes/types.ts' import { visit } from '../visit.ts' import type { Document, DocValue } from './Document.ts' diff --git a/src/index.ts b/src/index.ts index ad063f60..7240b3a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,16 +11,18 @@ export { Composer } from './compose/composer.ts' export { Document, type DocValue } from './doc/Document.ts' export { Schema } from './schema/Schema.ts' -export type { ErrorCode } from './errors.ts' export { YAMLError, YAMLParseError, YAMLWarning } from './errors.ts' +export type { ErrorCode } from './errors.ts' export { Alias } from './nodes/Alias.ts' -export { isNode } from './nodes/identity.ts' -export type { Node, NodeBase, Range } from './nodes/Node.ts' +export { isCollection, isNode } from './nodes/identity.ts' export { Pair } from './nodes/Pair.ts' export { Scalar } from './nodes/Scalar.ts' +export type { Node, NodeBase, Range } from './nodes/types.ts' export { YAMLMap } from './nodes/YAMLMap.ts' +export type { KeyArg } from './nodes/YAMLMap.ts' export { YAMLSeq } from './nodes/YAMLSeq.ts' +export { YAMLSet } from './nodes/YAMLSet.ts' export type { CreateNodeOptions, @@ -47,12 +49,11 @@ export { export type { TagId, Tags } from './schema/tags.ts' export type { CollectionTag, ScalarTag } from './schema/types.ts' export type { YAMLOMap } from './schema/yaml-1.1/omap.ts' -export type { YAMLSet } from './schema/yaml-1.1/set.ts' +export { visit, visitAsync } from './visit.ts' export type { asyncVisitor, asyncVisitorFn, visitor, visitorFn } from './visit.ts' -export { visit, visitAsync } from './visit.ts' diff --git a/src/nodes/Alias.ts b/src/nodes/Alias.ts index eee15eac..3a7128d6 100644 --- a/src/nodes/Alias.ts +++ b/src/nodes/Alias.ts @@ -3,12 +3,13 @@ import type { Document, DocValue } from '../doc/Document.ts' import type { FlowScalar } from '../parse/cst.ts' import type { StringifyContext } from '../stringify/stringify.ts' import { visit } from '../visit.ts' -import type { Node, NodeBase, Range } from './Node.ts' import { Pair } from './Pair.ts' import type { Scalar } from './Scalar.ts' import { ToJSContext } from './toJS.ts' +import type { Node, NodeBase, Range } from './types.ts' import type { YAMLMap } from './YAMLMap.ts' import type { YAMLSeq } from './YAMLSeq.ts' +import type { YAMLSet } from './YAMLSet.ts' export class Alias implements NodeBase { source: string @@ -62,7 +63,7 @@ export class Alias implements NodeBase { resolve( doc: Document, ctx?: ToJSContext - ): Scalar | YAMLMap | YAMLSeq | undefined { + ): Scalar | YAMLMap | YAMLSeq | YAMLSet | undefined { let nodes: Node[] if (ctx?.aliasResolveCache) { nodes = ctx.aliasResolveCache @@ -76,7 +77,7 @@ export class Alias implements NodeBase { if (ctx) ctx.aliasResolveCache = nodes } - let found: Scalar | YAMLMap | YAMLSeq | undefined = undefined + let found: Scalar | YAMLMap | YAMLSeq | YAMLSet | undefined = undefined for (const node of nodes) { if (node === this) break if (node.anchor === this.source) found = node @@ -153,9 +154,9 @@ function getAliasCount( const kc = getAliasCount(doc, ctx, node.key, anchors) const vc = getAliasCount(doc, ctx, node.value, anchors) return Math.max(kc, vc) - } else if (node && 'items' in node) { + } else if (Array.isArray(node)) { let count = 0 - for (const item of node.items) { + for (const item of node) { const c = getAliasCount(doc, ctx, item, anchors) if (c > count) count = c } diff --git a/src/nodes/Collection.ts b/src/nodes/Collection.ts deleted file mode 100644 index c60244b7..00000000 --- a/src/nodes/Collection.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { NodeCreator } from '../doc/NodeCreator.ts' -import type { Token } from '../parse/cst.ts' -import type { Schema } from '../schema/Schema.ts' -import type { Node, Range } from './Node.ts' -import type { Pair } from './Pair.ts' -import type { Scalar } from './Scalar.ts' - -export type Primitive = boolean | number | bigint | string | null -export type NodeOf = T extends Primitive ? Scalar : T - -export function collectionFromPath( - schema: Schema, - path: unknown[], - value: unknown -): Node { - let v = value - for (let i = path.length - 1; i >= 0; --i) { - const k = path[i] - if (typeof k === 'number' && Number.isInteger(k) && k >= 0) { - const a: unknown[] = [] - a[k] = v - v = a - } else { - v = new Map([[k, v]]) - } - } - return new NodeCreator(schema, { aliasDuplicateObjects: false }).create(v) -} - -export abstract class Collection { - schema: Schema | undefined - - declare items: (Node | Pair)[] - - /** An optional anchor on this collection. Used by alias nodes. */ - declare anchor?: string - - /** - * If true, stringify this and all child nodes using flow rather than - * block styles. - */ - declare flow?: boolean - - /** A comment on or immediately after this collection. */ - declare comment?: string | null - - /** A comment before this collection. */ - declare commentBefore?: string | null - - /** - * The `[start, value-end, node-end]` character offsets for - * the part of the source parsed into this collection (undefined if not parsed). - * The `value-end` and `node-end` positions are themselves not included in their respective ranges. - */ - declare range?: Range | null - - /** A blank line before this collection and its commentBefore */ - declare spaceBefore?: boolean - - /** The CST token that was composed into this collection. */ - declare srcToken?: Token - - /** A fully qualified tag, if required */ - declare tag?: string - - constructor(schema?: Schema) { - Object.defineProperty(this, 'schema', { - value: schema, - configurable: true, - enumerable: false, - writable: true - }) - } - - /** - * Create a copy of this collection. - * - * @param schema - If defined, overwrites the original's schema - */ - clone(schema?: Schema): this { - const copy: this = Object.create( - Object.getPrototypeOf(this), - Object.getOwnPropertyDescriptors(this) - ) - if (schema) copy.schema = schema - copy.items = copy.items.map(it => it.clone(schema)) - if (this.range) copy.range = [...this.range] - return copy - } - - /** Adds a value to the collection. */ - abstract add(value: unknown): void - - /** - * Removes a value from the collection. - * @returns `true` if the item was found and removed. - */ - abstract delete(key: unknown): boolean - - /** - * Returns item at `key`, or `undefined` if not found. - */ - abstract get(key: unknown): Node | Pair | undefined - - /** - * Checks if the collection includes a value with the key `key`. - */ - abstract has(key: unknown): boolean - - /** - * Sets a value in this collection. - */ - abstract set(key: unknown, value: unknown): void - - /** - * Adds a value to the collection. - * - * For `!!map` and `!!omap` the value must be a Pair instance. - */ - addIn(path: unknown[], value: unknown): void { - if (!path.length) this.add(value) - else { - const [key, ...rest] = path - const node = this.get(key) - if (node instanceof Collection) node.addIn(rest, value) - else if (node === undefined && this.schema) - this.set(key, collectionFromPath(this.schema, rest, value)) - else - throw new Error( - `Expected YAML collection at ${key}. Remaining path: ${rest}` - ) - } - } - - /** - * Removes a value from the collection. - * - * @returns `true` if the item was found and removed. - */ - deleteIn(path: unknown[]): boolean { - const [key, ...rest] = path - if (rest.length === 0) return this.delete(key) - const node = this.get(key) - if (node instanceof Collection) return node.deleteIn(rest) - else - throw new Error( - `Expected YAML collection at ${key}. Remaining path: ${rest}` - ) - } - - /** - * Returns item at `key`, or `undefined` if not found. - */ - getIn(path: unknown[]): Node | Pair | undefined { - const [key, ...rest] = path - const node = this.get(key) - if (rest.length === 0) return node - else return node instanceof Collection ? node.getIn(rest) : undefined - } - - /** - * Checks if the collection includes a value with the key `key`. - */ - hasIn(path: unknown[]): boolean { - const [key, ...rest] = path - if (rest.length === 0) return this.has(key) - const node = this.get(key) - return node instanceof Collection ? node.hasIn(rest) : false - } - - /** - * Sets a value in this collection. - */ - setIn(path: unknown[], value: unknown): void { - const [key, ...rest] = path - if (rest.length === 0) { - this.set(key, value) - } else { - const node = this.get(key) - if (node instanceof Collection) node.setIn(rest, value) - else if (node === undefined && this.schema) - this.set(key, collectionFromPath(this.schema, rest, value)) - else - throw new Error( - `Expected YAML collection at ${key}. Remaining path: ${rest}` - ) - } - } -} diff --git a/src/nodes/Pair.ts b/src/nodes/Pair.ts index 9296543a..18ddad34 100644 --- a/src/nodes/Pair.ts +++ b/src/nodes/Pair.ts @@ -4,9 +4,8 @@ import type { Schema } from '../schema/Schema.ts' import type { StringifyContext } from '../stringify/stringify.ts' import { stringifyPair } from '../stringify/stringifyPair.ts' import { addPairToJSMap } from './addPairToJSMap.ts' -import type { NodeOf, Primitive } from './Collection.ts' -import type { Node } from './Node.ts' import type { ToJSContext } from './toJS.ts' +import type { Node, NodeOf, Primitive } from './types.ts' export class Pair< K extends Primitive | Node = Primitive | Node, @@ -15,6 +14,10 @@ export class Pair< key: NodeOf value: NodeOf | null + declare comment?: never + declare commentBefore?: never + declare spaceBefore?: never + /** The CST token that was composed into this pair. */ declare srcToken?: CollectionItem diff --git a/src/nodes/Scalar.ts b/src/nodes/Scalar.ts index c82f91d5..8490ad5a 100644 --- a/src/nodes/Scalar.ts +++ b/src/nodes/Scalar.ts @@ -1,7 +1,7 @@ import type { Document, DocValue } from '../doc/Document.ts' import type { BlockScalar, FlowScalar } from '../parse/cst.ts' -import type { NodeBase, Range } from './Node.ts' import type { ToJSContext } from './toJS.ts' +import type { NodeBase, Range } from './types.ts' import type { MapLike } from './YAMLMap.ts' export declare namespace Scalar { diff --git a/src/nodes/YAMLMap.ts b/src/nodes/YAMLMap.ts index 84bb77ea..b2075041 100644 --- a/src/nodes/YAMLMap.ts +++ b/src/nodes/YAMLMap.ts @@ -2,130 +2,219 @@ import type { Document, DocValue } from '../doc/Document.ts' import { NodeCreator } from '../doc/NodeCreator.ts' import type { CreateNodeOptions } from '../options.ts' import type { BlockMap, FlowCollection } from '../parse/cst.ts' +import type { Schema } from '../schema/Schema.ts' import type { StringifyContext } from '../stringify/stringify.ts' -import { stringifyCollection } from '../stringify/stringifyCollection.ts' +import { indentComment, lineComment } from '../stringify/stringifyComment.ts' +import { stringifyPair } from '../stringify/stringifyPair.ts' import { addPairToJSMap } from './addPairToJSMap.ts' -import { Collection, type NodeOf, type Primitive } from './Collection.ts' import { isNode } from './identity.ts' -import type { Node, NodeBase } from './Node.ts' import { Pair } from './Pair.ts' import { Scalar } from './Scalar.ts' import { ToJSContext } from './toJS.ts' +import type { CollectionBase, Node, NodeOf, Primitive, Range } from './types.ts' +import { cloneMapOrSet } from './util-clone-map-or-set.ts' export type MapLike = | Map | Set | Record -export function findPair< - K extends Primitive | Node = Primitive | Node, - V extends Primitive | Node = Primitive | Node ->(items: Iterable>, key: unknown): Pair | undefined { - const k = key instanceof Scalar ? key.value : key - for (const it of items) { - if (it.key === key || it.key === k) return it - if (it.key instanceof Scalar && it.key.value === k) return it - } - return undefined -} +export type KeyArg = + | K + | NodeOf + | (K extends Scalar ? K['value'] : never) + | Pair export class YAMLMap< K extends Primitive | Node = Primitive | Node, V extends Primitive | Node = Primitive | Node -> - extends Collection - implements NodeBase -{ - static get tagName(): 'tag:yaml.org,2002:map' { - return 'tag:yaml.org,2002:map' - } +> implements CollectionBase { + static readonly tagName = 'tag:yaml.org,2002:map' + + declare schema: Schema + + values: Map> = new Map() + + /** An optional anchor on this map. Used by alias nodes. */ + declare anchor?: string + + /** If true, stringify this and all child nodes using flow rather than block styles. */ + declare flow?: boolean - items: Pair[] = [] + /** A comment on or immediately after this map. */ + declare comment?: string | null + + /** A comment before this map. */ + declare commentBefore?: string | null + + /** + * The `[start, value-end, node-end]` character offsets for + * the part of the source parsed into this map (undefined if not parsed). + * The `value-end` and `node-end` positions are themselves not included in their respective ranges. + */ + declare range?: Range | null + + /** A blank line before this map and its commentBefore */ + declare spaceBefore?: boolean + + /** The CST token that was composed into this map. */ declare srcToken?: BlockMap | FlowCollection + /** A fully qualified tag, if required */ + declare tag?: string + /** - * A generic collection parsing method that can be extended - * to other node classes that inherit from YAMLMap + * A generic collection factory method that can be used + * by other node classes that inherit from YAMLMap */ - static from(nc: NodeCreator, obj: unknown): YAMLMap { + static create(nc: NodeCreator, obj: unknown): YAMLMap { const { replacer } = nc const map = new this(nc.schema) - const add = (key: unknown, value: unknown) => { - if (typeof replacer === 'function') value = replacer.call(obj, key, value) - else if (Array.isArray(replacer) && !replacer.includes(key)) return - if (value !== undefined || nc.keepUndefined) - map.items.push(nc.createPair(key, value)) - } - if (obj instanceof Map) { - for (const [key, value] of obj) add(key, value) - } else if (obj && typeof obj === 'object') { - for (const key of Object.keys(obj)) add(key, (obj as any)[key]) - } - if (typeof nc.schema.sortMapEntries === 'function') { - map.items.sort(nc.schema.sortMapEntries) + if (obj && typeof obj === 'object') { + const iter = obj instanceof Map ? obj : Object.entries(obj) + for (let [key, value] of iter) { + if (replacer) { + if (typeof replacer === 'function') + value = replacer.call(obj, key, value) + else if (Array.isArray(replacer) && !replacer.includes(key)) continue + } + if (value !== undefined || nc.keepUndefined) { + const mk = nc.schema.mapKey(key) + const pair = nc.createPair(key, value) + map.values.set(mk, pair) + } + } } return map } + constructor(schema: Schema, elements?: Array>) { + Object.defineProperty(this, 'schema', { + value: schema, + configurable: true, + enumerable: false, + writable: true + }) + this.values = new Map( + elements?.map(pair => [this.schema.mapKey(pair), pair]) + ) + } + + get size(): number { + return this.values.size + } + /** - * Adds a key-value pair to the map. + * Create a copy of this map. * - * Using a key that is already in the collection overwrites the previous value. + * @param schema - If defined, overwrites the original's schema */ - add(pair: Pair): void { - if (!(pair instanceof Pair)) throw new TypeError('Expected a Pair') - - const prev = findPair(this.items, pair.key) - const sortEntries = this.schema?.sortMapEntries - if (prev) { - // For scalars, keep the old node & its comments and anchors - if (prev.value instanceof Scalar && pair.value instanceof Scalar) - prev.value.value = pair.value.value - else prev.value = pair.value - } else if (sortEntries) { - const i = this.items.findIndex(item => sortEntries(pair, item) < 0) - if (i === -1) this.items.push(pair) - else this.items.splice(i, 0, pair) - } else { - this.items.push(pair) + clone(schema?: Schema): this { + return cloneMapOrSet(this, schema) + } + + /** + * Remove a value from the mapping. + * @returns `true` if the item was found and removed. + */ + delete(key: KeyArg): boolean { + let mk = this.schema.mapKey(key) + if (this.values.delete(mk)) return true + mk = key instanceof Pair ? key.key : isNode(key) ? key : null + if (mk) { + for (const [k, p] of this.values) + if (p.key === mk) return this.values.delete(k) } + return false } - delete(key: unknown): boolean { - const it = findPair(this.items, key) - if (!it) return false - const del = this.items.splice(this.items.indexOf(it), 1) - return del.length > 0 + /** Return value at `key`, or `undefined` if not found. */ + get(key: KeyArg): NodeOf | null | undefined { + return this.getPair(key)?.value } - get(key: unknown): NodeOf | undefined { - const it = findPair(this.items, key) - return it?.value ?? undefined + /** Return pair at `key`, or `undefined` if not found. */ + getPair(key: KeyArg): Pair | undefined { + let mk = this.schema.mapKey(key) + const pair = this.values.get(mk) + if (pair) return pair + mk = key instanceof Pair ? key.key : isNode(key) ? key : null + if (mk) { + for (const p of this.values.values()) if (p.key === mk) return p + } + return undefined } - has(key: unknown): boolean { - return !!findPair(this.items, key) + /** Check if the mapping includes a value with the key `key`. */ + has(key: KeyArg): boolean { + let mk = this.schema.mapKey(key) + if (this.values.has(mk)) return true + mk = key instanceof Pair ? key.key : isNode(key) ? key : null + if (mk) { + for (const p of this.values.values()) if (p.key === mk) return true + } + return false + } + + /** + * Return the internal Map key matching `key`, or `undefined` if not found. + * + * @param allowMissing - If `true`, a key is always returned, + * even if the key is not in the map. + */ + keyOf(key: KeyArg, allowMissing = false): unknown { + let mk = this.schema.mapKey(key) + if (allowMissing || this.values.has(mk)) return mk + mk = key instanceof Pair ? key.key : isNode(key) ? key : null + if (mk) { + for (const [k, v] of this.values) if (v.key === mk) return k + } + return undefined + } + + pairs(): Iterable> { + return this.values.values() } set( - key: unknown, - value: unknown, + key: K | NodeOf | (K extends Scalar ? K['value'] : never), + value: V | NodeOf | (V extends Scalar ? V['value'] : never) | null, options?: Omit - ): void { - let pair: Pair - if (isNode(key) && (value === null || isNode(value))) { - pair = new Pair(key, value) - } else if (!this.schema) { - throw new Error('Schema is required') + ): this + set(pair: Pair): this + set( + keyOrPair: KeyArg, + value?: V | NodeOf | (V extends Scalar ? V['value'] : never) | null, + options?: Omit + ): this { + const mk = this.schema.mapKey(keyOrPair) + + let pair: Pair + if (keyOrPair instanceof Pair) { + pair = keyOrPair + } else if (isNode(keyOrPair) && (value == null || isNode(value))) { + pair = new Pair(keyOrPair, value ?? null) } else { - const nc = new NodeCreator(this.schema, { - ...options, - aliasDuplicateObjects: false - }) - pair = nc.createPair(key, value) - nc.setAnchors() + const nc = new NodeCreator(this.schema, options) + pair = nc.createPair(keyOrPair, value) as Pair + } + + if (pair.value instanceof Scalar) { + const prev = this.values.get(mk) + if (prev) { + if (!prev.value) { + prev.value = pair.value + return this + } + if (prev.value instanceof Scalar) { + // For scalars, keep the old node & its comments and anchors + Object.assign(prev.value, pair.value) + return this + } + } } - this.add(pair as Pair) + this.values.set(mk, pair) + return this } /** @@ -148,7 +237,7 @@ export class YAMLMap< ctx ??= new ToJSContext() const map = Type ? new Type() : ctx?.mapAsMap ? new Map() : {} if (this.anchor) ctx.setAnchor(this, map) - for (const item of this.items) addPairToJSMap(doc, ctx, map, item) + for (const pair of this.values.values()) addPairToJSMap(doc, ctx, map, pair) return map } @@ -158,18 +247,136 @@ export class YAMLMap< onChompKeep?: () => void ): string { if (!ctx) return JSON.stringify(this) - for (const item of this.items) { - if (!(item instanceof Pair)) - throw new Error( - `Map items must all be pairs; found ${JSON.stringify(item)} instead` - ) + let pairs: Iterable> = this.values.values() + if (ctx.sortMapEntries) pairs = Array.from(pairs).sort(ctx.sortMapEntries) + return (ctx.inFlow ?? this.flow) + ? this.#stringifyFlowMap(ctx, pairs) + : this.#stringifyBlockMap(ctx, pairs, onComment, onChompKeep) + } + + #stringifyBlockMap( + ctx: StringifyContext, + pairs: Iterable>, + onComment?: () => void, + onChompKeep?: () => void + ) { + const { + indent, + options: { commentString } + } = ctx + + let chompKeep = false // flag for the preceding node's status + const lines: string[] = [] + for (const pair of pairs) { + let comment: string | null = null + if (!chompKeep && pair.key.spaceBefore) lines.push('') + + let cb = pair.key.commentBefore + if (cb && chompKeep) cb = cb.replace(/^\n+/, '') + if (cb) { + const ic = indentComment(commentString(cb), indent).trimStart() + lines.push(ic) + } + + chompKeep = false + let str = stringifyPair( + pair, + ctx, + () => (comment = null), + () => (chompKeep = true) + ) + if (comment) str += lineComment(str, indent, commentString(comment)) + if (chompKeep && comment) chompKeep = false + lines.push(str) } - return stringifyCollection(this, ctx, { - blockItemPrefix: '', - flowChars: { start: '{', end: '}' }, - itemIndent: ctx.indent || '', - onChompKeep, - onComment - }) + + let str: string + if (lines.length === 0) { + str = '{}' + } else { + str = lines[0] + for (let i = 1; i < lines.length; ++i) { + const line = lines[i] + str += line ? `\n${indent}${line}` : '\n' + } + } + + if (this.comment) { + str += '\n' + indentComment(commentString(this.comment), indent) + onComment?.() + } else if (chompKeep) onChompKeep?.() + + return str + } + + #stringifyFlowMap(ctx: StringifyContext, pairs: Iterable>) { + const { + indent, + indentStep, + flowCollectionPadding: fcPadding, + options: { commentString, lineWidth, trailingComma } + } = ctx + const itemIndent = indent + indentStep + const itemCtx = { ...ctx, indent: itemIndent, inFlow: true } + + let reqNewline = false + let linesAtValue = 0 + const lines: string[] = [] + let itemsLeft = this.values.size + for (const pair of pairs) { + itemsLeft -= 1 + + let comment: string | null = null + const ik = pair.key + if (ik.spaceBefore) { + lines.push('') + reqNewline = true + } + if (ik.commentBefore) { + const ic = indentComment(commentString(ik.commentBefore), indent) + lines.push(ic.trimStart()) // Avoid double indent on first line + reqNewline = true + } + if (ik.comment) reqNewline = true + + const iv = pair.value + if (iv) { + if (iv.comment) comment = iv.comment + if (iv.commentBefore) reqNewline = true + } else if (ik?.comment) { + comment = ik.comment + } + + if (comment) reqNewline = true + let str = stringifyPair(pair, itemCtx, () => (comment = null)) + reqNewline ||= lines.length > linesAtValue || str.includes('\n') + if (itemsLeft > 0) { + str += ',' + } else if (trailingComma) { + if (!reqNewline && lineWidth > 0) { + const len = + lines.reduce((sum, line) => sum + line.length + 2, 2) + + (str.length + 2) + reqNewline = len > lineWidth + } + if (reqNewline) str += ',' + } + if (comment) str += lineComment(str, itemIndent, commentString(comment)) + lines.push(str) + linesAtValue = lines.length + } + + if (lines.length === 0) return '{}' + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2) + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth + } + if (reqNewline) { + let str = '{' + for (const line of lines) + str += line ? `\n${indentStep}${indent}${line}` : '\n' + return `${str}\n${indent}}` + } + return `{${fcPadding}${lines.join(' ')}${fcPadding}}` } } diff --git a/src/nodes/YAMLSeq.ts b/src/nodes/YAMLSeq.ts index d083ac88..ae5bf86a 100644 --- a/src/nodes/YAMLSeq.ts +++ b/src/nodes/YAMLSeq.ts @@ -2,14 +2,15 @@ import type { Document, DocValue } from '../doc/Document.ts' import { NodeCreator } from '../doc/NodeCreator.ts' import type { CreateNodeOptions } from '../options.ts' import type { BlockSequence, FlowCollection } from '../parse/cst.ts' +import type { Schema } from '../schema/Schema.ts' import type { StringifyContext } from '../stringify/stringify.ts' -import { stringifyCollection } from '../stringify/stringifyCollection.ts' -import { Collection, type NodeOf, type Primitive } from './Collection.ts' +import { stringify } from '../stringify/stringify.ts' +import { indentComment, lineComment } from '../stringify/stringifyComment.ts' import { isNode } from './identity.ts' -import type { Node, NodeBase } from './Node.ts' -import type { Pair } from './Pair.ts' +import { Pair } from './Pair.ts' import { Scalar } from './Scalar.ts' import { ToJSContext } from './toJS.ts' +import type { CollectionBase, Node, NodeOf, Primitive, Range } from './types.ts' const isScalarValue = (value: unknown): boolean => !value || (typeof value !== 'function' && typeof value !== 'object') @@ -17,106 +18,180 @@ const isScalarValue = (value: unknown): boolean => export class YAMLSeq< T extends Primitive | Node | Pair = Primitive | Node | Pair > - extends Collection - implements NodeBase + extends Array> + implements CollectionBase { static get tagName(): 'tag:yaml.org,2002:seq' { return 'tag:yaml.org,2002:seq' } - items: NodeOf[] = [] + declare schema: Schema + + /** An optional anchor on this collection. Used by alias nodes. */ + declare anchor?: string + + /** + * If true, stringify this and all child nodes using flow rather than + * block styles. + */ + declare flow?: boolean + + /** A comment on or immediately after this collection. */ + declare comment?: string | null + + /** A comment before this collection. */ + declare commentBefore?: string | null + + /** + * The `[start, value-end, node-end]` character offsets for + * the part of the source parsed into this collection (undefined if not parsed). + * The `value-end` and `node-end` positions are themselves not included in their respective ranges. + */ + declare range?: Range | null + + /** A blank line before this collection and its commentBefore */ + declare spaceBefore?: boolean + + /** The CST token that was composed into this collection. */ declare srcToken?: BlockSequence | FlowCollection - add( - value: T, - options?: Omit - ): void { - if (isNode(value)) this.items.push(value as NodeOf) - else if (!this.schema) throw new Error('Schema is required') - else { - const nc = new NodeCreator(this.schema, { - ...options, - aliasDuplicateObjects: false - }) - this.items.push(nc.create(value) as NodeOf) - nc.setAnchors() + /** A fully qualified tag, if required */ + declare tag?: string + + /** + * A generic collection factory method that can be extended + * to other node classes that inherit from YAMLSeq + */ + static create(nc: NodeCreator, obj: unknown): YAMLSeq { + const seq = new this(nc.schema) + if (obj && Symbol.iterator in Object(obj)) { + let i = 0 + for (let it of obj as Iterable) { + if (typeof nc.replacer === 'function') { + const key = obj instanceof Set ? it : String(i++) + it = nc.replacer.call(obj, key, it) + } + seq.push(nc.create(it)) + } } + return seq + } + + constructor(schema: Schema, elements: Array> = []) { + super(...nodeValues(schema, elements)) + Object.defineProperty(this, 'schema', { + value: schema, + configurable: true, + enumerable: false, + writable: true + }) + } + + get size(): number { + return this.length } /** - * Removes a value from the collection. + * Create a copy of this collection. * - * Throws if `idx` is not a non-negative integer. - * - * @returns `true` if the item was found and removed. + * @param schema - If defined, overwrites the original's schema */ - delete(idx: number): boolean { - if (!Number.isInteger(idx)) - throw new TypeError(`Expected an integer, not ${idx}.`) - if (idx < 0) throw new RangeError(`Invalid negative index ${idx}`) - const del = this.items.splice(idx, 1) - return del.length > 0 + clone(schema?: Schema): this { + const copy = (this.constructor as typeof YAMLSeq).from(this, it => + it.clone(schema) + ) as typeof this + if (this.range) copy.range = [...this.range] + const propDesc = Object.getOwnPropertyDescriptors(this) + for (const [name, prop] of Object.entries(propDesc)) { + if (!(name in copy)) Object.defineProperty(copy, name, prop) + } + if (schema) copy.schema = schema + return copy } /** - * Returns item at `key`, or `undefined` if not found. + * Change all elements within a range of indices in this sequence to a static value. * - * Throws if `idx` is not a non-negative integer. + * Non-node values are converted to Node values. */ - get(idx: number): NodeOf | undefined { - if (!Number.isInteger(idx)) - throw new TypeError(`Expected an integer, not ${JSON.stringify(idx)}.`) - if (idx < 0) throw new RangeError(`Invalid negative index ${idx}`) - return this.items[idx] + fill(value: T | NodeOf, start?: number, end?: number): this { + return super.fill(nodeValue(this.schema, value), start, end) + } + + /** @private */ + _push(item: NodeOf): void { + super.push(item) } /** - * Checks if the collection includes a value with the key `key`. + * Append new elements to this sequence, and return its new length. * - * Throws if `idx` is not a non-negative integer. + * Non-node values are converted to Node values. */ - has(idx: number): boolean { - if (!Number.isInteger(idx)) - throw new TypeError(`Expected an integer, not ${JSON.stringify(idx)}.`) - if (idx < 0) throw new RangeError(`Invalid negative index ${idx}`) - return idx < this.items.length + push(...values: Array>): number { + return super.push(...nodeValues(this.schema, values)) } /** - * Sets a value in this collection. For `!!set`, `value` needs to be a - * boolean to add/remove the item from the set. + * Set a value in this sequence. * - * Throws if `idx` is not a non-negative integer. + * Non-node values are converted to Node values. */ set( idx: number, - value: T, + value: T | NodeOf, options?: Omit ): void { if (!Number.isInteger(idx)) throw new TypeError(`Expected an integer, not ${JSON.stringify(idx)}.`) - if (idx < 0) throw new RangeError(`Invalid negative index ${idx}`) - const prev = this.items[idx] + const prev = this.at(idx) if (prev instanceof Scalar && isScalarValue(value)) prev.value = value - else if (isNode(value)) this.items[idx] = value as NodeOf - else if (!this.schema) throw new Error('Schema is required') else { - const nc = new NodeCreator(this.schema, { - ...options, - aliasDuplicateObjects: false - }) - this.items[idx] = nc.create(value) as NodeOf - nc.setAnchors() + if (idx < 0) { + if (idx < -this.length) throw new RangeError(`Invalid index ${idx}`) + idx += this.length + } + this[idx] = nodeValue(this.schema, value, options) } } + /** + * Changes the contents of this sequence by removing or replacing existing elements + * and/or adding new elements in place. + * + * Non-node values are converted to Node values. + */ + splice( + start: number, + deleteCount?: number, + ...values: Array> + ): NodeOf[] { + const nv = nodeValues(this.schema, values) + return arguments.length < 2 + ? super.splice(start) + : super.splice(start, Number(deleteCount), ...nv) + } + + /** + * Prepend new elements to this sequence, and return its new length. + * + * Non-node values are converted to Node values. + */ + unshift(...values: Array>): number { + return super.unshift(...nodeValues(this.schema, values)) + } + /** A plain JavaScript representation of this node. */ toJS(doc: Document, ctx?: ToJSContext): any[] { ctx ??= new ToJSContext() - const res: unknown[] = [] - if (this.anchor) ctx.setAnchor(this, res) - for (const item of this.items) res.push(item.toJS(doc, ctx)) - return res + if (this.anchor) { + const res: unknown[] = [] + if (this.anchor) ctx.setAnchor(this, res) + for (const item of this) res.push(item.toJS(doc, ctx)) + return res + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return Array.from(this, item => item.toJS(doc, ctx)) } toString( @@ -125,27 +200,166 @@ export class YAMLSeq< onChompKeep?: () => void ): string { if (!ctx) return JSON.stringify(this) - return stringifyCollection(this, ctx, { - blockItemPrefix: '- ', - flowChars: { start: '[', end: ']' }, - itemIndent: (ctx.indent || '') + ' ', - onChompKeep, - onComment - }) + return (ctx.inFlow ?? this.flow) + ? this.#stringifyFlowSeq(ctx) + : this.#stringifyBlockSeq(ctx, onComment, onChompKeep) } - static from(nc: NodeCreator, obj: unknown): YAMLSeq { - const seq = new this(nc.schema) - if (obj && Symbol.iterator in Object(obj)) { - let i = 0 - for (let it of obj as Iterable) { - if (typeof nc.replacer === 'function') { - const key = obj instanceof Set ? it : String(i++) - it = nc.replacer.call(obj, key, it) + #stringifyBlockSeq( + ctx: StringifyContext, + onComment?: () => void, + onChompKeep?: () => void + ) { + const { + indent, + options: { commentString } + } = ctx + const itemIndent = indent + ' ' + const itemCtx = { ...ctx, indent: itemIndent } + + let chompKeep = false // flag for the preceding node's status + const lines: string[] = [] + for (let i = 0; i < this.length; ++i) { + const item = this[i] + let comment: string | null = null + if (item instanceof Pair) { + if (!chompKeep && item.key.spaceBefore) lines.push('') + addCommentBefore(ctx, lines, item.key.commentBefore, chompKeep) + } else if (item) { + if (!chompKeep && item.spaceBefore) lines.push('') + addCommentBefore(ctx, lines, item.commentBefore, chompKeep) + if (item.comment) comment = item.comment + } + + chompKeep = false + let str = stringify( + item, + itemCtx, + () => (comment = null), + () => (chompKeep = true) + ) + if (comment) str += lineComment(str, itemIndent, commentString(comment)) + if (chompKeep && comment) chompKeep = false + lines.push(`- ${str}`) + } + + let str: string + if (lines.length === 0) { + str = '[]' + } else { + str = lines[0] + for (let i = 1; i < lines.length; ++i) { + const line = lines[i] + str += line ? `\n${indent}${line}` : '\n' + } + } + + if (this.comment) { + str += '\n' + indentComment(commentString(this.comment), indent) + onComment?.() + } else if (chompKeep) onChompKeep?.() + + return str + } + + #stringifyFlowSeq(ctx: StringifyContext) { + const { + indent, + indentStep, + flowCollectionPadding: fcPadding, + options: { commentString } + } = ctx + const itemIndent = indent + ' ' + indentStep + const itemCtx = { ...ctx, indent: itemIndent, inFlow: true } + + let reqNewline = false + let linesAtValue = 0 + const lines: string[] = [] + for (let i = 0; i < this.length; ++i) { + const item = this[i] + let comment: string | null = null + if (item instanceof Pair) { + const ik = item.key + if (ik.spaceBefore) lines.push('') + addCommentBefore(ctx, lines, ik.commentBefore, false) + if (ik.comment) reqNewline = true + + const iv = item.value + if (iv) { + if (iv.comment) comment = iv.comment + if (iv.commentBefore) reqNewline = true + } else if (ik?.comment) { + comment = ik.comment + } + } else if (item) { + if (item.spaceBefore) lines.push('') + addCommentBefore(ctx, lines, item.commentBefore, false) + if (item.comment) comment = item.comment + } + + if (comment) reqNewline = true + let str = stringify(item, itemCtx, () => (comment = null)) + reqNewline ||= lines.length > linesAtValue || str.includes('\n') + if (i < this.length - 1) { + str += ',' + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline ||= + lines.reduce((sum, line) => sum + line.length + 2, 2) + + (str.length + 2) > + ctx.options.lineWidth + } + if (reqNewline) { + str += ',' } - seq.items.push(nc.create(it)) } + if (comment) str += lineComment(str, itemIndent, commentString(comment)) + lines.push(str) + linesAtValue = lines.length } - return seq + + if (lines.length === 0) return '[]' + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2) + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth + } + if (reqNewline) { + let str = '[' + for (const line of lines) + str += line ? `\n${indentStep}${indent}${line}` : '\n' + return `${str}\n${indent}]` + } + return `[${fcPadding}${lines.join(' ')}${fcPadding}]` + } +} +function nodeValue( + schema: Schema, + value: T | NodeOf, + options?: CreateNodeOptions +) { + if (isNode(value) || value instanceof Pair) return value as NodeOf + const nc = new NodeCreator(schema, options) + return nc.create(value) as NodeOf +} + +function nodeValues(schema: Schema, values: Array>) { + let nc: NodeCreator | undefined + return values.map(value => { + if (isNode(value) || value instanceof Pair) return value + nc ??= new NodeCreator(schema) + return nc.create(value) + }) as NodeOf[] +} + +function addCommentBefore( + { indent, options: { commentString } }: StringifyContext, + lines: string[], + comment: string | null | undefined, + chompKeep: boolean +) { + if (comment && chompKeep) comment = comment.replace(/^\n+/, '') + if (comment) { + const ic = indentComment(commentString(comment), indent) + lines.push(ic.trimStart()) // Avoid double indent on first line } } diff --git a/src/nodes/YAMLSet.ts b/src/nodes/YAMLSet.ts new file mode 100644 index 00000000..56faae12 --- /dev/null +++ b/src/nodes/YAMLSet.ts @@ -0,0 +1,280 @@ +import type { Document, DocValue } from '../doc/Document.ts' +import { NodeCreator } from '../doc/NodeCreator.ts' +import type { CreateNodeOptions } from '../options.ts' +import type { BlockMap, FlowCollection } from '../parse/cst.ts' +import type { Schema } from '../schema/Schema.ts' +import { stringify, type StringifyContext } from '../stringify/stringify.ts' +import { indentComment, lineComment } from '../stringify/stringifyComment.ts' +import { isCollection, isNode } from './identity.ts' +import { Pair } from './Pair.ts' +import { ToJSContext } from './toJS.ts' +import type { CollectionBase, Node, NodeOf, Primitive, Range } from './types.ts' +import { cloneMapOrSet } from './util-clone-map-or-set.ts' + +export class YAMLSet< + T extends Primitive | Node = Primitive | Node +> implements CollectionBase { + static readonly tagName = 'tag:yaml.org,2002:set' + + declare schema: Schema + + /** A fully qualified tag */ + tag = 'tag:yaml.org,2002:set' + + values: Map> = new Map() + + /** An optional anchor on this set. Used by alias nodes. */ + declare anchor?: string + + /** If true, stringify this and all child nodes using flow rather than block styles. */ + declare flow?: boolean + + /** A comment on or immediately after this set. */ + declare comment?: string | null + + /** A comment before this set. */ + declare commentBefore?: string | null + + /** + * The `[start, value-end, node-end]` character offsets for + * the part of the source parsed into this set (undefined if not parsed). + * The `value-end` and `node-end` positions are themselves not included in their respective ranges. + */ + declare range?: Range | null + + /** A blank line before this set and its commentBefore */ + declare spaceBefore?: boolean + + /** The CST token that was composed into this set. */ + declare srcToken?: BlockMap | FlowCollection + + constructor(schema: Schema) { + Object.defineProperty(this, 'schema', { + value: schema, + configurable: true, + enumerable: false, + writable: true + }) + } + + get size(): number { + return this.values.size + } + + add( + value: T | NodeOf, + options?: Omit + ): this { + if (this.has(value)) return this + + const mk = this.schema.mapKey(value) + const node = isNode(value) + ? value + : new NodeCreator(this.schema, options).create(value) + this.values.set(mk, node as NodeOf) + return this + } + + /** + * Create a copy of this set. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema?: Schema): this { + return cloneMapOrSet(this, schema) + } + + /** + * Remove `value` from the set. + * @returns `true` if the item was found and removed. + */ + delete(value: T | NodeOf): boolean { + const mk = this.schema.mapKey(value) + if (this.values.delete(mk)) return true + if (isNode(value)) { + for (const [k, v] of this.values) { + if (v === value) return this.values.delete(k) + } + } + return false + } + + /** Return the node matching `value`, if the set includes it. */ + get(value: T | NodeOf): NodeOf | undefined { + const mk = this.schema.mapKey(value) + const node = this.values.get(mk) + if (node) return node + if (isNode(value)) { + for (const v of this.values.values()) if (v === value) return v + } + return undefined + } + + /** Check if the set includes `value`. */ + has(value: T | NodeOf): boolean { + const mk = this.schema.mapKey(value) + if (this.values.has(mk)) return true + if (isNode(value)) { + for (const v of this.values.values()) if (v === value) return true + } + return false + } + + /** + * Return the internal Map key matching `value`, or `undefined` if not found. + * + * @param allowMissing - If `true`, a key is always returned, + * even if the value is not in the set. + */ + keyOf(value: T | NodeOf, allowMissing = false): unknown { + const mk = this.schema.mapKey(value) + if (allowMissing || this.values.has(mk)) return mk + if (isNode(value)) { + for (const [k, v] of this.values) if (v === value) return k + } + return undefined + } + + /** A plain JavaScript representation of this set. */ + toJS(doc: Document, ctx?: ToJSContext): Set { + ctx ??= new ToJSContext() + const set = new Set() + if (this.anchor) ctx.setAnchor(this, set) + for (const item of this.values.values()) set.add(item.toJS(doc, ctx)) + return set + } + + toString( + ctx?: StringifyContext, + onComment?: () => void, + onChompKeep?: () => void + ): string { + if (!ctx) return JSON.stringify(this) + let items: Iterable> = this.values.values() + if (ctx.sortMapEntries) { + items = Array.from(items, item => new Pair(item)) + .sort(ctx.sortMapEntries) + .map(pair => pair.key) + } + if (ctx.inFlow ?? this.flow) { + return this.#stringifyFlowSet(ctx, items) + } else { + return this.#stringifyBlockSet(ctx, items, onComment, onChompKeep) + } + } + + #stringifyBlockSet( + ctx: StringifyContext, + items: Iterable>, + onComment?: () => void, + onChompKeep?: () => void + ) { + const { + indent, + indentStep, + options: { commentString } + } = ctx + const itemCtx = { + ...ctx, + implicitKey: false, + indent: indent + indentStep + } satisfies StringifyContext + + let chompKeep = false // flag for the preceding node's status + let res: string | null = null + for (const item of items) { + if (!chompKeep && item.spaceBefore) { + if (typeof res === 'string') res += '\n' + else res = '' + } + let cb = item.commentBefore + if (cb && chompKeep) cb = cb.replace(/^\n+/, '') + if (cb) { + const ic = indentComment(commentString(cb), indent).trimStart() + if (typeof res === 'string') res += `\n${indent}${ic}` + else res = ic + } + + chompKeep = false + let comment = item.comment + let str = stringify( + item, + itemCtx, + () => (comment = null), + () => (chompKeep = true) + ) + str = str ? `? ${str}` : '?' + if (comment) { + str += lineComment(str, itemCtx.indent, commentString(comment)) + chompKeep = false + } + if (typeof res === 'string') res += `\n${indent}${str}` + else res = str + } + + if (this.comment) { + const cs = commentString(this.comment) + if (typeof res === 'string') res += '\n' + indentComment(cs, indent) + else res = '{}' + lineComment('', indent, cs) + onComment?.() + } else if (chompKeep) onChompKeep?.() + + return res ?? '{}' + } + + #stringifyFlowSet(ctx: StringifyContext, items: Iterable>) { + const { + indent, + indentStep, + flowCollectionPadding: fcPadding, + options: { commentString, lineWidth, trailingComma } + } = ctx + const itemCtx = { + ...ctx, + indent: indent + indentStep + indentStep, + inFlow: true + } satisfies StringifyContext + + let reqNewline = false + const lines: string[] = [] + let itemsLeft = this.values.size + let singleLineWidth = 2 * fcPadding.length // '{}' + ' ' + ' ' - ', ' + for (const item of items) { + itemsLeft -= 1 + + if (item.spaceBefore) { + lines.push('') + reqNewline = true + } + if (item.commentBefore) { + const ic = indentComment(commentString(item.commentBefore), indent) + lines.push(ic.trimStart()) // Avoid double indent on first line + reqNewline = true + } + let comment = item.comment + if (comment) reqNewline = true + + itemCtx.implicitKey = !isCollection(item) + let str = stringify(item, itemCtx, () => (comment = null)) || '?' + if (isCollection(item)) str = `? ${str}` + + reqNewline ||= str.includes('\n') + if (!reqNewline && lineWidth > 0) { + singleLineWidth += str.length + 2 // str + ', ' + if (singleLineWidth > lineWidth) reqNewline = true + } + + if (itemsLeft > 0 || (trailingComma && reqNewline)) str += ',' + if (comment) + str += lineComment(str, indent + indentStep, commentString(comment)) + lines.push(str) + } + + if (lines.length === 0) return '{}' + if (!reqNewline) return `{${fcPadding}${lines.join(' ')}${fcPadding}}` + let str = '{' + for (const line of lines) + str += line ? `\n${indentStep}${indent}${line}` : '\n' + return `${str}\n${indent}}` + } +} diff --git a/src/nodes/addPairToJSMap.ts b/src/nodes/addPairToJSMap.ts index fd676b64..0493287a 100644 --- a/src/nodes/addPairToJSMap.ts +++ b/src/nodes/addPairToJSMap.ts @@ -2,9 +2,9 @@ import type { Document, DocValue } from '../doc/Document.ts' import { warn } from '../log.ts' import { addMergeToJSMap, isMergeKey } from '../schema/yaml-1.1/merge.ts' import { createStringifyContext } from '../stringify/stringify.ts' -import type { Node } from './Node.ts' import type { Pair } from './Pair.ts' import type { ToJSContext } from './toJS.ts' +import type { Node } from './types.ts' import type { MapLike } from './YAMLMap.ts' export function addPairToJSMap( diff --git a/src/nodes/identity.ts b/src/nodes/identity.ts index a3bb0314..a638207c 100644 --- a/src/nodes/identity.ts +++ b/src/nodes/identity.ts @@ -1,8 +1,20 @@ import { Alias } from './Alias.ts' -import { Collection } from './Collection.ts' -import type { Node } from './Node.ts' import { Scalar } from './Scalar.ts' +import type { Node } from './types.ts' +import { YAMLMap } from './YAMLMap.ts' +import { YAMLSeq } from './YAMLSeq.ts' +import { YAMLSet } from './YAMLSet.ts' + +/** Type predicate for collections */ +export const isCollection = ( + node: unknown +): node is YAMLMap | YAMLSeq | YAMLSet => + node instanceof YAMLMap || node instanceof YAMLSeq || node instanceof YAMLSet /** Type predicate for `Node` values */ export const isNode = (node: unknown): node is Node => - node instanceof Scalar || node instanceof Alias || node instanceof Collection + node instanceof Scalar || + node instanceof Alias || + node instanceof YAMLMap || + node instanceof YAMLSeq || + node instanceof YAMLSet diff --git a/src/nodes/toJS.ts b/src/nodes/toJS.ts index 316495b5..36ce3674 100644 --- a/src/nodes/toJS.ts +++ b/src/nodes/toJS.ts @@ -1,5 +1,5 @@ import type { ToJSOptions } from '../options.ts' -import type { Node } from './Node.ts' +import type { Node } from './types.ts' /** A context used in `node.toJS()` implementations */ export class ToJSContext { diff --git a/src/nodes/Node.ts b/src/nodes/types.ts similarity index 61% rename from src/nodes/Node.ts rename to src/nodes/types.ts index d656e111..fc8173b4 100644 --- a/src/nodes/Node.ts +++ b/src/nodes/types.ts @@ -7,8 +7,15 @@ import type { Scalar } from './Scalar.ts' import type { ToJSContext } from './toJS.ts' import type { YAMLMap } from './YAMLMap.ts' import type { YAMLSeq } from './YAMLSeq.ts' +import type { YAMLSet } from './YAMLSet.ts' -export type Node = Alias | Scalar | YAMLSeq | YAMLMap +export type Collection = YAMLMap | YAMLSeq | YAMLSet + +export type Node = Scalar | Collection | Alias + +export type Primitive = boolean | number | bigint | string | null + +export type NodeOf = T extends Primitive ? Scalar : T /** Utility type mapper */ export type NodeType = T extends @@ -23,9 +30,11 @@ export type NodeType = T extends ? Scalar : T extends Array ? YAMLSeq> - : T extends { [key: string | number]: any } - ? YAMLMap, NodeType> - : Node + : T extends Set + ? YAMLSet> + : T extends { [key: string | number]: any } + ? YAMLMap, NodeType> + : Node export type Range = [start: number, valueEnd: number, nodeEnd: number] @@ -53,8 +62,12 @@ export interface NodeBase { /** A fully qualified tag, if required */ tag?: string - /** Create a copy of this node. */ - clone(_schema?: Schema): this + /** + * Create a copy of this node. + * + * @param schema - If defined, overwrites the original's schema for cloned collections. + */ + clone(schema?: Schema): this /** A plain JavaScript representation of this node. */ toJS(doc: Document, opt?: ToJSContext): any @@ -65,3 +78,19 @@ export interface NodeBase { onChompKeep?: () => void ): string } + +export interface CollectionBase extends NodeBase { + schema: Schema + + /** An optional anchor on this collection. Used by alias nodes. */ + anchor?: string + + /** If true, stringify this and all child nodes using flow styles. */ + flow?: boolean + + /** The number of items in this collection. */ + readonly size: number + + /** Create a deep copy of this collection */ + clone(schema?: Schema): this +} diff --git a/src/nodes/util-clone-map-or-set.ts b/src/nodes/util-clone-map-or-set.ts new file mode 100644 index 00000000..a2c216c3 --- /dev/null +++ b/src/nodes/util-clone-map-or-set.ts @@ -0,0 +1,20 @@ +import type { Schema } from '../schema/Schema.ts' +import type { YAMLMap } from './YAMLMap.ts' +import type { YAMLSet } from './YAMLSet.ts' + +export function cloneMapOrSet( + coll: T, + schema?: Schema +): T { + schema ??= coll.schema + const copy = new (coll.constructor as typeof YAMLMap)(schema) as T + for (const [key, value] of coll.values) { + copy.values.set(key, value.clone(schema) as any) + } + if (coll.range) copy.range = [...coll.range] + const propDesc = Object.getOwnPropertyDescriptors(coll) + for (const [name, prop] of Object.entries(propDesc)) { + if (!(name in copy)) Object.defineProperty(copy, name, prop) + } + return copy +} diff --git a/src/options.ts b/src/options.ts index 4aeed603..0dcd2bdd 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,7 +1,6 @@ import type { Reviver } from './doc/applyReviver.ts' import type { Directives } from './doc/directives.ts' import type { LogLevelId } from './log.ts' -import type { Node } from './nodes/Node.ts' import type { Pair } from './nodes/Pair.ts' import type { Scalar } from './nodes/Scalar.ts' import type { LineCounter } from './parse/line-counter.ts' @@ -54,20 +53,6 @@ export type ParseOptions = { * Default: `false` */ stringKeys?: boolean - - /** - * YAML requires map keys to be unique. By default, this is checked by - * comparing scalar values with `===`; deep equality is not checked for - * aliases or collections. If merge keys are enabled by the schema, - * multiple `<<` keys are allowed. - * - * Set `false` to disable, or provide your own comparator function to - * customise. The comparator will be passed two node values, and - * is expected to return a `boolean` indicating their equality. - * - * Default: `true` - */ - uniqueKeys?: boolean | ((a: Node, b: Node) => boolean) } export type DocumentOptions = { @@ -109,6 +94,17 @@ export type SchemaOptions = { */ customTags?: Tags | ((tags: Tags) => Tags) | null + /** + * Determine an internal Map key representation for map and set values, + * which is used for detecting duplicates and to identify values. + * + * Key equality is based on the SameValueZero algorithm. + * + * If merge keys are enabled by the schema, + * multiple `<<` keys are each considered unique. + */ + mapKey?: (value: unknown) => unknown + /** * Enable support for `<<` merge keys. * @@ -142,15 +138,6 @@ export type SchemaOptions = { */ schema?: string | Schema - /** - * When adding to or stringifying a map, sort the entries. - * If `true`, sort by comparing key values with `<`. - * Does not affect item order when parsing. - * - * Default: `false` - */ - sortMapEntries?: boolean | ((a: Pair, b: Pair) => number) - /** * Override default values for `toString()` options. */ @@ -371,6 +358,14 @@ export type ToStringOptions = { */ singleQuote?: boolean | null + /** + * When stringifying a map or a set, sort the entries. + * If `true`, sort by comparing key values with `<`. + * + * Default: `false` + */ + sortMapEntries?: boolean | ((a: Pair, b: Pair) => number) + /** * Add a trailing comma after the last entry in a flow map or flow sequence that's split across multiple lines. * diff --git a/src/parse/cst-scalar.ts b/src/parse/cst-scalar.ts index bbe2aa54..ad1ebf0a 100644 --- a/src/parse/cst-scalar.ts +++ b/src/parse/cst-scalar.ts @@ -4,7 +4,7 @@ import { resolveBlockScalar } from '../compose/resolve-block-scalar.ts' import { resolveFlowScalar } from '../compose/resolve-flow-scalar.ts' import type { ErrorCode } from '../errors.ts' import { YAMLParseError } from '../errors.ts' -import type { Range } from '../nodes/Node.ts' +import type { Range } from '../nodes/types.ts' import type { Scalar } from '../nodes/Scalar.ts' import type { StringifyContext } from '../stringify/stringify.ts' import { stringifyString } from '../stringify/stringifyString.ts' diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index 47b400fa..a9d563fb 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -1,4 +1,5 @@ -import type { Pair } from '../nodes/Pair.ts' +import { Pair } from '../nodes/Pair.ts' +import { Scalar } from '../nodes/Scalar.ts' import type { SchemaOptions, ToStringOptions } from '../options.ts' import { map } from './common/map.ts' import { seq } from './common/seq.ts' @@ -6,32 +7,29 @@ import { string } from './common/string.ts' import { coreKnownTags, getTags } from './tags.ts' import type { CollectionTag, ScalarTag } from './types.ts' -const sortMapEntriesByKey = (a: Pair, b: Pair) => - a.key < b.key ? -1 : a.key > b.key ? 1 : 0 - export class Schema { compat: Array | null knownTags: Record + mapKey: (value: unknown) => unknown name: string - sortMapEntries: ((a: Pair, b: Pair) => number) | null tags: Array toStringOptions: Readonly | null // These are used by createNode() and composeScalar() /** @internal */ - declare readonly map: CollectionTag + declare readonly map: typeof map /** @internal */ - declare readonly scalar: ScalarTag + declare readonly scalar: typeof string /** @internal */ - declare readonly seq: CollectionTag + declare readonly seq: typeof seq constructor({ compat, customTags, + mapKey, merge, resolveKnownTags, schema, - sortMapEntries, toStringDefaults }: SchemaOptions) { this.compat = Array.isArray(compat) @@ -39,6 +37,7 @@ export class Schema { : compat ? getTags(null, compat) : null + this.mapKey = mapKey ?? defaultMapKey this.name = (typeof schema === 'string' && schema) || 'core' this.knownTags = resolveKnownTags ? coreKnownTags : {} this.tags = getTags(customTags, this.name, merge) @@ -47,14 +46,6 @@ export class Schema { Object.defineProperty(this, 'map', { value: map }) Object.defineProperty(this, 'scalar', { value: string }) Object.defineProperty(this, 'seq', { value: seq }) - - // Used by createMap() - this.sortMapEntries = - typeof sortMapEntries === 'function' - ? sortMapEntries - : sortMapEntries === true - ? sortMapEntriesByKey - : null } clone(): Schema { @@ -66,3 +57,9 @@ export class Schema { return copy } } + +function defaultMapKey(value: unknown): unknown { + if (value instanceof Pair) value = value.key + if (value instanceof Scalar) value = value.value + return value ?? null +} diff --git a/src/schema/common/map.ts b/src/schema/common/map.ts index 73c97d6c..da62ce32 100644 --- a/src/schema/common/map.ts +++ b/src/schema/common/map.ts @@ -1,13 +1,30 @@ +import type { NodeCreator } from '../../doc/NodeCreator.ts' +import type { Collection } from '../../nodes/types.ts' import { YAMLMap } from '../../nodes/YAMLMap.ts' import type { CollectionTag } from '../types.ts' -export const map: CollectionTag = { +export const map: { + collection: 'map' + default: true + nodeClass: typeof YAMLMap + tag: string + createNode(nc: NodeCreator, obj: unknown): YAMLMap + resolve( + map: Collection, + onError: (message: string) => void + ): YAMLMap +} = { collection: 'map', default: true, nodeClass: YAMLMap, tag: 'tag:yaml.org,2002:map', + + createNode(nc, obj): YAMLMap { + return YAMLMap.create(nc, obj) + }, + resolve(map, onError) { if (!(map instanceof YAMLMap)) onError('Expected a mapping for this tag') - return map + return map as YAMLMap } -} +} satisfies CollectionTag diff --git a/src/schema/common/seq.ts b/src/schema/common/seq.ts index 5a01192f..fd423c47 100644 --- a/src/schema/common/seq.ts +++ b/src/schema/common/seq.ts @@ -1,13 +1,27 @@ +import type { NodeCreator } from '../../doc/NodeCreator.ts' +import type { Collection } from '../../nodes/types.ts' import { YAMLSeq } from '../../nodes/YAMLSeq.ts' import type { CollectionTag } from '../types.ts' -export const seq: CollectionTag = { +export const seq: { + collection: 'seq' + default: true + nodeClass: typeof YAMLSeq + tag: string + createNode(nc: NodeCreator, obj: unknown): YAMLSeq + resolve(seq: Collection, onError: (message: string) => void): YAMLSeq +} = { collection: 'seq', default: true, nodeClass: YAMLSeq, tag: 'tag:yaml.org,2002:seq', - resolve(seq, onError) { + + createNode(nc, obj): YAMLSeq { + return YAMLSeq.create(nc, obj) + }, + + resolve(seq, onError): YAMLSeq { if (!(seq instanceof YAMLSeq)) onError('Expected a sequence for this tag') - return seq + return seq as YAMLSeq } -} +} satisfies CollectionTag diff --git a/src/schema/types.ts b/src/schema/types.ts index 042584df..25567057 100644 --- a/src/schema/types.ts +++ b/src/schema/types.ts @@ -1,8 +1,7 @@ import type { NodeCreator } from '../doc/NodeCreator.ts' -import type { Node } from '../nodes/Node.ts' +import type { Collection } from '../nodes/types.ts' +import type { Node } from '../nodes/types.ts' import type { Scalar } from '../nodes/Scalar.ts' -import type { YAMLMap } from '../nodes/YAMLMap.ts' -import type { YAMLSeq } from '../nodes/YAMLSeq.ts' import type { ParseOptions } from '../options.ts' import type { StringifyContext } from '../stringify/stringify.ts' import type { Schema } from './Schema.ts' @@ -91,16 +90,14 @@ export interface CollectionTag extends TagBase { /** The source collection type supported by this tag. */ collection: 'map' | 'seq' + createNode: (nc: NodeCreator, value: unknown) => Node + /** * The `Node` child class that implements this tag. * If set, used to select this tag when stringifying. - * - * If the class provides a static `from` method, then that - * will be used if the tag object doesn't have a `createNode` method. */ nodeClass?: { - new (schema?: Schema): Node - from?: (nc: NodeCreator, obj: unknown) => Node + new (schema: Schema): Node } /** @@ -110,7 +107,7 @@ export interface CollectionTag extends TagBase { * Note: this is required if nodeClass is not provided. */ resolve?: ( - value: YAMLMap | YAMLSeq, + value: Collection, onError: (message: string) => void, options: ParseOptions ) => unknown diff --git a/src/schema/util-primitive-key.ts b/src/schema/util-primitive-key.ts new file mode 100644 index 00000000..e7e55c1c --- /dev/null +++ b/src/schema/util-primitive-key.ts @@ -0,0 +1,17 @@ +import type { Primitive } from '../nodes/types.ts' +import { Pair } from '../nodes/Pair.ts' +import { Scalar } from '../nodes/Scalar.ts' + +export function primitiveKey(value: unknown): Primitive | undefined { + let value_ = value instanceof Pair ? value.key : value + value_ = value instanceof Scalar ? value.value : value + switch (typeof value_) { + case 'bigint': + case 'boolean': + case 'number': + case 'string': + return value_ + default: + return value_ ? undefined : null + } +} diff --git a/src/schema/yaml-1.1/merge.ts b/src/schema/yaml-1.1/merge.ts index f6bca109..7c33b59f 100644 --- a/src/schema/yaml-1.1/merge.ts +++ b/src/schema/yaml-1.1/merge.ts @@ -2,8 +2,7 @@ import type { Document, DocValue } from '../../doc/Document.ts' import { Alias } from '../../nodes/Alias.ts' import { Scalar } from '../../nodes/Scalar.ts' import type { ToJSContext } from '../../nodes/toJS.ts' -import type { MapLike, YAMLMap } from '../../nodes/YAMLMap.ts' -import { YAMLSeq } from '../../nodes/YAMLSeq.ts' +import { type MapLike, YAMLMap } from '../../nodes/YAMLMap.ts' import type { ScalarTag } from '../types.ts' // If the value associated with a merge key is a single mapping node, each of @@ -50,11 +49,11 @@ export function addMergeToJSMap( value: unknown ): void { value = ctx && value instanceof Alias ? value.resolve(doc, ctx) : value - if (value instanceof YAMLSeq) - for (const it of value.items) mergeValue(doc, ctx, map, it) - else if (Array.isArray(value)) + if (Array.isArray(value) && !(value instanceof YAMLMap)) { for (const it of value) mergeValue(doc, ctx, map, it) - else mergeValue(doc, ctx, map, value) + } else { + mergeValue(doc, ctx, map, value) + } } function mergeValue( diff --git a/src/schema/yaml-1.1/omap.ts b/src/schema/yaml-1.1/omap.ts index e65dc1aa..e1318c92 100644 --- a/src/schema/yaml-1.1/omap.ts +++ b/src/schema/yaml-1.1/omap.ts @@ -1,12 +1,10 @@ import type { Document, DocValue } from '../../doc/Document.ts' -import type { Primitive } from '../../nodes/Collection.ts' -import type { Node } from '../../nodes/Node.ts' -import type { Pair } from '../../nodes/Pair.ts' +import { Pair } from '../../nodes/Pair.ts' import { Scalar } from '../../nodes/Scalar.ts' import type { ToJSContext } from '../../nodes/toJS.ts' -import { YAMLMap } from '../../nodes/YAMLMap.ts' +import type { Node, Primitive } from '../../nodes/types.ts' import { YAMLSeq } from '../../nodes/YAMLSeq.ts' -import type { NodeCreator } from '../../util.ts' +import type { StringifyContext } from '../../util.ts' import type { Schema } from '../Schema.ts' import type { CollectionTag } from '../types.ts' import { createPairs, resolvePairs } from './pairs.ts' @@ -15,44 +13,70 @@ export class YAMLOMap< K extends Primitive | Node = Primitive | Node, V extends Primitive | Node = Primitive | Node > extends YAMLSeq> { - static tag = 'tag:yaml.org,2002:omap' + constructor(schema: Schema, elements?: Array>) { + super(schema, elements) + this.tag = omap.tag + } - constructor(schema?: Schema) { - super(schema) - this.tag = YAMLOMap.tag + /** + * Append new pairs to the omap, and return its new length. + */ + push(...pairs: Pair[]): number { + for (const pair of pairs) { + if (pair instanceof Pair) super.push(pair) + else throw new Error('Omap only supports Pair values') + } + return this.length } - add: typeof YAMLMap.prototype.add = YAMLMap.prototype.add.bind(this) - delete: typeof YAMLMap.prototype.delete = YAMLMap.prototype.delete.bind(this) - get: typeof YAMLMap.prototype.get = YAMLMap.prototype.get.bind(this) - has: typeof YAMLMap.prototype.has = YAMLMap.prototype.has.bind(this) - set: typeof YAMLMap.prototype.set = YAMLMap.prototype.set.bind(this) + /** + * Set a pair in this omap. + * + * Throws if `idx` is not an integer. + */ + set(idx: number, pair: Pair): void { + if (!Number.isInteger(idx)) + throw new TypeError(`Expected an integer, not ${JSON.stringify(idx)}.`) + if (idx < 0) { + if (idx < -this.length) throw new RangeError(`Invalid index ${idx}`) + idx += this.length + } + this[idx] = pair + } /** - * If `ctx` is given, the return type is always `Map`, + * The returned value actually has type `Map`, * but TypeScript won't allow widening the signature of a child method. */ - toJS(doc: Document, ctx?: ToJSContext): unknown[] { - if (!ctx) return super.toJS(doc) + toJS(doc: Document, ctx?: ToJSContext): never[] { + if (!ctx) return super.toJS(doc) as never[] const map = new Map() if (this.anchor) { ctx.anchors.set(this, { aliasCount: 0, count: 1, res: map }) } - for (const pair of this.items) { + for (const pair of this) { const key = pair.key.toJS(doc, ctx) - const value = pair.value ? pair.value.toJS(doc, ctx) : pair.value + const value = pair.value ? pair.value.toJS(doc, ctx) : null if (map.has(key)) throw new Error('Ordered maps must not include duplicate keys') map.set(key, value) } - return map as unknown as unknown[] + return map as unknown as never[] } - static from(nc: NodeCreator, iterable: unknown): YAMLOMap { - const pairs = createPairs(nc, iterable) - const omap = new this(nc.schema) - omap.items = pairs.items - return omap + toString( + ctx?: StringifyContext, + onComment?: () => void, + onChompKeep?: () => void + ): string { + const keys = new Set() + for (const pair of this) { + const key = this.schema.mapKey(pair) + if (keys.has(key)) + throw new Error('Ordered maps must not include duplicate keys') + keys.add(key) + } + return super.toString(ctx, onComment, onChompKeep) } } @@ -63,10 +87,15 @@ export const omap: CollectionTag = { default: false, tag: 'tag:yaml.org,2002:omap', + createNode(nc, iterable) { + const pairs = createPairs(nc, iterable) + return new YAMLOMap(nc.schema, pairs) + }, + resolve(seq, onError) { const pairs = resolvePairs(seq, onError) const seenKeys: unknown[] = [] - for (const { key } of pairs.items) { + for (const { key } of pairs) { if (key instanceof Scalar) { if (seenKeys.includes(key.value)) { onError(`Ordered maps must not include duplicate keys: ${key.value}`) @@ -75,6 +104,6 @@ export const omap: CollectionTag = { } } } - return Object.assign(new YAMLOMap(), pairs) + return Object.assign(new YAMLOMap(seq.schema), pairs) } } diff --git a/src/schema/yaml-1.1/pairs.ts b/src/schema/yaml-1.1/pairs.ts index 293ca388..c12647e1 100644 --- a/src/schema/yaml-1.1/pairs.ts +++ b/src/schema/yaml-1.1/pairs.ts @@ -1,5 +1,6 @@ import type { NodeCreator } from '../../doc/NodeCreator.ts' -import type { Node } from '../../nodes/Node.ts' +import type { Collection } from '../../nodes/types.ts' +import type { Node } from '../../nodes/types.ts' import { Pair } from '../../nodes/Pair.ts' import { Scalar } from '../../nodes/Scalar.ts' import { YAMLMap } from '../../nodes/YAMLMap.ts' @@ -7,17 +8,18 @@ import { YAMLSeq } from '../../nodes/YAMLSeq.ts' import type { CollectionTag } from '../types.ts' export function resolvePairs( - seq: YAMLSeq | YAMLMap, + seq: Collection, onError: (message: string) => void ): YAMLSeq { if (seq instanceof YAMLSeq) { - for (let i = 0; i < seq.items.length; ++i) { - const item = seq.items[i] + for (let i = 0; i < seq.length; ++i) { + const item = seq[i] if (item instanceof Pair) continue - else if (item instanceof YAMLMap) { - if (item.items.length > 1) + if (item instanceof YAMLMap) { + if (item.size > 1) onError('Each pair must have its own sequence indicator') - const pair = item.items[0] || new Pair(new Scalar(null)) + const pair = + item.values.values().next().value ?? new Pair(new Scalar(null)) if (item.commentBefore) pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore}\n${pair.key.commentBefore}` @@ -28,9 +30,9 @@ export function resolvePairs( ? `${item.comment}\n${cn.comment}` : item.comment } - seq.items[i] = pair + seq[i] = pair } else { - seq.items[i] = new Pair(item, null) + seq[i] = new Pair(item, null) } } } else onError('Expected a sequence for this tag') @@ -64,7 +66,7 @@ export function createPairs(nc: NodeCreator, iterable: unknown): YAMLSeq { } else { key = it } - pairs.items.push(nc.createPair(key, value)) + pairs.push(nc.createPair(key, value)) } return pairs } diff --git a/src/schema/yaml-1.1/set.ts b/src/schema/yaml-1.1/set.ts index 2e47a55c..dce9767f 100644 --- a/src/schema/yaml-1.1/set.ts +++ b/src/schema/yaml-1.1/set.ts @@ -1,140 +1,30 @@ -import type { Document, DocValue } from '../../doc/Document.ts' -import { NodeCreator } from '../../doc/NodeCreator.ts' -import type { NodeOf, Primitive } from '../../nodes/Collection.ts' -import { isNode } from '../../nodes/identity.ts' -import type { Node } from '../../nodes/Node.ts' -import { Pair } from '../../nodes/Pair.ts' -import { Scalar } from '../../nodes/Scalar.ts' -import type { ToJSContext } from '../../nodes/toJS.ts' -import { findPair, YAMLMap } from '../../nodes/YAMLMap.ts' -import type { CreateNodeOptions } from '../../options.ts' -import type { Schema } from '../../schema/Schema.ts' -import type { StringifyContext } from '../../stringify/stringify.ts' +import type { NodeCreator } from '../../doc/NodeCreator.ts' +import { YAMLSet } from '../../nodes/YAMLSet.ts' import type { CollectionTag } from '../types.ts' -export class YAMLSet< - T extends Primitive | Node = Primitive | Node -> extends YAMLMap { - static tag = 'tag:yaml.org,2002:set' - - constructor(schema?: Schema) { - super(schema) - this.tag = YAMLSet.tag - } - - /** - * Add a value to the set. - * - * If `value` is a Pair, its `.value` must be null and `options` is ignored. - * - * If the set already includes a matching value, no value is added. - */ - add( - value: unknown, - options?: Omit - ): void { - if (!(value instanceof Pair)) { - this.set(value, true, options) - } else if (value.value !== null) { - throw new TypeError('set pair values must be null') - } else { - const prev = findPair(this.items, value.key) - if (!prev) this.items.push(value as Pair) - } - } - - /** - * Returns the value matching `key`. - */ - get(key: unknown): NodeOf | undefined { - const pair = findPair(this.items, key) - return pair?.key - } - - /** - * `value` needs to be true/false to add/remove the item from the set. - */ - set( - key: unknown, - value: boolean, - options?: Omit - ): void { - if (typeof value !== 'boolean') - throw new Error(`Expected a boolean value, not ${typeof value}`) - const prev = findPair(this.items, key) - if (prev && !value) { - this.items.splice(this.items.indexOf(prev), 1) - } else if (!prev && value) { - let node: Node - if (isNode(key)) { - node = key - } else if (!this.schema) { - throw new Error('Schema is required') - } else { - const nc = new NodeCreator(this.schema, { - ...options, - aliasDuplicateObjects: false - }) - node = nc.create(key) - nc.setAnchors() - } - this.items.push(new Pair(node as NodeOf)) - } - } - - toJS(doc: Document, ctx?: ToJSContext): any { - return super.toJS(doc, ctx, Set) - } - - toString( - ctx?: StringifyContext, - onComment?: () => void, - onChompKeep?: () => void - ): string { - if (!ctx) return JSON.stringify(this) - return super.toString({ ...ctx, noValues: true }, onComment, onChompKeep) - } +export const set: CollectionTag = { + collection: 'map', + identify: value => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: 'tag:yaml.org,2002:set', - static from(nc: NodeCreator, iterable: unknown): YAMLSet { - const set = new this(nc.schema) + createNode(nc: NodeCreator, iterable: unknown): YAMLSet { + const set = new YAMLSet(nc.schema) if (iterable && Symbol.iterator in Object(iterable)) for (let value of iterable as Iterable) { if (typeof nc.replacer === 'function') value = nc.replacer.call(iterable, value, value) - set.items.push(nc.createPair(value, null) as Pair) + const mk = nc.schema.mapKey(value) + set.values.set(mk, nc.create(value)) } return set - } -} - -const hasAllNullValues = (map: YAMLMap): boolean => - map.items.every( - ({ value }) => - value == null || - (value instanceof Scalar && - value.value == null && - !value.commentBefore && - !value.comment && - !value.tag) - ) + }, -export const set: CollectionTag = { - collection: 'map', - identify: value => value instanceof Set, - nodeClass: YAMLSet, - default: false, - tag: 'tag:yaml.org,2002:set', - resolve(map, onError) { - if (!(map instanceof YAMLMap)) { - onError('Expected a mapping for this tag') - return map - } else if (!hasAllNullValues(map)) { - onError('Set items must all have null values') - return map - } else { - const set = Object.assign(new YAMLSet(), map) - for (const pair of map.items) pair.value &&= null - return set + resolve(set, onError) { + if (!(set instanceof YAMLSet)) { + onError('Expected a set for this tag') } + return set } } diff --git a/src/stringify/stringify.ts b/src/stringify/stringify.ts index bedae69f..9253ede7 100644 --- a/src/stringify/stringify.ts +++ b/src/stringify/stringify.ts @@ -1,8 +1,7 @@ import { anchorIsValid } from '../doc/anchors.ts' import type { Document } from '../doc/Document.ts' import { Alias } from '../nodes/Alias.ts' -import { Collection } from '../nodes/Collection.ts' -import type { Node } from '../nodes/Node.ts' +import type { Node } from '../nodes/types.ts' import { Pair } from '../nodes/Pair.ts' import { Scalar } from '../nodes/Scalar.ts' import type { ToStringOptions } from '../options.ts' @@ -22,13 +21,16 @@ export type StringifyContext = { inFlow: boolean | null inStringifyKey?: boolean flowCollectionPadding: string - noValues?: boolean options: Readonly< Required> > resolvedAliases?: Set + sortMapEntries: ((a: Pair, b: Pair) => number) | null } +const sortMapEntriesByKey = (a: Pair, b: Pair) => + a.key < b.key ? -1 : a.key > b.key ? 1 : 0 + export function createStringifyContext( doc: Document, options: ToStringOptions @@ -50,6 +52,7 @@ export function createStringifyContext( nullStr: 'null', simpleKeys: false, singleQuote: null, + sortMapEntries: false, trailingComma: false, trueStr: 'true', verifyAliasOrder: true @@ -70,6 +73,13 @@ export function createStringifyContext( inFlow = null } + const sortMapEntries = + typeof opt.sortMapEntries === 'function' + ? opt.sortMapEntries + : opt.sortMapEntries === true + ? sortMapEntriesByKey + : null + return { anchors: new Set(), doc, @@ -77,7 +87,8 @@ export function createStringifyContext( indent: '', indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : ' ', inFlow, - options: opt + options: opt, + sortMapEntries } } @@ -120,8 +131,7 @@ function stringifyProps( ) { if (!doc.directives) return '' const props = [] - const anchor = - (node instanceof Scalar || node instanceof Collection) && node.anchor + const anchor = node.anchor if (anchor && anchorIsValid(anchor)) { anchors.add(anchor) props.push(`&${anchor}`) diff --git a/src/stringify/stringifyCollection.ts b/src/stringify/stringifyCollection.ts deleted file mode 100644 index e8ff4eb9..00000000 --- a/src/stringify/stringifyCollection.ts +++ /dev/null @@ -1,181 +0,0 @@ -import type { Collection } from '../nodes/Collection.ts' -import { Pair } from '../nodes/Pair.ts' -import type { StringifyContext } from './stringify.ts' -import { stringify } from './stringify.ts' -import { indentComment, lineComment } from './stringifyComment.ts' - -interface StringifyCollectionOptions { - blockItemPrefix: string - flowChars: { start: '{'; end: '}' } | { start: '['; end: ']' } - itemIndent: string - onChompKeep?: () => void - onComment?: () => void -} - -export function stringifyCollection( - collection: Readonly, - ctx: StringifyContext, - options: StringifyCollectionOptions -): string { - const flow = ctx.inFlow ?? collection.flow - const stringify = flow ? stringifyFlowCollection : stringifyBlockCollection - return stringify(collection, ctx, options) -} - -function stringifyBlockCollection( - { comment, items }: Readonly, - ctx: StringifyContext, - { - blockItemPrefix, - flowChars, - itemIndent, - onChompKeep, - onComment - }: StringifyCollectionOptions -) { - const { - indent, - options: { commentString } - } = ctx - const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }) - - let chompKeep = false // flag for the preceding node's status - const lines: string[] = [] - for (let i = 0; i < items.length; ++i) { - const item = items[i] - let comment: string | null = null - if (item instanceof Pair) { - if (!chompKeep && item.key.spaceBefore) lines.push('') - addCommentBefore(ctx, lines, item.key.commentBefore, chompKeep) - } else if (item) { - if (!chompKeep && item.spaceBefore) lines.push('') - addCommentBefore(ctx, lines, item.commentBefore, chompKeep) - if (item.comment) comment = item.comment - } - - chompKeep = false - let str = stringify( - item, - itemCtx, - () => (comment = null), - () => (chompKeep = true) - ) - if (comment) str += lineComment(str, itemIndent, commentString(comment)) - if (chompKeep && comment) chompKeep = false - lines.push(blockItemPrefix + str) - } - - let str: string - if (lines.length === 0) { - str = flowChars.start + flowChars.end - } else { - str = lines[0] - for (let i = 1; i < lines.length; ++i) { - const line = lines[i] - str += line ? `\n${indent}${line}` : '\n' - } - } - - if (comment) { - str += '\n' + indentComment(commentString(comment), indent) - if (onComment) onComment() - } else if (chompKeep && onChompKeep) onChompKeep() - - return str -} - -function stringifyFlowCollection( - { items }: Readonly, - ctx: StringifyContext, - { flowChars, itemIndent }: StringifyCollectionOptions -) { - const { - indent, - indentStep, - flowCollectionPadding: fcPadding, - options: { commentString } - } = ctx - itemIndent += indentStep - const itemCtx = Object.assign({}, ctx, { - indent: itemIndent, - inFlow: true, - type: null - }) - - let reqNewline = false - let linesAtValue = 0 - const lines: string[] = [] - for (let i = 0; i < items.length; ++i) { - const item = items[i] - let comment: string | null = null - if (item instanceof Pair) { - const ik = item.key - if (ik.spaceBefore) lines.push('') - addCommentBefore(ctx, lines, ik.commentBefore, false) - if (ik.comment) reqNewline = true - - const iv = item.value - if (iv) { - if (iv.comment) comment = iv.comment - if (iv.commentBefore) reqNewline = true - } else if (ik?.comment) { - comment = ik.comment - } - } else if (item) { - if (item.spaceBefore) lines.push('') - addCommentBefore(ctx, lines, item.commentBefore, false) - if (item.comment) comment = item.comment - } - - if (comment) reqNewline = true - let str = stringify(item, itemCtx, () => (comment = null)) - reqNewline ||= lines.length > linesAtValue || str.includes('\n') - if (i < items.length - 1) { - str += ',' - } else if (ctx.options.trailingComma) { - if (ctx.options.lineWidth > 0) { - reqNewline ||= - lines.reduce((sum, line) => sum + line.length + 2, 2) + - (str.length + 2) > - ctx.options.lineWidth - } - if (reqNewline) { - str += ',' - } - } - if (comment) str += lineComment(str, itemIndent, commentString(comment)) - lines.push(str) - linesAtValue = lines.length - } - - const { start, end } = flowChars - if (lines.length === 0) { - return start + end - } else { - if (!reqNewline) { - const len = lines.reduce((sum, line) => sum + line.length + 2, 2) - reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth - } - if (reqNewline) { - let str = start - for (const line of lines) - str += line ? `\n${indentStep}${indent}${line}` : '\n' - return `${str}\n${indent}${end}` - } else { - return `${start}${fcPadding}${lines.join(' ')}${fcPadding}${end}` - } - } -} - -function addCommentBefore( - { indent, options: { commentString } }: StringifyContext, - lines: string[], - comment: string | null | undefined, - chompKeep: boolean -) { - if (comment && chompKeep) comment = comment.replace(/^\n+/, '') - if (comment) { - const ic = indentComment(commentString(comment), indent) - lines.push(ic.trimStart()) // Avoid double indent on first line - } -} diff --git a/src/stringify/stringifyPair.ts b/src/stringify/stringifyPair.ts index 109cabbf..2bd243cc 100644 --- a/src/stringify/stringifyPair.ts +++ b/src/stringify/stringifyPair.ts @@ -1,4 +1,4 @@ -import { Collection } from '../nodes/Collection.ts' +import { isCollection } from '../nodes/identity.ts' import type { Pair } from '../nodes/Pair.ts' import { Scalar } from '../nodes/Scalar.ts' import { YAMLSeq } from '../nodes/YAMLSeq.ts' @@ -15,14 +15,13 @@ export function stringifyPair( const { indent, indentStep, - noValues, options: { commentString, indentSeq, simpleKeys } } = ctx if (simpleKeys) { if (key.comment) { throw new Error('With simple keys, key nodes cannot have comments') } - if (key instanceof Collection) { + if (isCollection(key)) { const msg = 'With simple keys, collection cannot be used as a key value' throw new Error(msg) } @@ -35,9 +34,8 @@ export function stringifyPair( ctx = { ...ctx, - implicitKey: !explicitKey && (simpleKeys || !noValues), - indent: indent + indentStep, - noValues: false + implicitKey: !explicitKey, + indent: indent + indentStep } let keyComment = key.comment let keyCommentDone = false @@ -58,17 +56,11 @@ export function stringifyPair( } if (ctx.inFlow) { - if (noValues || value == null) { + if (value == null) { if (keyCommentDone && onComment) onComment() - return str === '' - ? '?' - : explicitKey - ? `? ${str}` - : noValues - ? str - : `${str}:` + return str === '' ? '?' : explicitKey ? `? ${str}` : `${str}:` } - } else if ((noValues && !simpleKeys) || (value == null && explicitKey)) { + } else if (value == null && explicitKey) { str = `? ${str}` if (keyComment && !keyCommentDone) { str += lineComment(str, ctx.indent, commentString(keyComment)) @@ -134,11 +126,11 @@ export function stringifyPair( } else { ws += `\n${ctx.indent}` } - } else if (!explicitKey && value instanceof Collection) { + } else if (!explicitKey && isCollection(value)) { const vs0 = valueStr[0] const nl0 = valueStr.indexOf('\n') const hasNewline = nl0 !== -1 - const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0 + const flow = ctx.inFlow ?? value.flow ?? value.size === 0 if (hasNewline || !flow) { let hasPropsLine = false if (hasNewline && (vs0 === '&' || vs0 === '!')) { diff --git a/src/test-events.ts b/src/test-events.ts index 41645f27..8f0bf313 100644 --- a/src/test-events.ts +++ b/src/test-events.ts @@ -1,12 +1,12 @@ import type { Document } from './doc/Document.ts' import { Alias } from './nodes/Alias.ts' -import { Collection } from './nodes/Collection.ts' import { isNode } from './nodes/identity.ts' -import type { Node } from './nodes/Node.ts' +import type { Node } from './nodes/types.ts' import { Pair } from './nodes/Pair.ts' import { Scalar } from './nodes/Scalar.ts' import { YAMLMap } from './nodes/YAMLMap.ts' import { YAMLSeq } from './nodes/YAMLSeq.ts' +import { YAMLSet } from './nodes/YAMLSet.ts' import { parseAllDocuments } from './public-api.ts' import { visit } from './visit.ts' @@ -78,10 +78,7 @@ function addEvents( if (errPos !== -1 && isNode(node) && node.range![0] >= errPos) throw new Error() let props = '' - let anchor = - node instanceof Scalar || node instanceof Collection - ? node.anchor - : undefined + let anchor = isNode(node) ? node.anchor : undefined if (anchor) { if (/\d$/.test(anchor)) { const alt = anchor.replace(/\d$/, '') @@ -94,18 +91,26 @@ function addEvents( if (node instanceof YAMLMap) { const ev = node.flow ? '+MAP {}' : '+MAP' events.push(`${ev}${props}`) - node.items.forEach(({ key, value }) => { + for (const { key, value } of node.values.values()) { addEvents(events, doc, errPos, key) addEvents(events, doc, errPos, value) - }) + } events.push('-MAP') } else if (node instanceof YAMLSeq) { const ev = node.flow ? '+SEQ []' : '+SEQ' events.push(`${ev}${props}`) - node.items.forEach(item => { + node.forEach(item => { addEvents(events, doc, errPos, item) }) events.push('-SEQ') + } else if (node instanceof YAMLSet) { + const ev = node.flow ? '+MAP {}' : '+MAP' + events.push(`${ev}${props}`) + for (const value of node.values.values()) { + addEvents(events, doc, errPos, value) + events.push('=VAL :') + } + events.push('-MAP') } else if (node instanceof Pair) { events.push(`+MAP${props}`) addEvents(events, doc, errPos, node.key) diff --git a/src/util.ts b/src/util.ts index d3301f1f..bf9d71bd 100644 --- a/src/util.ts +++ b/src/util.ts @@ -11,7 +11,6 @@ export type { NodeCreator } from './doc/NodeCreator.ts' export { debug, warn } from './log.ts' export type { LogLevelId } from './log.ts' export { ToJSContext } from './nodes/toJS.ts' -export { findPair } from './nodes/YAMLMap.ts' export { map as mapTag } from './schema/common/map.ts' export { seq as seqTag } from './schema/common/seq.ts' export { string as stringTag } from './schema/common/string.ts' diff --git a/src/visit.ts b/src/visit.ts index 67e1d70f..6f81ef14 100644 --- a/src/visit.ts +++ b/src/visit.ts @@ -1,11 +1,12 @@ -import { Document, type DocValue } from './doc/Document.ts' +import { Document } from './doc/Document.ts' import { Alias } from './nodes/Alias.ts' -import { isNode } from './nodes/identity.ts' -import type { Node } from './nodes/Node.ts' +import { isCollection, isNode } from './nodes/identity.ts' +import type { Node, Primitive } from './nodes/types.ts' import { Pair } from './nodes/Pair.ts' import { Scalar } from './nodes/Scalar.ts' import { YAMLMap } from './nodes/YAMLMap.ts' import { YAMLSeq } from './nodes/YAMLSeq.ts' +import { YAMLSet } from './nodes/YAMLSet.ts' const BREAK = Symbol('break visit') const SKIP = Symbol('skip children') @@ -21,13 +22,13 @@ export type visitor = | visitorFn | { Alias?: visitorFn - Collection?: visitorFn - Map?: visitorFn - Node?: visitorFn + Collection?: visitorFn + Map?: visitorFn + Node?: visitorFn Pair?: visitorFn Scalar?: visitorFn Seq?: visitorFn - Value?: visitorFn + Value?: visitorFn } export type asyncVisitorFn = ( @@ -46,13 +47,13 @@ export type asyncVisitor = | asyncVisitorFn | { Alias?: asyncVisitorFn - Collection?: asyncVisitorFn - Map?: asyncVisitorFn - Node?: asyncVisitorFn + Collection?: asyncVisitorFn + Map?: asyncVisitorFn + Node?: asyncVisitorFn Pair?: asyncVisitorFn Scalar?: asyncVisitorFn Seq?: asyncVisitorFn - Value?: asyncVisitorFn + Value?: asyncVisitorFn } /** @@ -122,17 +123,30 @@ function visit_( } if (typeof ctrl !== 'symbol') { - if (node instanceof YAMLMap || node instanceof YAMLSeq) { + if (node instanceof YAMLSeq) { path = [...path, node] - for (let i = 0; i < node.items.length; ++i) { - const ci = visit_(i, node.items[i], visitor, path) + for (let i = 0; i < node.length; ++i) { + const ci = visit_(i, node[i], visitor, path) if (typeof ci === 'number') i = ci - 1 else if (ci === BREAK) return BREAK else if (ci === REMOVE) { - node.items.splice(i, 1) + node.splice(i, 1) i -= 1 } } + } else if (node instanceof YAMLMap || node instanceof YAMLSet) { + path = [...path, node] + const entries = Array.from( + node.values as Iterable<[symbol | Primitive, Node | Pair]> + ) + const delKeys = [] + for (let i = 0; i < entries.length; ++i) { + const ci = visit_(i, entries[i][1], visitor, path) + if (typeof ci === 'number') i = ci - 1 + else if (ci === BREAK) return BREAK + else if (ci === REMOVE) delKeys.push(entries[i][0]) + } + for (const dk of delKeys) node.values.delete(dk) } else if (node instanceof Pair) { path = [...path, node] const ck = visit_('key', node.key, visitor, path) @@ -216,17 +230,30 @@ async function visitAsync_( } if (typeof ctrl !== 'symbol') { - if (node instanceof YAMLMap || node instanceof YAMLSeq) { + if (node instanceof YAMLSeq) { path = [...path, node] - for (let i = 0; i < node.items.length; ++i) { - const ci = await visitAsync_(i, node.items[i], visitor, path) + for (let i = 0; i < node.length; ++i) { + const ci = await visitAsync_(i, node[i], visitor, path) if (typeof ci === 'number') i = ci - 1 else if (ci === BREAK) return BREAK else if (ci === REMOVE) { - node.items.splice(i, 1) + node.splice(i, 1) i -= 1 } } + } else if (node instanceof YAMLMap || node instanceof YAMLSet) { + path = [...path, node] + const entries = Array.from( + node.values as Iterable<[symbol | Primitive, Node | Pair]> + ) + const delKeys = [] + for (let i = 0; i < entries.length; ++i) { + const ci = await visitAsync_(i, entries[i][1], visitor, path) + if (typeof ci === 'number') i = ci - 1 + else if (ci === BREAK) return BREAK + else if (ci === REMOVE) delKeys.push(entries[i][0]) + } + for (const dk of delKeys) node.values.delete(dk) } else if (node instanceof Pair) { path = [...path, node] const ck = await visitAsync_('key', node.key, visitor, path) @@ -288,7 +315,8 @@ function callVisitor( path: readonly (Document | Node | Pair)[] ): ReturnType> | ReturnType> { if (typeof visitor === 'function') return visitor(key, node, path) - if (node instanceof YAMLMap) return visitor.Map?.(key, node, path) + if (node instanceof YAMLMap || node instanceof YAMLSet) + return visitor.Map?.(key, node, path) if (node instanceof YAMLSeq) return visitor.Seq?.(key, node, path) if (node instanceof Pair) return visitor.Pair?.(key, node, path) if (node instanceof Scalar) return visitor.Scalar?.(key, node, path) @@ -302,8 +330,24 @@ function replaceNode( node: Node | Pair ): number | symbol | void { const parent = path[path.length - 1] - if (parent instanceof YAMLMap || parent instanceof YAMLSeq) { - parent.items[key as number] = node + if (parent instanceof YAMLSeq) { + parent[key as number] = node + } else if (parent instanceof YAMLMap) { + if (node instanceof Pair) { + const entries = Array.from(parent.values) + entries[key as number] = [parent.keyOf(node, true), node] + parent.values = new Map(entries) + } else { + throw new Error('Cannot replace map pair with non-pair value') + } + } else if (parent instanceof YAMLSet) { + if (isNode(node)) { + const entries = Array.from(parent.values) + entries[key as number] = [parent.keyOf(node, true), node] + parent.values = new Map(entries) + } else { + throw new Error('Cannot replace set value with non-node value') + } } else if (parent instanceof Pair) { if (isNode(node)) { if (key === 'key') parent.key = node @@ -312,7 +356,8 @@ function replaceNode( throw new Error(`Cannot replace pair ${key} with non-node value`) } } else if (parent instanceof Document) { - parent.value = node as DocValue + if (node instanceof Scalar || isCollection(node)) parent.value = node + else throw new Error('Cannot replace Document value with non-node value') } else { const pt = parent instanceof Alias ? 'alias' : 'scalar' throw new Error(`Cannot replace node with ${pt} parent`) diff --git a/tests/_setup.ts b/tests/_setup.ts new file mode 100644 index 00000000..486e9225 --- /dev/null +++ b/tests/_setup.ts @@ -0,0 +1,12 @@ +expect.addEqualityTesters([ + function arrayishEquals(a, b, customTesters) { + if ( + Array.isArray(a) && + Array.isArray(b) && + a.constructor !== b.constructor + ) { + return this.equals(Array.from(a), Array.from(b), customTesters) + } + return undefined + } +]) diff --git a/tests/_utils.ts b/tests/_utils.ts index 2ebde379..75262de0 100644 --- a/tests/_utils.ts +++ b/tests/_utils.ts @@ -16,3 +16,51 @@ export function source( const indent = match && Math.min(...match.map(el => el.length)) return indent ? res.replace(new RegExp(`^.{${indent}}`, 'gm'), '') : res } + +/** Test shorthand for `expect(YAMLMap).toMatchObject()` */ +export function _map( + input: Record | [any, any][], + props?: Record +): Record { + const entries = Array.isArray(input) ? input : Object.entries(input) + const values = new Map(entries.map(([k, v]) => [k, _pair(k, v)])) + return { values, ...props } +} + +/** Test shorthand for `expect(Pair).toMatchObject()` */ +export function _pair(key: any, value: any): { key: any; value: any } { + return value && + typeof value === 'object' && + 'key' in value && + 'value' in value + ? value + : { key: _node(key), value: _node(value) } +} + +/** Test shorthand for `expect(YAMLSeq).toMatchObject()` */ +export function _seq(...inputs: any[]): any[] { + return inputs.map(_node) +} + +/** Test shorthand for `expect(YAMLSet).toMatchObject()` */ +export function _set( + input: (boolean | number | string | null | [any, any])[], + props?: Record +): Record { + const values = new Map( + input.map(e => (Array.isArray(e) ? [e[0], _node(e[1])] : [e, _node(e)])) + ) + return { values, ...props } +} + +function _node(value: unknown) { + switch (typeof value) { + case 'bigint': + case 'boolean': + case 'number': + case 'string': + return { value } + default: + return value + } +} diff --git a/tests/cli.ts b/tests/cli.ts index f6844f45..daa7be19 100644 --- a/tests/cli.ts +++ b/tests/cli.ts @@ -1,5 +1,6 @@ import { Readable } from 'stream' import { cli } from 'yaml/cli' +import { _map } from './_utils.ts' const [major] = process.versions.node.split('.') const skip = Number(major) < 20 @@ -245,18 +246,23 @@ const skip = Number(major) < 20 ) }) describe('--doc', () => { - ok('basic', 'hello: world', ['--doc'], [{ value: { items: [{}] } }]) + ok( + 'basic', + 'hello: world', + ['--doc'], + [{ value: _map({ hello: 'world' }) }] + ) ok( 'multiple', 'hello: world\n---\n42', ['--doc'], - [{ value: { items: [{}] } }, { value: { value: 42 } }] + [{ value: _map({ hello: 'world' }) }, { value: { value: 42 } }] ) ok( 'error', 'hello: world: 2', ['--doc'], - [{ value: { items: [{}] } }], + [{ value: _map({ hello: _map({ world: 2 }) }) }], [{ name: 'YAMLParseError' }] ) }) diff --git a/tests/collection-access.ts b/tests/collection-access.ts index 3d0badeb..23a3f0fb 100644 --- a/tests/collection-access.ts +++ b/tests/collection-access.ts @@ -1,6 +1,5 @@ import { Document, - Pair, Scalar, YAMLMap, type YAMLOMap, @@ -8,6 +7,7 @@ import { type YAMLSet, parseDocument } from 'yaml' +import { _map, _pair, _seq, _set } from './_utils.ts' describe('Map', () => { let doc: Document @@ -15,34 +15,26 @@ describe('Map', () => { beforeEach(() => { doc = new Document({ a: 1, b: { c: 3, d: 4 } }) map = doc.value as any - expect(map.items).toMatchObject([ - { key: { value: 'a' }, value: { value: 1 } }, - { - key: { value: 'b' }, - value: { - items: [ - { key: { value: 'c' }, value: { value: 3 } }, - { key: { value: 'd' }, value: { value: 4 } } - ] - } - } - ]) }) - test('add', () => { - map.add(doc.createPair('c', 'x')) + test('create', () => { + expect(map).toMatchObject(_map({ a: 1, b: _map({ c: 3, d: 4 }) })) + }) + + test('set', () => { + map.set(doc.createPair('c', 'x')) expect(map.get('c')).toMatchObject({ value: 'x' }) - map.add(doc.createPair('c', 'y')) + map.set('c', 'y') expect(map.get('c')).toMatchObject({ value: 'y' }) - expect(map.items).toHaveLength(3) + expect(map.size).toBe(3) }) test('delete', () => { expect(map.delete('a')).toBe(true) expect(map.get('a')).toBeUndefined() expect(map.delete('c')).toBe(false) - expect(map.get('b')).toMatchObject({ items: [{}, {}] }) - expect(map.items).toHaveLength(1) + expect(map.get('b')).toMatchObject({ size: 2 }) + expect(map.size).toBe(1) }) test('get with value', () => { @@ -88,7 +80,7 @@ describe('Map', () => { expect(map.get('b')).toMatchObject({ value: 5 }) map.set('c', 6) expect(map.get('c')).toMatchObject({ value: 6 }) - expect(map.items).toHaveLength(3) + expect(map.size).toBe(3) }) test('set with node', () => { @@ -98,15 +90,30 @@ describe('Map', () => { expect(map.get('b')).toMatchObject({ value: 5 }) map.set(doc.createNode('c'), 6) expect(map.get('c')).toMatchObject({ value: 6 }) - expect(map.items).toHaveLength(3) + expect(map.size).toBe(3) }) test('set scalar node with anchor', () => { - doc = parseDocument('a: &A value\nb: *A\n') + const doc = parseDocument('a: &A value\nb: *A\n') doc.set('a', 'foo') expect(doc.get('a')).toMatchObject({ value: 'foo' }) expect(String(doc)).toBe('a: &A foo\nb: *A\n') }) + + test('object key equality', () => { + const a1 = [1] + const doc = new Document( + new Map([ + [1, 2], + ['1', '2'], + [a1, [2]] + ]) + ) + expect(doc.get(1)).toMatchObject({ value: 2 }) + expect(doc.get('1')).toMatchObject({ value: '2' }) + expect(doc.get([1])).toBeUndefined() + expect(doc.get(a1)).toMatchObject([{ value: 2 }]) + }) }) describe('Seq', () => { @@ -115,69 +122,46 @@ describe('Seq', () => { beforeEach(() => { doc = new Document([1, [2, 3]]) seq = doc.value as any - expect(seq.items).toMatchObject([ - { value: 1 }, - { items: [{ value: 2 }, { value: 3 }] } - ]) }) - test('add', () => { - seq.add(9) - expect(seq.get(2)).toMatchObject({ value: 9 }) - seq.add(1) - expect(seq.items).toHaveLength(4) + test('create', () => { + expect(seq).toMatchObject([{ value: 1 }, [{ value: 2 }, { value: 3 }]]) }) - test('delete', () => { - expect(seq.delete(0)).toBe(true) - expect(seq.delete(2)).toBe(false) - expect(() => seq.delete('a' as any)).toThrow(TypeError) - expect(seq.get(0)).toMatchObject({ items: [{ value: 2 }, { value: 3 }] }) - expect(seq.items).toHaveLength(1) + test('push', () => { + seq.push(9) + expect(seq[2]).toMatchObject({ value: 9 }) + seq.push(1) + expect(seq).toHaveLength(4) }) - test('get with integer', () => { - expect(seq.get(0)).toMatchObject({ value: 1 }) - const subSeq = seq.get(1) + test('index access', () => { + expect(seq[0]).toMatchObject({ value: 1 }) + const subSeq = seq[1] expect(subSeq).toBeInstanceOf(YAMLSeq) - expect(subSeq!.toJS(doc)).toMatchObject([2, 3]) - expect(seq.get(2)).toBeUndefined() - }) - - test('get with non-integer', () => { - expect(() => seq.get(-1)).toThrow(RangeError) - expect(() => seq.get(0.5)).toThrow(TypeError) - expect(() => seq.get('0' as any)).toThrow(TypeError) - expect(() => seq.get(doc.createNode(0) as any)).toThrow(TypeError) + expect(subSeq.toJS(doc)).toMatchObject([2, 3]) + expect(seq[2]).toBeUndefined() }) - test('has with integer', () => { - expect(seq.has(0)).toBe(true) - expect(seq.has(1)).toBe(true) - expect(seq.has(2)).toBe(false) - }) - - test('has with non-integer', () => { - expect(() => seq.has(-1)).toThrow(RangeError) - expect(() => seq.has(0.5)).toThrow(TypeError) - expect(() => seq.has('0' as any)).toThrow(TypeError) - // @ts-expect-error TS complains, as it should. - expect(() => seq.has()).toThrow(TypeError) - expect(() => seq.has(doc.createNode(0) as any)).toThrow(TypeError) + test('at() with integer', () => { + expect(seq.at(0)).toMatchObject({ value: 1 }) + const subSeq = seq.at(1) + expect(subSeq).toBeInstanceOf(YAMLSeq) + expect(subSeq!.toJS(doc)).toMatchObject([2, 3]) + expect(seq.at(2)).toBeUndefined() }) test('set with integer', () => { seq.set(0, 2) - expect(seq.get(0)).toMatchObject({ value: 2 }) - seq.set(1, 5) - expect(seq.get(1)).toMatchObject({ value: 5 }) + expect(seq.at(0)).toMatchObject({ value: 2 }) + seq.set(-1, 5) + expect(seq.at(1)).toMatchObject({ value: 5 }) seq.set(2, 6) - expect(seq.get(2)).toMatchObject({ value: 6 }) - expect(seq.items).toHaveLength(3) + expect(seq.at(2)).toMatchObject({ value: 6 }) + expect(seq).toHaveLength(3) }) test('set with non-integer', () => { - expect(() => seq.set(-1, 2)).toThrow(RangeError) expect(() => seq.set(0.5, 2)).toThrow(TypeError) expect(() => seq.set(doc.createNode(0) as any, 2)).toThrow(TypeError) }) @@ -187,14 +171,12 @@ describe('Set', () => { let doc: Document let set: YAMLSet beforeEach(() => { - doc = new Document(null, { version: '1.1' }) - set = doc.createNode([1, 2, 3], { tag: '!!set' }) as any - doc.value = set - expect(set.items).toMatchObject([ - { key: { value: 1 }, value: null }, - { key: { value: 2 }, value: null }, - { key: { value: 3 }, value: null } - ]) + doc = new Document([1, 2, [3]], { tag: '!!set', version: '1.1' }) + set = doc.value as any + }) + + test('create', () => { + expect(set).toMatchObject(_set([1, 2, [[3], _seq(3)]])) }) test('add', () => { @@ -202,343 +184,131 @@ describe('Set', () => { expect(set.get('x')).toMatchObject({ value: 'x' }) set.add('x') const y0 = new Scalar('y') - set.add(new Pair(y0)) - set.add(new Pair(new Scalar('y'))) + set.add(y0) + set.add(new Scalar('y')) expect(set.get('y')).toBe(y0) - expect(set.items).toHaveLength(5) + expect(set.size).toBe(5) }) test('get', () => { expect(set.get(1)).toMatchObject({ value: 1 }) expect(set.get(0)).toBeUndefined() expect(set.get('1')).toBeUndefined() - }) - - test('set', () => { - set.set(1, true) - expect(set.get(1)).toMatchObject({ value: 1 }) - set.set(1, false) - expect(set.get(1)).toBeUndefined() - set.set(4, false) - expect(set.get(4)).toBeUndefined() - set.set(4, true) - expect(set.get(4)).toMatchObject(new Scalar(4)) - expect(set.items).toHaveLength(3) - }) -}) - -describe('OMap', () => { - let doc: Document - let omap: YAMLOMap - beforeEach(() => { - doc = new Document(null, { version: '1.1' }) - omap = doc.createNode([{ a: 1 }, { b: { c: 3, d: 4 } }], { - tag: '!!omap' - }) as any - doc.value = omap - expect(omap.items).toMatchObject([ - { key: { value: 'a' }, value: { value: 1 } }, - { - key: { value: 'b' }, - value: { - items: [ - { key: { value: 'c' }, value: { value: 3 } }, - { key: { value: 'd' }, value: { value: 4 } } - ] - } - } - ]) - }) - - test('add', () => { - omap.add(doc.createPair('c', 'x')) - expect(omap.get('c')).toMatchObject({ value: 'x' }) - omap.add(doc.createPair('c', 'y')) - expect(omap.items).toHaveLength(3) - }) - - test('delete', () => { - expect(omap.delete('a')).toBe(true) - expect(omap.get('a')).toBeUndefined() - expect(omap.delete('c')).toBe(false) - expect(omap.get('b')).toMatchObject({ items: [{}, {}] }) - expect(omap.items).toHaveLength(1) - }) - - test('get', () => { - expect(omap.get('a')).toMatchObject({ value: 1 }) - const subMap = omap.get('b') - expect(subMap).toBeInstanceOf(YAMLMap) - expect(subMap.toJS(doc)).toMatchObject({ c: 3, d: 4 }) - expect(omap.get('c')).toBeUndefined() + expect(set.get([3] as any)).toBeUndefined() }) test('has', () => { - expect(omap.has('a')).toBe(true) - expect(omap.has('b')).toBe(true) - expect(omap.has('c')).toBe(false) - expect(omap.has('')).toBe(false) - // @ts-expect-error TS should complain here - expect(omap.has()).toBe(false) + expect(set.has(0)).toBe(false) + expect(set.has(1)).toBe(true) + expect(set.has(new Scalar(1))).toBe(true) + const seq = Array.from(set.values.values()).at(-1)! + expect(set.has(seq)).toBe(true) }) - test('set', () => { - omap.set('a', 2) - expect(omap.get('a')).toMatchObject({ value: 2 }) - omap.set('b', 5) - expect(omap.get('b')).toMatchObject({ value: 5 }) - omap.set('c', 6) - expect(omap.get('c')).toMatchObject({ value: 6 }) - expect(omap.items).toHaveLength(3) + test('delete', () => { + expect(set.delete(0)).toBe(false) + expect(set.delete(1)).toBe(true) + expect(set.has(1)).toBe(false) + expect(set.size).toBe(2) }) }) -describe('Collection', () => { +describe('OMap', () => { let doc: Document - let map: YAMLMap | YAMLSeq>> + let omap: YAMLOMap beforeEach(() => { - doc = new Document({ a: 1, b: [2, 3] }) - map = doc.value as any - }) - - test('addIn', () => { - map.addIn(['b'], 4) - expect(map.getIn(['b', 2])).toMatchObject({ value: 4 }) - map.addIn([], doc.createPair('c', 5)) - expect(map.get('c')).toMatchObject({ value: 5 }) - expect(() => map.addIn(['a'], -1)).toThrow(/Expected YAML collection/) - map.addIn(['b', 3], 6) - expect(map.items).toHaveLength(3) - const seq = map.getIn(['b']) - expect(seq).toBeInstanceOf(YAMLSeq) - expect((seq as any).items).toHaveLength(4) - }) - - test('deleteIn', () => { - expect(map.deleteIn(['a'])).toBe(true) - expect(map.get('a')).toBeUndefined() - expect(map.deleteIn(['b', 1])).toBe(true) - expect(map.getIn(['b', 1])).toBeUndefined() - expect(map.deleteIn([1])).toBe(false) - expect(map.deleteIn(['b', 2])).toBe(false) - expect(() => map.deleteIn(['a', 'e'])).toThrow(/Expected YAML collection/) - expect(map.items).toHaveLength(1) - const subSeq = map.getIn(['b']) - expect(subSeq).toBeInstanceOf(YAMLSeq) - expect((subSeq as any).items).toHaveLength(1) + doc = new Document([{ a: 1 }, { b: { c: 3, d: 4 } }], { + tag: '!!omap', + version: '1.1' + }) + omap = doc.value as any }) - test('getIn', () => { - expect(map.getIn(['a'])).toMatchObject({ value: 1 }) - expect(map.getIn(['b', 1])).toMatchObject({ value: 3 }) - expect(() => map.getIn(['b', '1'])).toThrow(TypeError) - expect(map.getIn(['b', 2])).toBeUndefined() - expect(map.getIn(['c', 'e'])).toBeUndefined() - expect(map.getIn(['a', 'e'])).toBeUndefined() + test('create', () => { + expect(omap).toMatchObject([ + _pair('a', 1), + _pair('b', _map({ c: 3, d: 4 })) + ]) }) - test('hasIn', () => { - expect(map.hasIn(['a'])).toBe(true) - expect(map.hasIn(['b', 1])).toBe(true) - expect(() => map.hasIn(['b', '1'])).toThrow(TypeError) - expect(map.hasIn(['b', 2])).toBe(false) - expect(map.hasIn(['c', 'e'])).toBe(false) - expect(map.hasIn(['a', 'e'])).toBe(false) + test('push', () => { + omap.push(doc.createPair('c', 'x')) + expect(omap.at(-1)).toMatchObject({ value: { value: 'x' } }) + omap.push(doc.createPair('c', 'y')) + expect(() => omap.toString()).toThrow() + expect(() => omap.push('d' as any)).toThrow() + expect(omap.size).toBe(4) }) - test('setIn', () => { - map.setIn(['a'], 2) - expect(map.get('a')).toMatchObject({ value: 2 }) - map.setIn(['b', 1], 5) - expect(map.getIn(['b', 1])).toMatchObject({ value: 5 }) - map.setIn([1], 6) - expect(map.get(1)).toMatchObject({ value: 6 }) - map.setIn(['b', 2], 6) - expect(map.getIn(['b', 2])).toMatchObject({ value: 6 }) - map.setIn(['e', 'e'], 7) - expect(map.getIn(['e', 'e'])).toMatchObject({ value: 7 }) - expect(() => map.setIn(['a', 'e'], 8)).toThrow(/Expected YAML collection/) - expect(map.items).toHaveLength(4) - const subSeq = map.getIn(['b']) - expect(subSeq).toBeInstanceOf(YAMLSeq) - expect((subSeq as any).items).toHaveLength(3) + test('set', () => { + omap.set(0, doc.createPair('a', 2)) + omap.set(1, doc.createPair('b', 5)) + omap.set(3, doc.createPair('d', 6)) + expect(omap).toMatchObject([ + _pair('a', 2), + _pair('b', 5), + undefined, + _pair('d', 6) + ]) + expect(omap.size).toBe(4) + expect(String(doc)).toBe(`\ +!!omap +- a: 2 +- b: 5 +- null +- d: 6\n`) + }) + + test('toString', () => { + expect(String(doc)).toBe(`\ +!!omap +- a: 1 +- b: + c: 3 + d: 4\n`) }) }) describe('Document', () => { - let doc: Document | YAMLSeq>>> - beforeEach(() => { - doc = new Document({ a: 1, b: [2, 3] }) - expect(doc.value.items).toMatchObject([ - { key: { value: 'a' }, value: { value: 1 } }, - { - key: { value: 'b' }, - value: { items: [{ value: 2 }, { value: 3 }] } - } - ]) - }) - - test('add', () => { - doc.add(doc.createPair('c', 'x')) - expect(doc.get('c')).toMatchObject({ value: 'x' }) - expect(doc.value.items).toHaveLength(3) - }) - - test('addIn', () => { - doc.addIn(['b'], 4) - expect(doc.getIn(['b', 2])).toMatchObject({ value: 4 }) - doc.addIn([], doc.createPair('c', 5)) - expect(doc.get('c')).toMatchObject({ value: 5 }) - expect(() => doc.addIn(['a'], -1)).toThrow(/Expected YAML collection/) - doc.addIn(['b', 3], 6) - expect(doc.value.items).toHaveLength(3) - expect((doc.get('b') as any).items).toHaveLength(4) - }) - - test('delete', () => { - expect(doc.delete('a')).toBe(true) - expect(doc.delete('a')).toBe(false) - expect(doc.get('a')).toBeUndefined() - expect(doc.value.items).toHaveLength(1) - }) - - test('delete on scalar value', () => { - const doc = new Document('s') - expect(() => doc.set('a', 1)).toThrow(/document value/) - }) - - test('deleteIn', () => { - expect(doc.deleteIn(['a'])).toBe(true) - expect(doc.get('a')).toBeUndefined() - expect(doc.deleteIn(['b', 1])).toBe(true) - expect(doc.getIn(['b', 1])).toBeUndefined() - expect(doc.deleteIn([1])).toBe(false) - expect(doc.deleteIn(['b', 2])).toBe(false) - expect(() => doc.deleteIn(['a', 'e'])).toThrow(/Expected/) - expect(doc.value.items).toHaveLength(1) - expect((doc.get('b') as any).items).toHaveLength(1) - expect(() => doc.deleteIn(null as any)).toThrow() - }) - - test('get', () => { + test('get with map value', () => { + const doc = new Document({ a: 1, b: [2, 3] }) expect(doc.get('a')).toMatchObject({ value: 1 }) expect(doc.get('c')).toBeUndefined() }) - test('get on scalar value', () => { - const doc = new Document('s') - expect(doc.get('a')).toBeUndefined() - }) - - test('getIn collection', () => { - expect(doc.getIn(['a'])).toMatchObject({ value: 1 }) - expect(doc.getIn(['b', 1])).toMatchObject({ value: 3 }) - expect(() => doc.getIn(['b', 'e'])).toThrow(TypeError) - expect(doc.getIn(['c', 'e'])).toBeUndefined() - expect(doc.getIn(['a', 'e'])).toBeUndefined() - }) - - test('getIn scalar', () => { - const doc = new Document('s') - expect(doc.getIn([])).toMatchObject({ value: 's' }) - expect(() => doc.getIn(null as any)).toThrow() - expect(doc.getIn([0])).toBeUndefined() - }) - - test('has', () => { - expect(doc.has('a')).toBe(true) - expect(doc.has('c')).toBe(false) + test('get with seq value', () => { + const doc = new Document([2, 3]) + expect(doc.get(0)).toMatchObject({ value: 2 }) + expect(doc.get(-1)).toMatchObject({ value: 3 }) + expect(() => doc.get('a')).toThrow() }) - test('has on scalar value', () => { + test('get with scalar value', () => { const doc = new Document('s') - expect(doc.has('a')).toBe(false) - }) - - test('hasIn', () => { - expect(doc.hasIn(['a'])).toBe(true) - expect(doc.hasIn(['b', 1])).toBe(true) - expect(() => doc.hasIn(['b', 'e'])).toThrow(TypeError) - expect(doc.hasIn(['c', 'e'])).toBe(false) - expect(doc.hasIn(['a', 'e'])).toBe(false) + expect(doc.get('a')).toBeUndefined() }) - test('set', () => { + test('set with map value', () => { + const doc = new Document({ a: 1, b: [2, 3] }) doc.set('a', 2) expect(doc.get('a')).toMatchObject({ value: 2 }) doc.set('c', 6) expect(doc.get('c')).toMatchObject({ value: 6 }) - expect(doc.value.items).toHaveLength(3) - }) - - test('set on scalar value', () => { - const doc = new Document('s') - expect(() => doc.set('a', 1)).toThrow(/document value/) + expect(doc.value.size).toBe(3) }) - test('setIn', () => { - doc.setIn(['a'], 2) - expect(doc.getIn(['a'])).toMatchObject({ value: 2 }) - doc.setIn(['b', 1], 5) - expect(doc.getIn(['b', 1])).toMatchObject({ value: 5 }) - doc.setIn(['c'], 6) - expect(doc.getIn(['c'])).toMatchObject({ value: 6 }) - doc.setIn(['e', 1, 'e'], 7) - expect(doc.getIn(['e', 1, 'e'])).toMatchObject({ value: 7 }) - expect(() => doc.setIn(['a', 'e'], 8)).toThrow(/Expected YAML collection/) - expect(doc.value.items).toHaveLength(4) - expect((doc.get('b') as any).items).toHaveLength(2) - expect(String(doc)).toBe( - 'a: 2\nb:\n - 2\n - 5\nc: 6\ne:\n - null\n - e: 7\n' - ) + test('set with seq value', () => { + const doc = new Document([2, 3]) + doc.set(0, 4) + expect(doc.get(0)).toMatchObject({ value: 4 }) + doc.set(3, 6) + expect(doc.get(3)).toMatchObject({ value: 6 }) + expect(() => doc.set('a', 1)).toThrow(TypeError) + expect(doc.value.size).toBe(4) }) - test('setIn on scalar value', () => { + test('set with scalar value', () => { const doc = new Document('s') - expect(() => doc.setIn(['a'], 1)).toThrow(/document value/) - }) - - test('setIn on parsed document', () => { - const doc = parseDocument('{ a: 1, b: [2, 3] }') - doc.setIn(['c', 1], 9) - expect(String(doc)).toBe('{ a: 1, b: [ 2, 3 ], c: [ null, 9 ] }\n') - }) - - test('setIn with __proto__ as key', () => { - doc.setIn(['c', '__proto__'], 9) - expect(String(doc)).toBe('a: 1\nb:\n - 2\n - 3\nc:\n __proto__: 9\n') - }) - - test('setIn with object key', () => { - doc.value = doc.createNode({}) - const foo = { foo: 'FOO' } - doc.setIn([foo], 'BAR') - expect(doc.value.items).toMatchObject([ - { - key: { items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] }, - value: { value: 'BAR' } - } - ]) - }) - - test('setIn with repeated object key', () => { - doc.value = doc.createNode({}) - const foo = { foo: 'FOO' } - doc.setIn([foo, foo], 'BAR') - expect(doc.value.items).toMatchObject([ - { - key: { items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] }, - value: { - items: [ - { - key: { - items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] - }, - value: { value: 'BAR' } - } - ] - } - } - ]) + expect(() => doc.set('a', 1)).toThrow(/document value/) }) }) diff --git a/tests/directives.ts b/tests/directives.ts index ed197f03..c27afd1c 100644 --- a/tests/directives.ts +++ b/tests/directives.ts @@ -17,12 +17,10 @@ describe('%TAG', () => { '!': '!foo:', '!bar!': '!bar:' }) - expect(doc.value).toMatchObject({ - items: [ - { value: 'v1', tag: '!foo:bar' }, - { value: 'v2', tag: '!bar:foo' } - ] - }) + expect(doc.value).toMatchObject([ + { value: 'v1', tag: '!foo:bar' }, + { value: 'v2', tag: '!bar:foo' } + ]) }) test('parse global tags', () => { @@ -39,12 +37,10 @@ describe('%TAG', () => { '!': 'foo:', '!bar!': 'bar:bar#bar?' }) - expect(doc.value).toMatchObject({ - items: [ - { value: 'v1', tag: 'foo:bar' }, - { value: 'v2', tag: 'bar:bar#bar?foo' } - ] - }) + expect(doc.value).toMatchObject([ + { value: 'v1', tag: 'foo:bar' }, + { value: 'v2', tag: 'bar:bar#bar?foo' } + ]) }) test('create & stringify', () => { diff --git a/tests/doc/YAML-1.2.spec.ts b/tests/doc/YAML-1.2.spec.ts index fcd0d6d1..7da32156 100644 --- a/tests/doc/YAML-1.2.spec.ts +++ b/tests/doc/YAML-1.2.spec.ts @@ -435,8 +435,8 @@ application specific tag: !something | ], warnings: [['Unresolved tag: !something']], special(src) { - const doc = YAML.parseDocument(src, { schema: 'yaml-1.1' }) - const data = doc.value.items[1].value.value + const doc = YAML.parseDocument(src, { schema: 'yaml-1.1' }) + const data = doc.get('picture').value expect(data).toBeInstanceOf(Uint8Array) expect(data.byteLength).toBe(65) } @@ -1687,7 +1687,8 @@ last line }, 'Example 8.21. Block Scalar Nodes': { - src: `literal: |2 + src: `\ +literal: |2 value folded: !foo @@ -1703,7 +1704,8 @@ folded: }, 'Example 8.22. Block Collection Nodes': { - src: `sequence: !!seq + src: `\ +sequence: !!seq - entry - !!seq - nested @@ -1716,13 +1718,14 @@ mapping: !!map } ], special(src) { - const doc = YAML.parseDocument, false>(src) - expect(doc.value.tag).toBeUndefined() - expect(doc.value.items[0].value.tag).toBe('tag:yaml.org,2002:seq') - expect(doc.value.items[0].value.items[1].tag).toBe( - 'tag:yaml.org,2002:seq' + const doc = YAML.parseDocument, false>( + src ) - expect(doc.value.items[1].value.tag).toBe('tag:yaml.org,2002:map') + expect(doc.value.tag).toBeUndefined() + const seq = doc.get('sequence') + expect(seq.tag).toBe('tag:yaml.org,2002:seq') + expect(seq[1].tag).toBe('tag:yaml.org,2002:seq') + expect(doc.get('mapping').tag).toBe('tag:yaml.org,2002:map') } } }, diff --git a/tests/doc/anchors.ts b/tests/doc/anchors.ts index b1386700..bc337470 100644 --- a/tests/doc/anchors.ts +++ b/tests/doc/anchors.ts @@ -1,14 +1,12 @@ import type { Alias, YAMLMap, YAMLSeq } from 'yaml' import { Document, parse, parseDocument } from 'yaml' +import { _map } from '../_utils.ts' test('basic', () => { const src = `- &a 1\n- *a\n` const doc = parseDocument(src) expect(doc.errors).toHaveLength(0) - expect(doc.value.items).toMatchObject([ - { anchor: 'a', value: 1 }, - { source: 'a' } - ]) + expect(doc.value).toMatchObject([{ anchor: 'a', value: 1 }, { source: 'a' }]) expect(String(doc)).toBe(src) }) @@ -16,7 +14,7 @@ test('re-defined anchor', () => { const src = '- &a 1\n- &a 2\n- *a\n' const doc = parseDocument(src) expect(doc.errors).toHaveLength(0) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { anchor: 'a', value: 1 }, { anchor: 'a', value: 2 }, { source: 'a' } @@ -30,10 +28,8 @@ test('circular reference', () => { const doc = parseDocument(src) expect(doc.errors).toHaveLength(0) expect(doc.warnings).toHaveLength(0) - expect(doc.value).toMatchObject({ - anchor: 'a', - items: [{ value: 1 }, { source: 'a' }] - }) + expect(doc.value).toMatchObject([{ value: 1 }, { source: 'a' }]) + expect(doc.value.anchor).toBe('a') const res = doc.toJS() expect(res[1]).toBe(res) expect(String(doc)).toBe(src) @@ -42,16 +38,18 @@ test('circular reference', () => { describe('anchor on tagged collection', () => { test('!!set', () => { const res = parse('- &a !!set { 1, 2 }\n- *a\n') - expect(Array.from(res[0])).toMatchObject([1, 2]) + expect(res[0]).toMatchObject(new Set([1, 2])) expect(res[1]).toBe(res[0]) }) test('!!omap', () => { const res = parse('- &a !!omap [ 1: 1, 2: 2 ]\n- *a\n') - expect(Array.from(res[0])).toMatchObject([ - [1, 1], - [2, 2] - ]) + expect(res[0]).toMatchObject( + new Map([ + [1, 1], + [2, 2] + ]) + ) expect(res[1]).toBe(res[0]) }) }) @@ -60,16 +58,16 @@ describe('create', () => { test('node.anchor', () => { const doc = parseDocument('[{ a: A }, { b: B }]') doc.get(0).anchor = 'AA' - doc.getIn([0, 'a']).anchor = 'a' - doc.getIn([0, 'a']).anchor = '' - doc.getIn([1, 'b']).anchor = 'BB' + doc.get(0).get('a').anchor = 'a' + doc.get(0).get('a').anchor = '' + doc.get(1).get('b').anchor = 'BB' expect(String(doc)).toBe('[ &AA { a: A }, { b: &BB B } ]\n') }) test('doc.createAlias', () => { const doc = parseDocument('[{ a: A }, { b: B }]') const alias = doc.createAlias(doc.get(0), 'AA') - doc.value.items.push(alias) + doc.value.push(alias) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { b: 'B' }, { a: 'A' }]) const alias2 = doc.createAlias(doc.get(1), 'AA') expect(doc.get(1).anchor).toBe('AA1') @@ -81,12 +79,8 @@ describe('create', () => { const doc = new Document() const ref: unknown[] = [] const node = doc.createNode({ src: ref, ref }) - expect(node).toMatchObject({ - items: [ - { key: { value: 'src' }, value: { items: [], anchor: 'a1' } }, - { key: { value: 'ref' }, value: { source: 'a1' } } - ] - }) + expect(node).toMatchObject(_map({ src: [], ref: { source: 'a1' } })) + expect(node.get('src')!.anchor).toBe('a1') }) test('repeated Date in createNode', () => { @@ -94,12 +88,9 @@ describe('create', () => { const date = new Date() const value = date.toJSON() const node = doc.createNode({ src: date, ref: date }) - expect(node).toMatchObject({ - items: [ - { key: { value: 'src' }, value: { value, anchor: 'a1' } }, - { key: { value: 'ref' }, value: { source: 'a1' } } - ] - }) + expect(node).toMatchObject( + _map({ src: { value, anchor: 'a1' }, ref: { source: 'a1' } }) + ) }) }) @@ -114,7 +105,7 @@ describe('errors', () => { test('set tag on alias', () => { const doc = parseDocument, false>('[{ a: A }, { b: B }]') - const node = doc.value.items[0] + const node = doc.value[0] const alias = doc.createAlias(node, 'AA') expect(() => { // @ts-expect-error This is intentionally wrong. @@ -127,7 +118,7 @@ describe('errors', () => { '[{ a: A }, { b: B }]' ) const alias = doc.createAlias(doc.get(0), 'AA') - doc.value.items.unshift(alias) + doc.value.unshift(alias) expect(() => String(doc)).toThrow( 'Unresolved alias (the anchor must be set before the alias): AA' ) @@ -168,7 +159,7 @@ describe('__proto__ as anchor name', () => { const src = `- &__proto__ 1\n- *__proto__\n` const doc = parseDocument(src) expect(doc.errors).toHaveLength(0) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { anchor: '__proto__', value: 1 }, { source: '__proto__' } ]) @@ -180,8 +171,8 @@ describe('__proto__ as anchor name', () => { const doc = parseDocument, false>( '[{ a: A }, { b: B }]' ) - const alias = doc.createAlias(doc.value.items[0], '__proto__') - doc.value.items.push(alias) + const alias = doc.createAlias(doc.value[0], '__proto__') + doc.value.push(alias) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { b: 'B' }, { a: 'A' }]) expect(String(doc)).toMatch( '[ &__proto__ { a: A }, { b: B }, *__proto__ ]\n' @@ -250,13 +241,13 @@ describe('merge <<', () => { test('YAML.parseDocument', () => { const doc = parseDocument, false>(src, { merge: true }) - expect(doc.value.items.slice(5).map(it => it.items[0].value)).toMatchObject( - [ - { source: 'CENTER' }, - { items: [{ source: 'CENTER' }, { source: 'BIG' }] }, - { items: [{ source: 'BIG' }, { source: 'LEFT' }, { source: 'SMALL' }] } - ] - ) + expect( + doc.value.slice(5).map(it => it.values.values().next().value!.value) + ).toMatchObject([ + { source: 'CENTER' }, + [{ source: 'CENTER' }, { source: 'BIG' }], + [{ source: 'BIG' }, { source: 'LEFT' }, { source: 'SMALL' }] + ]) }) test('alias is associated with a sequence', () => { @@ -293,9 +284,9 @@ describe('merge <<', () => { '[{ a: A }, { b: B }]', { merge: true } ) - const [a, b] = doc.value.items + const [a, b] = doc.value const merge = doc.createPair('<<', doc.createAlias(a)) - b.items.push(merge) + b.set(merge) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { a: 'A', b: 'B' }]) expect(String(doc)).toBe('[ &a1 { a: A }, { b: B, <<: *a1 } ]\n') }) @@ -305,9 +296,9 @@ describe('merge <<', () => { '[{ a: A }, { b: B }]', { customTags: ['merge'] } ) - const [a, b] = doc.value.items + const [a, b] = doc.value const merge = doc.createPair('<<', doc.createAlias(a)) - b.items.push(merge) + b.set(merge) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { a: 'A', b: 'B' }]) expect(String(doc)).toBe('[ &a1 { a: A }, { b: B, <<: *a1 } ]\n') }) @@ -317,9 +308,9 @@ describe('merge <<', () => { '[{ a: A }, { b: B }]', { merge: true } ) - const [a, b] = doc.value.items + const [a, b] = doc.value const merge = doc.createPair(Symbol('<<'), doc.createAlias(a)) - b.items.push(merge) + b.set(merge) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { a: 'A', b: 'B' }]) expect(String(doc)).toBe('[ &a1 { a: A }, { b: B, <<: *a1 } ]\n') }) @@ -329,10 +320,10 @@ describe('merge <<', () => { '[{ a: A }, { b: B }]', { merge: true } ) - const [a, b] = doc.value.items + const [a, b] = doc.value const alias = doc.createAlias(a, 'AA') const merge = doc.createPair('<<', alias) - b.items.push(merge) + b.set(merge) expect(doc.toJS()).toMatchObject([{ a: 'A' }, { a: 'A', b: 'B' }]) expect(String(doc)).toBe('[ &AA { a: A }, { b: B, <<: *AA } ]\n') }) @@ -341,8 +332,8 @@ describe('merge <<', () => { const doc = parseDocument('[{ a: A }, { b: B }]', { merge: true }) - const alias = doc.createAlias(doc.getIn([0, 'a'])) - doc.addIn([1], doc.createPair('<<', alias)) + const alias = doc.createAlias(doc.get(0).get('a')) + doc.get(1).set(doc.createPair('<<', alias)) expect(String(doc)).toBe('[ { a: &a1 A }, { b: B, <<: *a1 } ]\n') expect(() => doc.toJS()).toThrow( 'Merge sources must be maps or map aliases' diff --git a/tests/doc/comments.ts b/tests/doc/comments.ts index 224d40fb..f9c43710 100644 --- a/tests/doc/comments.ts +++ b/tests/doc/comments.ts @@ -1,4 +1,4 @@ -import { source } from '../_utils.ts' +import { _map, _pair, _seq, source } from '../_utils.ts' import * as YAML from 'yaml' describe('parse comments', () => { @@ -94,15 +94,13 @@ describe('parse comments', () => { ` const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [ - { commentBefore: 'c0', value: 'value 1', comment: 'c1' }, - { value: 'value 2' } - ], - range: [4, 31, 31] - }, + value: [ + { commentBefore: 'c0', value: 'value 1', comment: 'c1' }, + { value: 'value 2' } + ], comment: 'c2' }) + expect(doc.value.range).toMatchObject([4, 31, 31]) }) test('multiline', () => { @@ -119,9 +117,7 @@ describe('parse comments', () => { ` const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [{ comment: 'c0' }, { commentBefore: 'c1\n\nc2' }] - }, + value: [{ comment: 'c0' }, { commentBefore: 'c1\n\nc2' }], comment: 'c3\nc4' }) }) @@ -140,12 +136,10 @@ describe('parse comments', () => { ` const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [ - { key: { commentBefore: 'c0' }, value: { comment: 'c1' } }, - { key: {}, value: {} } - ] - }, + value: _map({ + key1: _pair({ commentBefore: 'c0' }, { comment: 'c1' }), + key2: 'value 2' + }), comment: 'c2' }) }) @@ -164,12 +158,10 @@ describe('parse comments', () => { ` const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [ - { value: { comment: 'c0' } }, - { key: { commentBefore: 'c1\n\nc2' } } - ] - }, + value: _map({ + key1: { comment: 'c0' }, + key2: { key: { commentBefore: 'c1\n\nc2' }, value: {} } + }), comment: 'c3\nc4' }) }) @@ -187,22 +179,18 @@ describe('parse comments', () => { k3: v3 #c5 ` - const doc = YAML.parseDocument(src) + const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [ - { - commentBefore: 'c0\nc1', - items: [ - {}, - { key: { commentBefore: 'c2' }, value: { comment: 'c3' } }, - { key: { commentBefore: 'c4' } } - ] - } - ] - }, + value: [ + _map({ + k1: 'v1', + k2: _pair({ commentBefore: 'c2' }, { comment: 'c3' }), + k3: _pair({ commentBefore: 'c4' }, 'v3') + }) + ], comment: 'c5' }) + expect(doc.value[0].commentBefore).toBe('c0\nc1') expect(String(doc)).toBe(source` #c0 #c1 @@ -230,26 +218,21 @@ describe('parse comments', () => { - v3 #c4 #c5 ` - const doc = YAML.parseDocument(src) + const doc = YAML.parseDocument(src) expect(doc).toMatchObject({ - value: { - items: [ - { - key: { commentBefore: 'c0', value: 'k1' }, - value: { - commentBefore: 'c1', - items: [{ value: 'v1' }, { commentBefore: 'c2', value: 'v2' }], - comment: 'c3' - } - }, - { - key: { value: 'k2' }, - value: { items: [{ value: 'v3', comment: 'c4' }] } - } - ] - }, + value: _map({ + k1: _pair( + { commentBefore: 'c0', value: 'k1' }, + _seq('v1', { commentBefore: 'c2', value: 'v2' }) + ), + k2: _pair({ value: 'k2' }, _seq({ value: 'v3', comment: 'c4' })) + }), comment: 'c5' }) + expect(doc.get('k1')).toMatchObject({ + commentBefore: 'c1', + comment: 'c3' + }) expect(String(doc)).toBe(source` #c0 k1: @@ -272,7 +255,7 @@ describe('parse comments', () => { [ a, #c0 b #c1 ]`) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { value: 'a', comment: 'c0' }, { value: 'b', comment: 'c1' } ]) @@ -284,19 +267,23 @@ describe('parse comments', () => { b: c, #c1 d #c2 }`) - expect(doc.value.items).toMatchObject([ - { key: { value: 'a', comment: 'c0' } }, - { key: { value: 'b' }, value: { value: 'c', comment: 'c1' } }, - { key: { value: 'd', comment: 'c2' } } - ]) + expect(doc.value).toMatchObject( + _map({ + a: _pair({ value: 'a', comment: 'c0' }, null), + b: { value: 'c', comment: 'c1' }, + d: _pair({ value: 'd', comment: 'c2' }, null) + }) + ) }) test('multi-line comments', () => { const doc = YAML.parseDocument('{ a,\n#c0\n#c1\nb }') - expect(doc.value.items).toMatchObject([ - { key: { value: 'a' } }, - { key: { commentBefore: 'c0\nc1', value: 'b' } } - ]) + expect(doc.value).toMatchObject( + _map({ + a: { key: { value: 'a' }, value: {} }, + b: { key: { commentBefore: 'c0\nc1', value: 'b' }, value: {} } + }) + ) }) }) @@ -386,8 +373,8 @@ describe('stringify comments', () => { const src = '- value 1\n- value 2\n' const doc = YAML.parseDocument, false>(src) doc.value.commentBefore = 'c0' - doc.value.items[0].commentBefore = 'c1' - doc.value.items[1].commentBefore = 'c2' + doc.value[0].commentBefore = 'c1' + doc.value[1].commentBefore = 'c2' doc.value.comment = 'c3' expect(String(doc)).toBe(source` #c0 @@ -402,8 +389,8 @@ describe('stringify comments', () => { test('multiline', () => { const src = '- value 1\n- value 2\n' const doc = YAML.parseDocument, false>(src) - doc.value.items[0].commentBefore = 'c0\nc1' - doc.value.items[1].commentBefore = ' \nc2\n\nc3' + doc.value[0].commentBefore = 'c0\nc1' + doc.value[1].commentBefore = ' \nc2\n\nc3' doc.value.comment = 'c4\nc5' expect(String(doc)).toBe(source` #c0 @@ -425,13 +412,13 @@ describe('stringify comments', () => { YAML.YAMLMap>, false >(src) - doc.value.items[0].key.commentBefore = 'c0' - doc.value.items[0].key.comment = 'c1' - const seq = doc.value.items[0].value! - seq.commentBefore = 'c2' - seq.items[0].commentBefore = 'c3' - seq.items[1].commentBefore = 'c4' - seq.comment = 'c5' + const { key, value: seq } = doc.value.getPair('map')! + key.commentBefore = 'c0' + key.comment = 'c1' + seq!.commentBefore = 'c2' + seq![0].commentBefore = 'c3' + seq![1].commentBefore = 'c4' + seq!.comment = 'c5' expect(String(doc)).toBe(source` #c0 map: #c1 @@ -449,8 +436,8 @@ describe('stringify comments', () => { '- a\n- b\n' ) doc.value.commentBefore = 'c0' - doc.value.items[0].commentBefore = 'c1' - doc.value.items[1].commentBefore = 'c2\nc3' + doc.value[0].commentBefore = 'c1' + doc.value[1].commentBefore = 'c2\nc3' expect(doc.toString({ commentString: str => str.replace(/^/gm, '// ') })) .toBe(source` // c0 @@ -470,10 +457,12 @@ describe('stringify comments', () => { YAML.YAMLMap, false >(src) - doc.value.items[0].key.commentBefore = 'c0' - doc.value.items[1].key.commentBefore = 'c1' - doc.value.items[1].key.comment = 'c2' - doc.value.items[1].value!.spaceBefore = true + const p1 = doc.getPair('key1')! + p1.key.commentBefore = 'c0' + const p2 = doc.getPair('key2')! + p2.key.commentBefore = 'c1' + p2.key.comment = 'c2' + p2.value!.spaceBefore = true doc.value.comment = 'c3' expect(String(doc)).toBe(source` #c0 @@ -492,11 +481,13 @@ describe('stringify comments', () => { YAML.YAMLMap, false >(src) - doc.value.items[0].key.commentBefore = 'c0\nc1' - doc.value.items[1].key.commentBefore = ' \nc2\n\nc3' - doc.value.items[1].key.comment = 'c4\nc5' - doc.value.items[1].value!.spaceBefore = true - doc.value.items[1].value!.commentBefore = 'c6' + const p1 = doc.getPair('key1')! + p1.key.commentBefore = 'c0\nc1' + const p2 = doc.getPair('key2')! + p2.key.commentBefore = ' \nc2\n\nc3' + p2.key.comment = 'c4\nc5' + p2.value!.spaceBefore = true + p2.value!.commentBefore = 'c6' doc.value.comment = 'c7\nc8' expect(String(doc)).toBe(source` #c0 @@ -736,24 +727,41 @@ describe('blank lines', () => { }) describe('not after block scalar with keep chomping', () => { - const cases = [ - { name: 'in seq', src: '- |+\n a\n\n- b\n' }, - { name: 'in map', src: 'a: |+\n A\n\nb: B\n' }, - { name: 'in seq in map', src: 'a:\n - |+\n A\n\nb: B\n' } - ] - for (const { name, src } of cases) { - test(name, () => { - const doc = YAML.parseDocument(src) - expect(String(doc)).toBe(src) - let it = doc.value.items[1] - if (it.key) it = it.key - expect(it).not.toHaveProperty('spaceBefore', true) - it.spaceBefore = true - expect(String(doc)).toBe(src) - it.commentBefore = '\n\n' - expect(String(doc)).toBe(src) - }) - } + test('in seq', () => { + const src = '- |+\n a\n\n- b\n' + const doc = YAML.parseDocument(src) + expect(String(doc)).toBe(src) + const it = doc.value[1] + expect(it).not.toHaveProperty('spaceBefore', true) + it.spaceBefore = true + expect(String(doc)).toBe(src) + it.commentBefore = '\n\n' + expect(String(doc)).toBe(src) + }) + + test('in map', () => { + const src = 'a: |+\n A\n\nb: B\n' + const doc = YAML.parseDocument(src) + expect(String(doc)).toBe(src) + const it = doc.getPair('b')!.key + expect(it).not.toHaveProperty('spaceBefore', true) + it.spaceBefore = true + expect(String(doc)).toBe(src) + it.commentBefore = '\n\n' + expect(String(doc)).toBe(src) + }) + + test('in seq in map', () => { + const src = 'a:\n - |+\n A\n\nb: B\n' + const doc = YAML.parseDocument(src) + expect(String(doc)).toBe(src) + const it = doc.getPair('b')!.key + expect(it).not.toHaveProperty('spaceBefore', true) + it.spaceBefore = true + expect(String(doc)).toBe(src) + it.commentBefore = '\n\n' + expect(String(doc)).toBe(src) + }) test('as value', () => { const src = '|+\n a\n\n#c\n' @@ -771,18 +779,12 @@ describe('blank lines', () => { test('before block map values', () => { const src = 'a:\n\n 1\nb:\n\n #c\n 2\n' const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { - key: { value: 'a' }, - value: { value: 1, spaceBefore: true } - }, - { - key: { value: 'b' }, - value: { value: 2, commentBefore: 'c', spaceBefore: true } - } - ] - }) + expect(doc.value).toMatchObject( + _map({ + a: { value: 1, spaceBefore: true }, + b: { value: 2, commentBefore: 'c', spaceBefore: true } + }) + ) expect(String(doc)).toBe(src) }) @@ -806,43 +808,41 @@ describe('blank lines', () => { test('flow seq', () => { const src = '[1,\n\n2,\n3,\n\n4\n\n]' const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { value: 1 }, - { value: 2, spaceBefore: true }, - { value: 3 }, - { value: 4, spaceBefore: true } - ] - }) + expect(doc.value).toMatchObject( + _seq(1, { value: 2, spaceBefore: true }, 3, { + value: 4, + spaceBefore: true + }) + ) expect(String(doc)).toBe('[\n 1,\n\n 2,\n 3,\n\n 4\n]\n') }) test('flow map', () => { const src = '{\n\na: 1,\n\nb: 2 }' const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { key: { value: 'a', spaceBefore: true }, value: { value: 1 } }, - { key: { value: 'b', spaceBefore: true }, value: { value: 2 } } - ] - }) + expect(doc.value).toMatchObject( + _map( + { + a: _pair({ value: 'a', spaceBefore: true }, 1), + b: _pair({ value: 'b', spaceBefore: true }, 2) + }, + { flow: true } + ) + ) }) test('flow map value comments & spaces', () => { const src = '{\n a:\n #c\n 1,\n b:\n\n #d\n 2\n}\n' const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ + expect(doc.value).toMatchObject( + _map( { - key: { value: 'a' }, - value: { value: 1, commentBefore: 'c' } + a: { value: 1, commentBefore: 'c' }, + b: { value: 2, commentBefore: 'd', spaceBefore: true } }, - { - key: { value: 'b' }, - value: { value: 2, commentBefore: 'd', spaceBefore: true } - } - ] - }) + { flow: true } + ) + ) expect(String(doc)).toBe(src) }) }) @@ -931,10 +931,9 @@ map: # c4 ` const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { value: { commentBefore: ' c1\n \n c2\n', comment: ' c3\n \n c4' } } - ] + expect(doc.get('key')).toMatchObject({ + commentBefore: ' c1\n \n c2\n', + comment: ' c3\n \n c4' }) expect(doc.toString()).toBe(source` key: @@ -963,10 +962,9 @@ map: # c4 ` const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { value: { commentBefore: ' c1\n\n c2\n', comment: ' c3\n\n c4' } } - ] + expect(doc.get('key')).toMatchObject({ + commentBefore: ' c1\n\n c2\n', + comment: ' c3\n\n c4' }) expect(doc.toString()).toBe(source` key: @@ -993,12 +991,10 @@ map: - v2 ` const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { commentBefore: ' c1\n \n c2', comment: ' c3\n ' }, - { commentBefore: ' c4' } - ] - }) + expect(doc.value).toMatchObject([ + { commentBefore: ' c1\n \n c2', comment: ' c3\n ' }, + { commentBefore: ' c4' } + ]) expect(doc.toString()).toBe(source` # c1 # @@ -1023,12 +1019,10 @@ map: - v2 ` const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { commentBefore: ' c1\n\n c2', comment: ' c3' }, - { commentBefore: ' c4', spaceBefore: true } - ] - }) + expect(doc.value).toMatchObject([ + { commentBefore: ' c1\n\n c2', comment: ' c3' }, + { commentBefore: ' c4', spaceBefore: true } + ]) expect(doc.toString()).toBe(source` # c1 @@ -1046,7 +1040,7 @@ map: const doc = YAML.parseDocument, false>( '- v1\n- v2\n' ) - const [v1, v2] = doc.value.items + const [v1, v2] = doc.value v1.commentBefore = '\n' v1.comment = '\n' v2.commentBefore = '\n' @@ -1066,10 +1060,11 @@ map: YAML.YAMLMap, false >('k1: v1\nk2: v2') - const [p1, p2] = doc.value.items + const p1 = doc.getPair('k1')! p1.key.commentBefore = '\n' p1.value!.commentBefore = '\n' p1.value!.comment = '\n' + const p2 = doc.getPair('k2')! p2.key.commentBefore = '\n' p2.value!.commentBefore = '\n' p2.value!.comment = '\n' @@ -1188,13 +1183,12 @@ describe('collection end comments', () => { #2 - d ` - const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { items: [{ value: 'a' }, { value: 'b' }], comment: '1\n\n2' }, - { value: 'd' } - ] - }) + const doc = YAML.parseDocument(src) + expect(doc.value).toMatchObject([ + [{ value: 'a' }, { value: 'b' }], + { value: 'd' } + ]) + expect(doc.value[0].comment).toBe('1\n\n2') expect(String(doc)).toBe(source` #0 - - a @@ -1216,19 +1210,10 @@ describe('collection end comments', () => { #2 - d ` - const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { - items: [ - { key: { value: 'a' }, value: { value: 1 } }, - { key: { value: 'b' }, value: { value: 2 } } - ], - comment: '1\n\n2' - }, - { value: 'd' } - ] - }) + const doc = YAML.parseDocument(src) + expect(doc.value).toMatchObject( + _seq(_map({ a: 1, b: 2 }, { comment: '1\n\n2' }), 'd') + ) expect(String(doc)).toBe(source` #0 - a: 1 @@ -1251,16 +1236,9 @@ describe('collection end comments', () => { #2 d: 1 ` - const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { - key: { value: 'a' }, - value: { items: [{ value: 'b' }, { value: 'c' }], comment: '1\n\n2' } - }, - { key: { value: 'd' }, value: { value: 1 } } - ] - }) + const doc = YAML.parseDocument(src) + expect(doc.value).toMatchObject(_map({ a: _seq('b', 'c'), d: 1 })) + expect(doc.get('a')).toMatchObject({ comment: '1\n\n2' }) expect(String(doc)).toBe(source` #0 a: @@ -1284,22 +1262,10 @@ describe('collection end comments', () => { #2 d: 1 ` - const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { - key: { value: 'a' }, - value: { - items: [ - { key: { value: 'b' }, value: { value: 1 } }, - { key: { value: 'c' }, value: { value: 2 } } - ], - comment: '1\n\n2' - } - }, - { key: { value: 'd' }, value: { value: 1 } } - ] - }) + const doc = YAML.parseDocument(src) + expect(doc.value).toMatchObject( + _map({ a: _map({ b: 1, c: 2 }, { comment: '1\n\n2' }), d: 1 }) + ) expect(String(doc)).toBe(source` #0 a: @@ -1321,28 +1287,17 @@ a: #2 - e\n` - const doc = YAML.parseDocument(src) - expect(doc.value).toMatchObject({ - items: [ - { - key: { value: 'a' }, - value: { - commentBefore: '1', - items: [ - { - items: [ - { - key: { value: 'b' }, - value: { items: [{ value: 'c' }] } - } - ] - }, - { spaceBefore: true, commentBefore: '2', value: 'e' } - ] - } - } - ] - }) + const doc = YAML.parseDocument(src) + expect(doc.value).toMatchObject( + _map({ + a: _seq(_map({ b: _seq('c') }), { + spaceBefore: true, + commentBefore: '2', + value: 'e' + }) + }) + ) + expect(doc.get('a')).toMatchObject({ commentBefore: '1' }) expect(String(doc)).toBe(src) }) }) diff --git a/tests/doc/createNode.ts b/tests/doc/createNode.ts index 842c64ce..a3f6b61e 100644 --- a/tests/doc/createNode.ts +++ b/tests/doc/createNode.ts @@ -1,6 +1,6 @@ import type { Alias, Node } from 'yaml' import { Document, Scalar, YAMLMap, YAMLSeq } from 'yaml' -import { source } from '../_utils.ts' +import { _map, _pair, _seq, _set, source } from '../_utils.ts' describe('createNode(value)', () => { test('boolean', () => { @@ -48,14 +48,14 @@ describe('arrays', () => { test('createNode([])', () => { const s = new Document().createNode([]) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toHaveLength(0) + expect(s).toHaveLength(0) }) test('createNode([true])', () => { const doc = new Document() const s = doc.createNode([true]) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([{ value: true }]) + expect(s).toMatchObject([{ value: true }]) doc.value = s expect(String(doc)).toBe('- true\n') }) @@ -64,7 +64,7 @@ describe('arrays', () => { const doc = new Document() const s = doc.createNode([true], { flow: true }) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([{ value: true }]) + expect(s).toMatchObject([{ value: true }]) doc.value = s expect(String(doc)).toBe('[ true ]\n') }) @@ -74,12 +74,12 @@ describe('arrays', () => { test('createNode(value)', () => { const s = new Document().createNode(array) as any expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toHaveLength(2) - expect(s.items[0].value).toBe(3) - expect(s.items[1]).toBeInstanceOf(YAMLSeq) - expect(s.items[1].items).toHaveLength(2) - expect(s.items[1].items[0].value).toBe('four') - expect(s.items[1].items[1].value).toBe(5) + expect(s).toHaveLength(2) + expect(s[0].value).toBe(3) + expect(s[1]).toBeInstanceOf(YAMLSeq) + expect(s[1]).toHaveLength(2) + expect(s[1][0].value).toBe('four') + expect(s[1][1].value).toBe(5) }) test('set doc value', () => { const res = '- 3\n- - four\n - 5\n' @@ -95,16 +95,14 @@ describe('objects', () => { test('createNode({})', () => { const s = new Document().createNode({}) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toHaveLength(0) + expect(s.size).toBe(0) }) test('createNode({ x: true })', () => { const doc = new Document() const s = doc.createNode({ x: true }) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } } - ]) + expect(s).toMatchObject(_map({ x: true })) doc.value = s expect(String(doc)).toBe('x: true\n') }) @@ -113,9 +111,7 @@ describe('objects', () => { const doc = new Document() const s = doc.createNode({ x: true }, { flow: true }) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } } - ]) + expect(s).toMatchObject(_map({ x: true }, { flow: true })) doc.value = s expect(String(doc)).toBe('{ x: true }\n') }) @@ -123,9 +119,7 @@ describe('objects', () => { test('createNode({ x: true, y: undefined })', () => { const s = new Document().createNode({ x: true, y: undefined }) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } } - ]) + expect(s).toMatchObject(_map({ x: true })) }) test('createNode({ x: true, y: undefined }, { keepUndefined: true })', () => { @@ -134,19 +128,14 @@ describe('objects', () => { { keepUndefined: true } ) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } }, - { key: { value: 'y' }, value: null } - ]) + expect(s).toMatchObject(_map({ x: true, y: null })) }) test('createNode(pair)', () => { const pair = new Document().createPair('x', true) const s = new Document().createNode(pair) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } } - ]) + expect(s).toMatchObject(_map({ x: true })) }) describe('{ x: 3, y: [4], z: { w: "five", v: 6 } }', () => { @@ -154,20 +143,10 @@ describe('objects', () => { test('createNode(value)', () => { const s = new Document().createNode(object) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toHaveLength(3) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: 3 } }, - { key: { value: 'y' }, value: { items: [{ value: 4 }] } }, - { - key: { value: 'z' }, - value: { - items: [ - { key: { value: 'w' }, value: { value: 'five' } }, - { key: { value: 'v' }, value: { value: 6 } } - ] - } - } - ]) + expect(s.size).toBe(3) + expect(s).toMatchObject( + _map({ x: 3, y: _seq(4), z: _map({ w: 'five', v: 6 }) }) + ) }) test('set doc value', () => { const res = `x: 3 @@ -188,25 +167,29 @@ describe('Set', () => { test('createNode(new Set)', () => { const s = new Document().createNode(new Set()) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toHaveLength(0) + expect(s).toHaveLength(0) }) + test('createNode(new Set([true]))', () => { const s = new Document().createNode(new Set([true])) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([{ value: true }]) + expect(s).toMatchObject([{ value: true }]) }) + describe("Set { 3, Set { 'four', 5 } }", () => { const set = new Set([3, new Set(['four', 5])]) + test('createNode(set)', () => { const s = new Document().createNode(set) as any expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toHaveLength(2) - expect(s.items[0].value).toBe(3) - expect(s.items[1]).toBeInstanceOf(YAMLSeq) - expect(s.items[1].items).toHaveLength(2) - expect(s.items[1].items[0].value).toBe('four') - expect(s.items[1].items[1].value).toBe(5) + expect(s).toHaveLength(2) + expect(s[0].value).toBe(3) + expect(s[1]).toBeInstanceOf(YAMLSeq) + expect(s[1]).toHaveLength(2) + expect(s[1][0].value).toBe('four') + expect(s[1][1].value).toBe(5) }) + test('set doc value', () => { const res = '- 3\n- - four\n - 5\n' const doc = new Document(set) @@ -214,23 +197,21 @@ describe('Set', () => { doc.value = doc.createNode(set) expect(String(doc)).toBe(res) }) + test('Schema#createNode() - YAML 1.2', () => { const doc = new Document(null) const s = doc.createNode(set) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([ - { value: 3 }, - { items: [{ value: 'four' }, { value: 5 }] } - ]) + expect(s).toMatchObject([{ value: 3 }, [{ value: 'four' }, { value: 5 }]]) }) + test('Schema#createNode() - YAML 1.1', () => { const doc = new Document(null, { version: '1.1' }) const s = doc.createNode(set) as any - expect(s.constructor.tag).toBe('tag:yaml.org,2002:set') - expect(s.items).toMatchObject([ - { key: { value: 3 } }, - { key: { items: [{ key: { value: 'four' } }, { key: { value: 5 } }] } } - ]) + expect(s.constructor.tagName).toBe('tag:yaml.org,2002:set') + expect(s).toMatchObject( + _set([3, [new Set(['four', 5]), _set(['four', 5])]]) + ) }) }) }) @@ -239,45 +220,37 @@ describe('Map', () => { test('createNode(new Map)', () => { const s = new Document().createNode(new Map()) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toHaveLength(0) + expect(s.size).toBe(0) }) test('createNode(new Map([["x", true]]))', () => { const s = new Document().createNode(new Map([['x', true]])) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: true } } - ]) + expect(s).toMatchObject(_map({ x: true })) }) describe("Map { 'x' => 3, 'y' => Set { 4 }, Map { 'w' => 'five', 'v' => 6 } => 'z' }", () => { - const map = new Map([ + const mapKey = new Map([ + ['w', 'five'], + ['v', 6] + ]) + const map = new Map([ ['x', 3], ['y', new Set([4])], - [ - new Map([ - ['w', 'five'], - ['v', 6] - ]), - 'z' - ] + [mapKey, 'z'] ]) + test('createNode(map)', () => { const s = new Document().createNode(map) expect(s).toBeInstanceOf(YAMLMap) - expect(s.items).toHaveLength(3) - expect(s.items).toMatchObject([ - { key: { value: 'x' }, value: { value: 3 } }, - { key: { value: 'y' }, value: { items: [{ value: 4 }] } }, - { - key: { - items: [ - { key: { value: 'w' }, value: { value: 'five' } }, - { key: { value: 'v' }, value: { value: 6 } } - ] - }, - value: { value: 'z' } - } - ]) + expect(s.size).toBe(3) + expect(s).toMatchObject( + _map([ + ['x', 3], + ['y', _seq(4)], + [mapKey, _pair(_map({ w: 'five', v: 6 }), 'z')] + ]) + ) }) + test('set doc value', () => { const res = `x: 3 y: @@ -306,13 +279,8 @@ describe('strictly equal objects', () => { const foo = { foo: 'FOO' } const s = new Document().createNode([foo, foo]) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([ - { - anchor: 'a1', - items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] - }, - { source: 'a1' } - ]) + expect(s).toMatchObject(_seq(_map({ foo: 'FOO' }), { source: 'a1' })) + expect(s[0].anchor).toBe('a1') }) test('createNode([foo, foo], { aliasDuplicateObjects: false })', () => { @@ -321,10 +289,7 @@ describe('strictly equal objects', () => { aliasDuplicateObjects: false }) expect(s).toBeInstanceOf(YAMLSeq) - expect(s.items).toMatchObject([ - { items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] }, - { items: [{ key: { value: 'foo' }, value: { value: 'FOO' } }] } - ]) + expect(s).toMatchObject(_seq(_map({ foo: 'FOO' }), _map({ foo: 'FOO' }))) }) }) @@ -333,16 +298,9 @@ describe('circular references', () => { const map: any = { foo: 'bar' } map.map = map const doc = new Document(map) - expect(doc.value).toMatchObject({ - anchor: 'a1', - items: [ - { key: { value: 'foo' }, value: { value: 'bar' } }, - { - key: { value: 'map' }, - value: { source: 'a1' } - } - ] - }) + expect(doc.value).toMatchObject( + _map({ foo: 'bar', map: { source: 'a1' } }, { anchor: 'a1' }) + ) expect(doc.toString()).toBe(source` &a1 foo: bar @@ -354,8 +312,8 @@ describe('circular references', () => { const baz: any = {} const map = { foo: { bar: { baz } } } baz.map = map - const doc = new Document(map) - expect(doc.getIn(['foo', 'bar', 'baz', 'map'])).toMatchObject({ + const doc = new Document(map) + expect(doc.get('foo').get('bar').get('baz').get('map')).toMatchObject({ source: 'a1' }) expect(doc.toString()).toBe(source` @@ -372,15 +330,13 @@ describe('circular references', () => { const two = ['two'] const seq = [one, two, one, one, two] const doc = new Document(seq) - expect(doc.value).toMatchObject({ - items: [ - { items: [{ value: 'one' }] }, - { items: [{ value: 'two' }] }, - { source: 'a1' }, - { source: 'a1' }, - { source: 'a2' } - ] - }) + expect(doc.value).toMatchObject([ + [{ value: 'one' }], + [{ value: 'two' }], + { source: 'a1' }, + { source: 'a1' }, + { source: 'a2' } + ]) expect(doc.toString()).toBe(source` - &a1 - one @@ -396,12 +352,10 @@ describe('circular references', () => { const baz = { a: 1 } const seq = [{ foo: { bar: { baz } } }, { fe: { fi: { fo: { baz } } } }] const doc = new Document(null) - const node = doc.createNode(seq) - const source = node.getIn([0, 'foo', 'bar', 'baz']) as Node - const alias = node.getIn([1, 'fe', 'fi', 'fo', 'baz']) as Alias - expect(source).toMatchObject({ - items: [{ key: { value: 'a' }, value: { value: 1 } }] - }) + const node = doc.createNode(seq) as any + const source = node[0].get('foo').get('bar').get('baz') as Node + const alias = node[1].get('fe').get('fi').get('fo').get('baz') as Alias + expect(source).toMatchObject(_map({ a: 1 })) expect(alias.source).toBe(source.anchor) }) }) diff --git a/tests/doc/errors.ts b/tests/doc/errors.ts index a518d50c..df66b83b 100644 --- a/tests/doc/errors.ts +++ b/tests/doc/errors.ts @@ -1,5 +1,5 @@ import * as YAML from 'yaml' -import { source } from '../_utils.ts' +import { _map, _seq, source } from '../_utils.ts' describe('tabs as indentation', () => { test('fail on map value indented with tab', () => { @@ -66,12 +66,7 @@ describe('block collections', () => { expect(doc.errors[0].message).toMatch( 'All mapping items must start at the same column' ) - expect(doc.value).toMatchObject({ - items: [ - { key: { value: 'foo' }, value: { value: '1' } }, - { key: { value: 'bar' }, value: { value: 2 } } - ] - }) + expect(doc.value).toMatchObject(_map({ foo: '1', bar: 2 })) }) test('sequence with bad indentation', () => { @@ -81,9 +76,7 @@ describe('block collections', () => { expect(doc.errors[0].message).toMatch( 'All sequence items must start at the same column' ) - expect(doc.value).toMatchObject({ - items: [{ value: 'foo' }, { items: [{ value: 'bar' }] }] - }) + expect(doc.value).toMatchObject(_seq('foo', _seq('bar'))) }) test('seq item in mapping', () => { @@ -94,12 +87,12 @@ describe('block collections', () => { { code: 'UNEXPECTED_TOKEN' }, { code: 'MISSING_CHAR' } ]) - expect(doc.value).toMatchObject({ - items: [ - { key: { value: 'foo' }, value: { value: '1' } }, - { key: { value: null }, value: null } - ] - }) + expect(doc.value).toMatchObject( + _map([ + ['foo', '1'], + [null, { key: { value: null }, value: null }] + ]) + ) }) test('doubled value indicator', () => { diff --git a/tests/doc/foldFlowLines.ts b/tests/doc/foldFlowLines.ts index c95bb1c9..20a3c291 100644 --- a/tests/doc/foldFlowLines.ts +++ b/tests/doc/foldFlowLines.ts @@ -160,7 +160,7 @@ describe('double-quoted', () => { const str = YAML.stringify({ x }) const doc = YAML.parseDocument(str) expect(doc.errors).toHaveLength(0) - expect(doc.value.items[0].value.value).toBe(x) + expect(doc.value.get('x').value).toBe(x) }) }) @@ -188,7 +188,7 @@ describe('double-quoted', () => { const str = YAML.stringify({ key: [[value]] }) const doc = YAML.parseDocument(str) expect(doc.errors).toHaveLength(0) - expect(doc.value.items[0].value.items[0].items[0].value).toBe(value) + expect(doc.value.get('key')[0][0].value).toBe(value) }) }) @@ -332,10 +332,10 @@ describe('end-to-end', () => { - plain with comment # that won't get folded ` const doc = YAML.parseDocument, false>(src) - expect(doc.value.items[0].value).toBe( + expect(doc.value[0].value).toBe( 'plain value with enough length to fold twice' ) - expect(doc.value.items[1].value).toBe('plain with comment') + expect(doc.value[1].value).toBe('plain with comment') expect(doc.toString(foldOptions)).toBe(src) }) diff --git a/tests/doc/parse.ts b/tests/doc/parse.ts index 1d2de131..b866c754 100644 --- a/tests/doc/parse.ts +++ b/tests/doc/parse.ts @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import * as YAML from 'yaml' -import { source } from '../_utils.ts' +import { _map, _pair, _seq, source } from '../_utils.ts' let readArtifact if (typeof window === 'undefined') { @@ -173,18 +173,14 @@ test('long scalar value in flow map (#36)', () => { describe('flow collection keys', () => { test('block map with flow collection key as explicit key', () => { - const doc = YAML.parseDocument(`? []: x`) + const doc = YAML.parseDocument>(`? []: x`) expect(doc.errors).toHaveLength(0) - expect(doc.value).toMatchObject({ - items: [ - { - key: { - items: [{ key: { items: [], flow: true }, value: { value: 'x' } }] - }, - value: null - } - ] - }) + expect(doc.value).toMatchObject(_map([[_map([[_seq(), 'x']]), null]])) + for (const outer of doc.value.pairs()) { + for (const inner of outer.key.pairs()) { + expect(inner.key).toMatchObject({ flow: true }) + } + } }) test('flow collection as first block map key (redhat-developer/vscode-yaml#712)', () => { @@ -194,19 +190,14 @@ describe('flow collection keys', () => { c: d `) expect(doc.errors).toHaveLength(0) - expect(doc.value).toMatchObject({ - items: [ - { - key: { value: 'a' }, - value: { - items: [ - { key: { items: [] }, value: { value: 'b' } }, - { key: { value: 'c' }, value: { value: 'd' } } - ] - } - } - ] - }) + expect(doc.value).toMatchObject( + _map({ + a: _map([ + [_seq(), 'b'], + ['c', 'd'] + ]) + }) + ) }) test('flow collection as second block map key (redhat-developer/vscode-yaml#712)', () => { @@ -217,34 +208,25 @@ describe('flow collection keys', () => { c: d `) expect(doc.errors).toHaveLength(0) - expect(doc.value).toMatchObject({ - items: [ - { key: { value: 'x' }, value: { value: 'y' } }, - { - key: { value: 'a' }, - value: { - items: [ - { key: { items: [] }, value: { value: 'b' } }, - { key: { value: 'c' }, value: { value: 'd' } } - ] - } - } - ] - }) + expect(doc.value).toMatchObject( + _map({ + x: 'y', + a: _map([ + [_seq(), 'b'], + ['c', 'd'] + ]) + }) + ) }) test('empty scalar as last flow collection value (#550)', () => { const doc = YAML.parseDocument('{c:}') - expect(doc.value.items).toMatchObject([ - { key: { value: 'c' }, value: { value: null } } - ]) + expect(doc.value).toMatchObject(_map({ c: { value: null } })) }) test('plain key with no space before flow collection value (#550)', () => { const doc = YAML.parseDocument('{c:[]}') - expect(doc.value.items).toMatchObject([ - { key: { value: 'c' }, value: { items: [] } } - ]) + expect(doc.value).toMatchObject(_map({ c: _seq() })) }) }) @@ -321,8 +303,10 @@ describe('empty(ish) nodes', () => { const src = '{ ? : 123 }' const doc = YAML.parseDocument(src) expect(doc.errors).toHaveLength(0) - expect(doc.value.items[0].key.value).toBeNull() - expect(doc.value.items[0].value.value).toBe(123) + expect(doc.getPair(null)).toMatchObject({ + key: { value: null }, + value: { value: 123 } + }) }) describe('comment on empty pair value (#19)', () => { @@ -357,8 +341,8 @@ describe('empty(ish) nodes', () => { }) test('empty node position', () => { - const doc = YAML.parseDocument('\r\na: # 123\r\n') - const empty = doc.value.items[0].value + const doc = YAML.parseDocument('\r\na: # 123\r\n') + const empty = doc.get('a') expect(empty.range).toEqual([5, 5, 12]) }) @@ -393,7 +377,7 @@ describe('maps with no values', () => { const src = `{\na: null,\n? b\n}` const doc = YAML.parseDocument(src) expect(String(doc)).toBe(`{ a: null, b: }\n`) - doc.value.items[1].key.comment = 'c' + doc.getPair('b')!.key.comment = 'c' expect(String(doc)).toBe(`{\n a: null,\n b: #c\n}\n`) doc.set('b', 'x') expect(String(doc)).toBe(`{\n a: null,\n b: #c\n x\n}\n`) @@ -412,18 +396,22 @@ describe('maps with no values', () => { test('implicit scalar key after explicit key with no value', () => { const doc = YAML.parseDocument('? - 1\nx:\n') - expect(doc.value.items).toMatchObject([ - { key: { items: [{ value: 1 }] }, value: null }, - { key: { value: 'x' }, value: { value: null } } - ]) + expect(doc.value).toMatchObject( + _map([ + [_seq(1), null], + ['x', { value: null }] + ]) + ) }) test('implicit flow collection key after explicit key with no value', () => { const doc = YAML.parseDocument('? - 1\n[x]: y\n') - expect(doc.value.items).toMatchObject([ - { key: { items: [{ value: 1 }] }, value: null }, - { key: { items: [{ value: 'x' }] }, value: { value: 'y' } } - ]) + expect(doc.value).toMatchObject( + _map([ + [_seq(1), null], + [_seq('x'), 'y'] + ]) + ) }) }) @@ -431,10 +419,12 @@ describe('odd indentations', () => { test('Block map with empty explicit key (#551)', () => { const doc = YAML.parseDocument('?\n? a') expect(doc.errors).toHaveLength(0) - expect(doc.value.items).toMatchObject([ - { key: { value: null }, value: null }, - { key: { value: 'a' }, value: null } - ]) + expect(doc.value).toMatchObject( + _map([ + [null, _pair({ value: null }, null)], + ['a', null] + ]) + ) }) test('Block map with unindented !!null explicit key', () => { @@ -590,14 +580,14 @@ describe('duplicate keys', () => { test('disable with option', () => { const doc = YAML.parseDocument('foo: 1\nbar: 2\nfoo: 3\n', { - uniqueKeys: false + mapKey: () => Symbol() }) expect(doc.errors).toMatchObject([]) }) test('customise with option', () => { const doc = YAML.parseDocument('foo: 1\nbar: 2\nfoo: 3\n', { - uniqueKeys: () => true + mapKey: () => true }) expect(doc.errors).toMatchObject([ { code: 'DUPLICATE_KEY' }, @@ -703,7 +693,7 @@ describe('keepSourceTokens', () => { test(`${type}: default false`, () => { const doc = YAML.parseDocument(src) expect(doc.value).not.toHaveProperty('srcToken') - expect(doc.value.items[0]).not.toHaveProperty('srcToken') + expect(doc.getPair('foo')).not.toHaveProperty('srcToken') expect(doc.get('foo')).not.toHaveProperty('srcToken') }) @@ -712,7 +702,7 @@ describe('keepSourceTokens', () => { keepSourceTokens: true }) expect(doc.value.srcToken).toMatchObject({ type }) - expect(doc.value.items[0].srcToken).toMatchObject({ + expect(doc.getPair('foo').srcToken).toMatchObject({ key: { type: 'scalar' }, value: { type: 'scalar' } }) @@ -934,15 +924,17 @@ describe('stringKeys', () => { `, { stringKeys: true } ) - expect(doc.value.items).toMatchObject([ - { key: { value: 'x' }, value: { value: 'x' } }, - { key: { value: 'y' }, value: { value: 'y' } }, - { key: { value: '42' }, value: { value: 42 } }, - { key: { value: 'true' }, value: { value: true } }, - { key: { value: 'null' }, value: { value: null } }, - { key: { value: '~' }, value: { value: null } }, - { key: { value: '' }, value: { value: null } } - ]) + expect(doc.value).toMatchObject( + _map({ + x: 'x', + y: 'y', + '42': 42, + true: true, + null: { value: null }, + '~': { value: null }, + '': { value: null } + }) + ) }) test('explicit non-string tag', () => { diff --git a/tests/doc/stringify.ts b/tests/doc/stringify.ts index 48d0710a..3c0d866d 100644 --- a/tests/doc/stringify.ts +++ b/tests/doc/stringify.ts @@ -150,8 +150,8 @@ blah blah\n`) YAML.YAMLMap, false >({ foo }, { version }) - for (const node of doc.value.items) - node.value!.type = Scalar.QUOTE_DOUBLE + for (const pair of doc.value.values.values()) + pair.value!.type = Scalar.QUOTE_DOUBLE expect( doc .toString(opt) @@ -165,7 +165,7 @@ blah blah\n`) const doc = new YAML.Document, false>([foo], { version }) - for (const node of doc.value.items) node.type = Scalar.QUOTE_DOUBLE + for (const node of doc.value) node.type = Scalar.QUOTE_DOUBLE expect( doc .toString(opt) @@ -180,8 +180,8 @@ blah blah\n`) YAML.YAMLMap>, false >({ foo: [foo] }, { version }) - const seq = doc.value.items[0].value! - for (const node of seq.items) node.type = Scalar.QUOTE_DOUBLE + const seq = doc.value.get('foo')! + for (const node of seq) node.type = Scalar.QUOTE_DOUBLE expect( doc .toString(opt) @@ -330,12 +330,11 @@ z: ) }) - test('Map with non-Pair item', () => { + test('pushing non-Pair item', () => { const doc = new YAML.Document({ x: 3, y: 4 }) expect(String(doc)).toBe('x: 3\ny: 4\n') // @ts-expect-error This should fail. - doc.value.items.push('TEST') - expect(() => String(doc)).toThrow(/^Map items must all be pairs.*TEST/) + expect(() => doc.value.push('TEST')).toThrow(TypeError) }) test('Keep block scalar types for keys', () => { @@ -368,7 +367,7 @@ z: test('Block map, with key.comment', () => { const doc = getDoc() doc.set('a', new Scalar(null)) - doc.value.items[0].key.comment = 'c' + doc.getPair('a')!.key.comment = 'c' expect(doc.toString({ nullStr: '' })).toBe('a: #c\nb:\n') }) @@ -388,7 +387,7 @@ z: test('Flow map, with key.comment', () => { const doc = getDoc() doc.value.flow = true - doc.value.items[0].key.comment = 'c' + doc.getPair('a')!.key.comment = 'c' expect(doc.toString({ nullStr: '' })).toBe('{\n a:, #c\n b:\n}\n') }) @@ -910,14 +909,14 @@ describe('simple keys', () => { test('key with block scalar value', () => { const doc = YAML.parseDocument('foo: bar') - doc.value.items[0].key.type = 'BLOCK_LITERAL' + doc.value.getPair('foo')!.key.type = 'BLOCK_LITERAL' expect(doc.toString()).toBe('? |-\n foo\n: bar\n') expect(doc.toString({ simpleKeys: true })).toBe('"foo": bar\n') }) test('key with comment', () => { const doc = YAML.parseDocument('foo: bar') - doc.value.items[0].key.comment = 'FOO' + doc.value.getPair('foo')!.key.comment = 'FOO' expect(doc.toString()).toBe('foo: #FOO\n bar\n') expect(() => doc.toString({ simpleKeys: true })).toThrow( /With simple keys, key nodes cannot have comments/ @@ -978,13 +977,17 @@ describe('sortMapEntries', () => { a.key < b.key ? 1 : a.key > b.key ? -1 : 0 expect(YAML.stringify(obj, { sortMapEntries })).toBe('c: 3\nb: 2\na: 1\n') }) - test('doc.add', () => { - const doc = new YAML.Document(obj, { sortMapEntries: true }) - doc.add(doc.createPair('bb', 4)) - expect(String(doc)).toBe('a: 1\nb: 2\nbb: 4\nc: 3\n') + test('map.set', () => { + const doc = new YAML.Document(obj) + doc.value.set(doc.createPair('bb', 4)) + expect(doc.toString({ sortMapEntries: true })).toBe( + 'a: 1\nb: 2\nbb: 4\nc: 3\n' + ) }) - test('doc.set', () => { - const doc = new YAML.Document(obj, { sortMapEntries: true }) + test('doc.set with toStringDefaults', () => { + const doc = new YAML.Document(obj, { + toStringDefaults: { sortMapEntries: true } + }) doc.set('bb', 4) expect(String(doc)).toBe('a: 1\nb: 2\nbb: 4\nc: 3\n') }) @@ -1222,7 +1225,7 @@ describe('Scalar options', () => { defaultKeyType: Scalar.QUOTE_SINGLE } as const const doc = new YAML.Document({ foo: null }) - const key = doc.value.items[0].key as Scalar + const key = doc.getPair('foo')!.key as Scalar key.type = Scalar.BLOCK_LITERAL expect(doc.toString(opt)).toBe('? "foo"\n') }) @@ -1608,8 +1611,7 @@ describe('YAML.stringify on ast Document', () => { describe('flow collection padding', () => { const doc = new YAML.Document() - doc.value = new YAML.YAMLSeq() - doc.value.items = [new Scalar(1), new Scalar(2)] + doc.value = new YAML.YAMLSeq(doc.schema, [new Scalar(1), new Scalar(2)]) doc.value.flow = true test('default', () => { diff --git a/tests/doc/types.ts b/tests/doc/types.ts index 31a3cf28..ddda9ff0 100644 --- a/tests/doc/types.ts +++ b/tests/doc/types.ts @@ -147,7 +147,7 @@ describe('number types', () => { intAsBigInt: false, version: '1.1' }) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { value: 10, format: 'BIN' }, { value: 83, format: 'OCT' }, { value: -0, format: 'OCT' }, @@ -159,10 +159,10 @@ describe('number types', () => { { value: 0.42 }, { value: 0.4 } ]) - expect(doc.value.items[3]).not.toHaveProperty('format') - expect(doc.value.items[6]).not.toHaveProperty('format') - expect(doc.value.items[6]).not.toHaveProperty('minFractionDigits') - expect(doc.value.items[7]).not.toHaveProperty('format') + expect(doc.value[3]).not.toHaveProperty('format') + expect(doc.value[6]).not.toHaveProperty('format') + expect(doc.value[6]).not.toHaveProperty('minFractionDigits') + expect(doc.value[7]).not.toHaveProperty('format') }) test('Version 1.2', () => { @@ -180,7 +180,7 @@ describe('number types', () => { intAsBigInt: false, version: '1.2' }) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { value: 83, format: 'OCT' }, { value: 0, format: 'OCT' }, { value: 123456 }, @@ -191,10 +191,10 @@ describe('number types', () => { { value: 0.42 }, { value: 0.4 } ]) - expect(doc.value.items[2]).not.toHaveProperty('format') - expect(doc.value.items[5]).not.toHaveProperty('format') - expect(doc.value.items[5]).not.toHaveProperty('minFractionDigits') - expect(doc.value.items[6]).not.toHaveProperty('format') + expect(doc.value[2]).not.toHaveProperty('format') + expect(doc.value[5]).not.toHaveProperty('format') + expect(doc.value[5]).not.toHaveProperty('minFractionDigits') + expect(doc.value[6]).not.toHaveProperty('format') }) }) @@ -212,7 +212,7 @@ describe('number types', () => { intAsBigInt: true, version: '1.1' }) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { value: 10n, format: 'BIN' }, { value: 83n, format: 'OCT' }, { value: 0n, format: 'OCT' }, @@ -221,9 +221,9 @@ describe('number types', () => { { value: 0.5123, format: 'EXP' }, { value: 4.02 } ]) - expect(doc.value.items[3]).not.toHaveProperty('format') - expect(doc.value.items[6]).not.toHaveProperty('format') - expect(doc.value.items[6]).not.toHaveProperty('minFractionDigits') + expect(doc.value[3]).not.toHaveProperty('format') + expect(doc.value[6]).not.toHaveProperty('format') + expect(doc.value[6]).not.toHaveProperty('minFractionDigits') expect(doc.toJS()).toEqual([10n, 83n, 0n, 123456n, 310, 0.5123, 4.02]) expect(doc.value.toJS(doc)).toEqual([ 10n, @@ -249,7 +249,7 @@ describe('number types', () => { intAsBigInt: true, version: '1.2' }) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { value: 83n, format: 'OCT' }, { value: 0n, format: 'OCT' }, { value: 123456n }, @@ -257,9 +257,9 @@ describe('number types', () => { { value: 0.5123, format: 'EXP' }, { value: 4.02 } ]) - expect(doc.value.items[2]).not.toHaveProperty('format') - expect(doc.value.items[5]).not.toHaveProperty('format') - expect(doc.value.items[5]).not.toHaveProperty('minFractionDigits') + expect(doc.value[2]).not.toHaveProperty('format') + expect(doc.value[5]).not.toHaveProperty('format') + expect(doc.value[5]).not.toHaveProperty('minFractionDigits') }) }) }) @@ -344,7 +344,7 @@ describe('json schema', () => { }) expect(doc.errors).toHaveLength(2) doc.errors = [] - doc.value.items[1].value!.tag = 'tag:yaml.org,2002:float' + doc.get('fixed')!.tag = 'tag:yaml.org,2002:float' expect(String(doc)).toBe( '"canonical": 685230.15\n"fixed": !!float 685230.15\n"negative infinity": "-.inf"\n"not a number": ".NaN"\n' ) @@ -486,7 +486,8 @@ one: 1 '{ 3: 4 }': 'many' }) expect(doc.errors).toHaveLength(0) - doc.value.items[2].key = doc.createNode({ 3: 4 }) + const last = Array.from(doc.value.values.values()).at(-1) + last!.key = doc.createNode({ 3: 4 }) expect(doc.toJS()).toMatchObject({ one: 1, 2: 'two', @@ -508,12 +509,13 @@ one: 1 ]) ) expect(doc.errors).toHaveLength(0) - doc.value.items[2].key = doc.createNode({ 5: 6 }) + const last = Array.from(doc.value.values.values()).at(-1) + last!.key = doc.createNode({ 5: 6 }) expect(doc.toJS({ mapAsMap: true })).toMatchObject( new Map([ ['one', 1], [2, 'two'], - [new Map([[5, 6]]), 'many'] + [new Map([['5', 6]]), 'many'] ]) ) }) @@ -538,8 +540,8 @@ description: const doc = parseDocument>>(src, { schema: 'yaml-1.1' }) - const canonical = doc.value.items[0].value!.value - const generic = doc.value.items[1].value!.value + const canonical = doc.value.get('canonical')!.value + const generic = doc.value.get('generic')!.value expect(canonical).toBeInstanceOf(Uint8Array) expect(generic).toBeInstanceOf(Uint8Array) expect(canonical).toHaveLength(185) @@ -710,9 +712,9 @@ no time zone (Z): 2001-12-15 2:59:43.10 date (00:00:00Z): 2002-12-14` const doc = parseDocument>(src) - doc.value.items.forEach(item => { - expect(item.value!.value).toBeInstanceOf(Date) - }) + for (const pair of doc.value.values.values()) { + expect(pair.value!.value).toBeInstanceOf(Date) + } expect(doc.toJS()).toMatchObject({ canonical: new Date('2001-12-15T02:59:43.100Z'), 'valid iso8601': new Date('2001-12-15T02:59:43.100Z'), @@ -748,7 +750,7 @@ date (00:00:00Z): 2002-12-14\n`) test(name, () => { const doc = parseDocument(src, { version: '1.1' }) expect(doc.value).toBeInstanceOf(YAMLSeq) - expect(doc.value.items).toMatchObject([ + expect(doc.value).toMatchObject([ { key: { value: 'a' }, value: { value: 1 } }, { key: { value: 'b' }, value: { value: 2 } }, { key: { value: 'a' }, value: { value: 3 } } @@ -780,7 +782,7 @@ date (00:00:00Z): 2002-12-14\n`) ]) test(name, () => { const doc = parseDocument(src, { version: '1.1' }) - expect(doc.value.constructor.tag).toBe('tag:yaml.org,2002:omap') + expect(doc.value.constructor.name).toBe('YAMLOMap') expect(doc.toJS()).toBeInstanceOf(Map) expect(doc.toJS()).toMatchObject( new Map([ @@ -828,8 +830,10 @@ date (00:00:00Z): 2002-12-14\n`) ], { tag: '!!omap' } ) - expect(doc.value.constructor.tag).toBe('tag:yaml.org,2002:omap') - expect(String(doc)).toBe(`!!omap\n- a: 1\n- b: 2\n- a: 3\n`) + expect(doc.value.constructor.name).toBe('YAMLOMap') + expect(() => doc.toString()).toThrow() + doc.value[2].key.value = 'c' + expect(String(doc)).toBe(`!!omap\n- a: 1\n- b: 2\n- c: 3\n`) }) }) @@ -840,7 +844,7 @@ date (00:00:00Z): 2002-12-14\n`) ]) test(name, () => { const doc = parseDocument(src, { version: '1.1' }) - expect(doc.value.constructor.tag).toBe('tag:yaml.org,2002:set') + expect(doc.value.constructor.tagName).toBe('tag:yaml.org,2002:set') expect(doc.toJS()).toBeInstanceOf(Set) expect(doc.toJS()).toMatchObject(new Set(['a', 'b', 'c'])) expect(String(doc)).toBe(src) @@ -855,7 +859,7 @@ date (00:00:00Z): 2002-12-14\n`) expect(doc.errors).toMatchObject([ { name: 'YAMLParseError', - message: 'Set items must all have null values' + message: 'Set items must not have non-empty values' } ]) }) @@ -907,7 +911,7 @@ date (00:00:00Z): 2002-12-14\n`) const src = '- { a: A, b: B }\n- { b: X }\n' const doc = parseDocument(src, { version: '1.1' }) const alias = doc.createAlias(doc.get(0), 'a') - doc.addIn([1], doc.createPair('<<', alias)) + doc.get(1).set(doc.createPair('<<', alias)) expect(doc.toString()).toBe('- &a { a: A, b: B }\n- { b: X, <<: *a }\n') expect(doc.toJS()).toMatchObject([ { a: 'A', b: 'B' }, @@ -919,7 +923,7 @@ date (00:00:00Z): 2002-12-14\n`) const src = '- { a: A, b: B }\n- { b: X }\n' const doc = parseDocument(src, { version: '1.1' }) const alias = doc.createAlias(doc.get(0), 'a') - doc.addIn([1], doc.createPair('<<', alias)) + doc.get(1).set(doc.createPair('<<', alias)) expect(doc.toString()).toBe('- &a { a: A, b: B }\n- { b: X, <<: *a }\n') expect(doc.toJS()).toMatchObject([ { a: 'A', b: 'B' }, @@ -945,12 +949,11 @@ describe('custom tags', () => { const doc = parseDocument>(src) expect(doc.value).toBeInstanceOf(YAMLSeq) expect(doc.value.tag).toBe('tag:example.com,2000:test/x') - const { items } = doc.value - expect(items).toHaveLength(4) - items.forEach(item => expect(typeof item.value).toBe('string')) - expect(items[0].tag).toBe('!y') - expect(items[1].tag).toBe('tag:example.com,2000:test/z') - expect(items[2].tag).toBe('tag:example.com,2000:other/w') + expect(doc.value).toHaveLength(4) + doc.value.forEach(item => expect(typeof item.value).toBe('string')) + expect(doc.value[0].tag).toBe('!y') + expect(doc.value[1].tag).toBe('tag:example.com,2000:test/z') + expect(doc.value[2].tag).toBe('tag:example.com,2000:other/w') }) test('stringify', () => { @@ -977,10 +980,10 @@ describe('custom tags', () => { }) doc.value.commentBefore = 'c' - doc.value.items[3].comment = 'cc' + doc.value[3].comment = 'cc' const s = new Scalar(6) s.tag = '!g' - doc.value.items.splice(1, 1, s, new Scalar('7')) + doc.value.splice(1, 1, s, new Scalar('7')) expect(String(doc)).toBe(source` %TAG !e! tag:example.com,2000:test/ %TAG !f! tag:example.com,2000:other/ @@ -1060,6 +1063,9 @@ describe('custom tags', () => { const nullObject: CollectionTag = { tag: '!nullobject', collection: 'map', + createNode(nc, value) { + return YAMLNullObject.create(nc, value) + }, identify: (value: any) => !!value && typeof value === 'object' && !Object.getPrototypeOf(value), nodeClass: YAMLNullObject diff --git a/tests/visit.ts b/tests/visit.ts index 1344cd4e..495f58c7 100644 --- a/tests/visit.ts +++ b/tests/visit.ts @@ -144,7 +144,7 @@ for (const [visit_, title] of [ const Scalar = vi.fn() await visit_(doc, { Seq(_, seq) { - seq.items.push(doc.createNode('three')) + seq.push(doc.createNode('three')) }, Scalar }) diff --git a/vitest.config.js b/vitest.config.js index bc445dc8..9f98d266 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -2,40 +2,24 @@ import { resolve } from 'node:path' import { env } from 'node:process' import { defineConfig } from 'vitest/config' -let alias -if (env.TEST_DIST || env.npm_lifecycle_event === 'test:dist') { +let getPath +if (!!env.TEST_DIST || env.npm_lifecycle_event === 'test:dist') { console.log('Testing build output from dist/') - alias = [ - { - find: /^yaml/, - /** @param {string} path */ - customResolver(path) { - const name = path.split('/')[1] ?? 'index' - return resolve(import.meta.dirname, 'dist', `${name}.js`) - } - }, - { - find: '../src/test-events.ts', - replacement: resolve(import.meta.dirname, 'dist', 'test-events.js') - } - ] + getPath = name => resolve(import.meta.dirname, 'dist', `${name}.js`) } else { - alias = [ - { - find: /^yaml/, - /** @param {string} path */ - customResolver(path) { - const name = path.split('/')[1] ?? 'index' - return resolve(import.meta.dirname, 'src', `${name}.ts`) - } - } - ] + getPath = name => resolve(import.meta.dirname, 'src', `${name}.ts`) } export default defineConfig({ test: { - alias, + alias: { + '../src/test-events.ts': getPath('test-events'), + 'yaml/cli': getPath('cli'), + 'yaml/util': getPath('util'), + yaml: getPath('index') + }, globals: true, + setupFiles: ['tests/_setup.ts'], include: ['tests/**/*.{js,ts}'], exclude: ['tests/_*', 'tests/artifacts/', 'tests/json-test-suite/'] }