feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

21
smart-app-city/frontend/node_modules/js-yaml/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
(The MIT License)
Copyright (C) 2011-2015 by Vitaly Puzrin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

231
smart-app-city/frontend/node_modules/js-yaml/README.md generated vendored Normal file
View File

@@ -0,0 +1,231 @@
JS-YAML - YAML 1.2 parser / writer for JavaScript
=================================================
[![CI](https://github.com/nodeca/js-yaml/actions/workflows/ci.yml/badge.svg)](https://github.com/nodeca/js-yaml/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/js-yaml.svg)](https://www.npmjs.org/package/js-yaml)
__[Online Demo](https://nodeca.github.io/js-yaml/)__
This is an implementation of [YAML](https://yaml.org/), a human-friendly data
serialization language. Started as [PyYAML](https://pyyaml.org/) port, it was
completely rewritten from scratch. Now it's very fast, and supports 1.2 spec.
Installation
------------
### YAML module for node.js
```
npm install js-yaml
```
### CLI executable
If you want to inspect your YAML files from CLI, install js-yaml globally:
```
npm install -g js-yaml
```
#### Usage
```
usage: js-yaml [-h] [-v] [-c] [-t] file
Positional arguments:
file File with YAML document(s)
Optional arguments:
-h, --help Show this help message and exit.
-v, --version Show program's version number and exit.
-c, --compact Display errors in compact mode
-t, --trace Show stack trace on error
```
API
---
Here we cover the most 'useful' methods. If you need advanced details (creating
your own tags), see [examples](https://github.com/nodeca/js-yaml/tree/master/examples)
for more info.
``` javascript
const yaml = require('js-yaml');
const fs = require('fs');
// Get document, or throw exception on error
try {
const doc = yaml.load(fs.readFileSync('/home/ixti/example.yml', 'utf8'));
console.log(doc);
} catch (e) {
console.log(e);
}
```
### load (string [ , options ])
Parses `string` as single YAML document. Returns either a
plain object, a string, a number, `null` or `undefined`, or throws `YAMLException` on error. By default, does
not support regexps, functions and undefined.
options:
- `filename` _(default: null)_ - string to be used as a file path in
error/warning messages.
- `onWarning` _(default: null)_ - function to call on warning messages.
Loader will call this function with an instance of `YAMLException` for each warning.
- `schema` _(default: `DEFAULT_SCHEMA`)_ - specifies a schema to use.
- `FAILSAFE_SCHEMA` - only strings, arrays and plain objects:
https://www.yaml.org/spec/1.2/spec.html#id2802346
- `JSON_SCHEMA` - all JSON-supported types:
https://www.yaml.org/spec/1.2/spec.html#id2803231
- `CORE_SCHEMA` - same as `JSON_SCHEMA`:
https://www.yaml.org/spec/1.2/spec.html#id2804923
- `DEFAULT_SCHEMA` - all supported YAML types.
- `json` _(default: false)_ - compatibility with JSON.parse behaviour. If true, then duplicate keys in a mapping will override values rather than throwing an error.
- `maxDepth` _(default: 100)_ - limits nesting depth for collections.
- `maxMergeSeqLength` _(default: 20)_ - limits the number of elements in merge
(`<<`) sequences.
NOTE: This function **does not** understand multi-document sources, it throws
exception on those.
NOTE: JS-YAML **does not** support schema-specific tag resolution restrictions.
So, the JSON schema is not as strictly defined in the YAML specification.
It allows numbers in any notation, use `Null` and `NULL` as `null`, etc.
The core schema also has no such restrictions. It allows binary notation for integers.
### loadAll (string [, iterator] [, options ])
Same as `load()`, but understands multi-document sources. Applies
`iterator` to each document if specified, or returns array of documents.
``` javascript
const yaml = require('js-yaml');
yaml.loadAll(data, function (doc) {
console.log(doc);
});
```
### dump (object [ , options ])
Serializes `object` as a YAML document. Uses `DEFAULT_SCHEMA`, so it will
throw an exception if you try to dump regexps or functions. However, you can
disable exceptions by setting the `skipInvalid` option to `true`.
options:
- `indent` _(default: 2)_ - indentation width to use (in spaces).
- `noArrayIndent` _(default: false)_ - when true, will not add an indentation level to array elements
- `skipInvalid` _(default: false)_ - do not throw on invalid types (like function
in the safe schema) and skip pairs and single values with such types.
- `flowLevel` _(default: -1)_ - specifies level of nesting, when to switch from
block to flow style for collections. -1 means block style everwhere
- `styles` - "tag" => "style" map. Each tag may have own set of styles.
- `schema` _(default: `DEFAULT_SCHEMA`)_ specifies a schema to use.
- `sortKeys` _(default: `false`)_ - if `true`, sort keys when dumping YAML. If a
function, use the function to sort the keys.
- `lineWidth` _(default: `80`)_ - set max line width. Set `-1` for unlimited width.
- `noRefs` _(default: `false`)_ - if `true`, don't convert duplicate objects into references
- `noCompatMode` _(default: `false`)_ - if `true` don't try to be compatible with older
yaml versions. Currently: don't quote "yes", "no" and so on, as required for YAML 1.1
- `condenseFlow` _(default: `false`)_ - if `true` flow sequences will be condensed, omitting the space between `a, b`. Eg. `'[a,b]'`, and omitting the space between `key: value` and quoting the key. Eg. `'{"a":b}'` Can be useful when using yaml for pretty URL query params as spaces are %-encoded.
- `quotingType` _(`'` or `"`, default: `'`)_ - strings will be quoted using this quoting style. If you specify single quotes, double quotes will still be used for non-printable characters.
- `forceQuotes` _(default: `false`)_ - if `true`, all non-key strings will be quoted even if they normally don't need to.
- `replacer` - callback `function (key, value)` called recursively on each key/value in source object (see `replacer` docs for `JSON.stringify`).
The following table show availlable styles (e.g. "canonical",
"binary"...) available for each tag (.e.g. !!null, !!int ...). Yaml
output is shown on the right side after `=>` (default setting) or `->`:
``` none
!!null
"canonical" -> "~"
"lowercase" => "null"
"uppercase" -> "NULL"
"camelcase" -> "Null"
"empty" -> ""
!!int
"binary" -> "0b1", "0b101010", "0b1110001111010"
"octal" -> "0o1", "0o52", "0o16172"
"decimal" => "1", "42", "7290"
"hexadecimal" -> "0x1", "0x2A", "0x1C7A"
!!bool
"lowercase" => "true", "false"
"uppercase" -> "TRUE", "FALSE"
"camelcase" -> "True", "False"
!!float
"lowercase" => ".nan", '.inf'
"uppercase" -> ".NAN", '.INF'
"camelcase" -> ".NaN", '.Inf'
```
Example:
``` javascript
dump(object, {
'styles': {
'!!null': 'canonical' // dump null as ~
},
'sortKeys': true // sort object keys
});
```
Supported YAML types
--------------------
The list of standard YAML tags and corresponding JavaScript types. See also
[YAML tag discussion](https://pyyaml.org/wiki/YAMLTagDiscussion) and
[YAML types repository](https://yaml.org/type/).
```
!!null '' # null
!!bool 'yes' # bool
!!int '3...' # number
!!float '3.14...' # number
!!binary '...base64...' # buffer
!!timestamp 'YYYY-...' # date
!!omap [ ... ] # array of key-value pairs
!!pairs [ ... ] # array or array pairs
!!set { ... } # array of objects with given keys and null values
!!str '...' # string
!!seq [ ... ] # array
!!map { ... } # object
```
**JavaScript-specific tags**
See [js-yaml-js-types](https://github.com/nodeca/js-yaml-js-types) for
extra types.
Caveats
-------
Note, that you use arrays or objects as key in JS-YAML. JS does not allow objects
or arrays as keys, and stringifies (by calling `toString()` method) them at the
moment of adding them.
``` yaml
---
? [ foo, bar ]
: - baz
? { foo: bar }
: - baz
- baz
```
``` javascript
{ "foo,bar": ["baz"], "[object Object]": ["baz", "baz"] }
```

117
smart-app-city/frontend/node_modules/js-yaml/bin/js-yaml.js generated vendored Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env node
'use strict'
const fs = require('fs')
const argparse = require('argparse')
const yaml = require('..')
/// /////////////////////////////////////////////////////////////////////////////
const cli = new argparse.ArgumentParser({
prog: 'js-yaml',
add_help: true
})
cli.add_argument('-v', '--version', {
action: 'version',
version: require('../package.json').version
})
cli.add_argument('-c', '--compact', {
help: 'Display errors in compact mode',
action: 'store_true'
})
// deprecated (not needed after we removed output colors)
// option suppressed, but not completely removed for compatibility
cli.add_argument('-j', '--to-json', {
help: argparse.SUPPRESS,
dest: 'json',
action: 'store_true'
})
cli.add_argument('-t', '--trace', {
help: 'Show stack trace on error',
action: 'store_true'
})
cli.add_argument('file', {
help: 'File to read, utf-8 encoded without BOM',
nargs: '?',
default: '-'
})
/// /////////////////////////////////////////////////////////////////////////////
const options = cli.parse_args()
/// /////////////////////////////////////////////////////////////////////////////
function readFile (filename, encoding, callback) {
if (options.file === '-') {
// read from stdin
const chunks = []
process.stdin.on('data', function (chunk) {
chunks.push(chunk)
})
process.stdin.on('end', function () {
return callback(null, Buffer.concat(chunks).toString(encoding))
})
} else {
fs.readFile(filename, encoding, callback)
}
}
readFile(options.file, 'utf8', function (error, input) {
let output
let isYaml
if (error) {
if (error.code === 'ENOENT') {
console.error('File not found: ' + options.file)
process.exit(2)
}
console.error(
(options.trace && error.stack) ||
error.message ||
String(error))
process.exit(1)
}
try {
output = JSON.parse(input)
isYaml = false
} catch (err) {
if (err instanceof SyntaxError) {
try {
output = []
yaml.loadAll(input, function (doc) { output.push(doc) }, {})
isYaml = true
if (output.length === 0) output = null
else if (output.length === 1) output = output[0]
} catch (e) {
if (options.trace && err.stack) console.error(e.stack)
else console.error(e.toString(options.compact))
process.exit(1)
}
} else {
console.error(
(options.trace && err.stack) ||
err.message ||
String(err))
process.exit(1)
}
}
if (isYaml) console.log(JSON.stringify(output, null, ' '))
else console.log(yaml.dump(output))
})

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

44
smart-app-city/frontend/node_modules/js-yaml/index.js generated vendored Normal file
View File

@@ -0,0 +1,44 @@
'use strict'
const loader = require('./lib/loader')
const dumper = require('./lib/dumper')
function renamed (from, to) {
return function () {
throw new Error('Function yaml.' + from + ' is removed in js-yaml 4. ' +
'Use yaml.' + to + ' instead, which is now safe by default.')
}
}
module.exports.Type = require('./lib/type')
module.exports.Schema = require('./lib/schema')
module.exports.FAILSAFE_SCHEMA = require('./lib/schema/failsafe')
module.exports.JSON_SCHEMA = require('./lib/schema/json')
module.exports.CORE_SCHEMA = require('./lib/schema/core')
module.exports.DEFAULT_SCHEMA = require('./lib/schema/default')
module.exports.load = loader.load
module.exports.loadAll = loader.loadAll
module.exports.dump = dumper.dump
module.exports.YAMLException = require('./lib/exception')
// Re-export all types in case user wants to create custom schema
module.exports.types = {
binary: require('./lib/type/binary'),
float: require('./lib/type/float'),
map: require('./lib/type/map'),
null: require('./lib/type/null'),
pairs: require('./lib/type/pairs'),
set: require('./lib/type/set'),
timestamp: require('./lib/type/timestamp'),
bool: require('./lib/type/bool'),
int: require('./lib/type/int'),
merge: require('./lib/type/merge'),
omap: require('./lib/type/omap'),
seq: require('./lib/type/seq'),
str: require('./lib/type/str')
}
// Removed functions from JS-YAML 3.0.x
module.exports.safeLoad = renamed('safeLoad', 'load')
module.exports.safeLoadAll = renamed('safeLoadAll', 'loadAll')
module.exports.safeDump = renamed('safeDump', 'dump')

View File

@@ -0,0 +1,50 @@
'use strict'
function isNothing (subject) {
return (typeof subject === 'undefined') || (subject === null)
}
function isObject (subject) {
return (typeof subject === 'object') && (subject !== null)
}
function toArray (sequence) {
if (Array.isArray(sequence)) return sequence
else if (isNothing(sequence)) return []
return [sequence]
}
function extend (target, source) {
if (source) {
const sourceKeys = Object.keys(source)
for (let index = 0, length = sourceKeys.length; index < length; index += 1) {
const key = sourceKeys[index]
target[key] = source[key]
}
}
return target
}
function repeat (string, count) {
let result = ''
for (let cycle = 0; cycle < count; cycle += 1) {
result += string
}
return result
}
function isNegativeZero (number) {
return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number)
}
module.exports.isNothing = isNothing
module.exports.isObject = isObject
module.exports.toArray = toArray
module.exports.repeat = repeat
module.exports.isNegativeZero = isNegativeZero
module.exports.extend = extend

View File

@@ -0,0 +1,937 @@
'use strict'
const common = require('./common')
const YAMLException = require('./exception')
const DEFAULT_SCHEMA = require('./schema/default')
const _toString = Object.prototype.toString
const _hasOwnProperty = Object.prototype.hasOwnProperty
const CHAR_BOM = 0xFEFF
const CHAR_TAB = 0x09 /* Tab */
const CHAR_LINE_FEED = 0x0A /* LF */
const CHAR_CARRIAGE_RETURN = 0x0D /* CR */
const CHAR_SPACE = 0x20 /* Space */
const CHAR_EXCLAMATION = 0x21 /* ! */
const CHAR_DOUBLE_QUOTE = 0x22 /* " */
const CHAR_SHARP = 0x23 /* # */
const CHAR_PERCENT = 0x25 /* % */
const CHAR_AMPERSAND = 0x26 /* & */
const CHAR_SINGLE_QUOTE = 0x27 /* ' */
const CHAR_ASTERISK = 0x2A /* * */
const CHAR_COMMA = 0x2C /* , */
const CHAR_MINUS = 0x2D /* - */
const CHAR_COLON = 0x3A /* : */
const CHAR_EQUALS = 0x3D /* = */
const CHAR_GREATER_THAN = 0x3E /* > */
const CHAR_QUESTION = 0x3F /* ? */
const CHAR_COMMERCIAL_AT = 0x40 /* @ */
const CHAR_LEFT_SQUARE_BRACKET = 0x5B /* [ */
const CHAR_RIGHT_SQUARE_BRACKET = 0x5D /* ] */
const CHAR_GRAVE_ACCENT = 0x60 /* ` */
const CHAR_LEFT_CURLY_BRACKET = 0x7B /* { */
const CHAR_VERTICAL_LINE = 0x7C /* | */
const CHAR_RIGHT_CURLY_BRACKET = 0x7D /* } */
const ESCAPE_SEQUENCES = {}
ESCAPE_SEQUENCES[0x00] = '\\0'
ESCAPE_SEQUENCES[0x07] = '\\a'
ESCAPE_SEQUENCES[0x08] = '\\b'
ESCAPE_SEQUENCES[0x09] = '\\t'
ESCAPE_SEQUENCES[0x0A] = '\\n'
ESCAPE_SEQUENCES[0x0B] = '\\v'
ESCAPE_SEQUENCES[0x0C] = '\\f'
ESCAPE_SEQUENCES[0x0D] = '\\r'
ESCAPE_SEQUENCES[0x1B] = '\\e'
ESCAPE_SEQUENCES[0x22] = '\\"'
ESCAPE_SEQUENCES[0x5C] = '\\\\'
ESCAPE_SEQUENCES[0x85] = '\\N'
ESCAPE_SEQUENCES[0xA0] = '\\_'
ESCAPE_SEQUENCES[0x2028] = '\\L'
ESCAPE_SEQUENCES[0x2029] = '\\P'
const DEPRECATED_BOOLEANS_SYNTAX = [
'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',
'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'
]
const DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/
function compileStyleMap (schema, map) {
if (map === null) return {}
const result = {}
const keys = Object.keys(map)
for (let index = 0, length = keys.length; index < length; index += 1) {
let tag = keys[index]
let style = String(map[tag])
if (tag.slice(0, 2) === '!!') {
tag = 'tag:yaml.org,2002:' + tag.slice(2)
}
const type = schema.compiledTypeMap['fallback'][tag]
if (type && _hasOwnProperty.call(type.styleAliases, style)) {
style = type.styleAliases[style]
}
result[tag] = style
}
return result
}
function encodeHex (character) {
let handle
let length
const string = character.toString(16).toUpperCase()
if (character <= 0xFF) {
handle = 'x'
length = 2
} else if (character <= 0xFFFF) {
handle = 'u'
length = 4
} else if (character <= 0xFFFFFFFF) {
handle = 'U'
length = 8
} else {
throw new YAMLException('code point within a string may not be greater than 0xFFFFFFFF')
}
return '\\' + handle + common.repeat('0', length - string.length) + string
}
const QUOTING_TYPE_SINGLE = 1
const QUOTING_TYPE_DOUBLE = 2
function State (options) {
this.schema = options['schema'] || DEFAULT_SCHEMA
this.indent = Math.max(1, (options['indent'] || 2))
this.noArrayIndent = options['noArrayIndent'] || false
this.skipInvalid = options['skipInvalid'] || false
this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel'])
this.styleMap = compileStyleMap(this.schema, options['styles'] || null)
this.sortKeys = options['sortKeys'] || false
this.lineWidth = options['lineWidth'] || 80
this.noRefs = options['noRefs'] || false
this.noCompatMode = options['noCompatMode'] || false
this.condenseFlow = options['condenseFlow'] || false
this.quotingType = options['quotingType'] === '"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE
this.forceQuotes = options['forceQuotes'] || false
this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null
this.implicitTypes = this.schema.compiledImplicit
this.explicitTypes = this.schema.compiledExplicit
this.tag = null
this.result = ''
this.duplicates = []
this.usedDuplicates = null
}
// Indents every line in a string. Empty lines (\n only) are not indented.
function indentString (string, spaces) {
const ind = common.repeat(' ', spaces)
let position = 0
let result = ''
const length = string.length
while (position < length) {
let line
const next = string.indexOf('\n', position)
if (next === -1) {
line = string.slice(position)
position = length
} else {
line = string.slice(position, next + 1)
position = next + 1
}
if (line.length && line !== '\n') result += ind
result += line
}
return result
}
function generateNextLine (state, level) {
return '\n' + common.repeat(' ', state.indent * level)
}
function testImplicitResolving (state, str) {
for (let index = 0, length = state.implicitTypes.length; index < length; index += 1) {
const type = state.implicitTypes[index]
if (type.resolve(str)) {
return true
}
}
return false
}
// [33] s-white ::= s-space | s-tab
function isWhitespace (c) {
return c === CHAR_SPACE || c === CHAR_TAB
}
// Returns true if the character can be printed without escaping.
// From YAML 1.2: "any allowed characters known to be non-printable
// should also be escaped. [However,] This isnt mandatory"
// Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029.
function isPrintable (c) {
return (c >= 0x00020 && c <= 0x00007E) ||
((c >= 0x000A1 && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029) ||
((c >= 0x0E000 && c <= 0x00FFFD) && c !== CHAR_BOM) ||
(c >= 0x10000 && c <= 0x10FFFF)
}
// [34] ns-char ::= nb-char - s-white
// [27] nb-char ::= c-printable - b-char - c-byte-order-mark
// [26] b-char ::= b-line-feed | b-carriage-return
// Including s-white (for some reason, examples doesn't match specs in this aspect)
// ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark
function isNsCharOrWhitespace (c) {
return isPrintable(c) &&
c !== CHAR_BOM &&
// - b-char
c !== CHAR_CARRIAGE_RETURN &&
c !== CHAR_LINE_FEED
}
// [127] ns-plain-safe(c) ::= c = flow-out ⇒ ns-plain-safe-out
// c = flow-in ⇒ ns-plain-safe-in
// c = block-key ⇒ ns-plain-safe-out
// c = flow-key ⇒ ns-plain-safe-in
// [128] ns-plain-safe-out ::= ns-char
// [129] ns-plain-safe-in ::= ns-char - c-flow-indicator
// [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - “:” - “#” )
// | ( /* An ns-char preceding */ “#” )
// | ( “:” /* Followed by an ns-plain-safe(c) */ )
function isPlainSafe (c, prev, inblock) {
const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c)
const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c)
return (
(
// ns-plain-safe
inblock // c = flow-in
? cIsNsCharOrWhitespace
: cIsNsCharOrWhitespace &&
// - c-flow-indicator
c !== CHAR_COMMA &&
c !== CHAR_LEFT_SQUARE_BRACKET &&
c !== CHAR_RIGHT_SQUARE_BRACKET &&
c !== CHAR_LEFT_CURLY_BRACKET &&
c !== CHAR_RIGHT_CURLY_BRACKET
) &&
// ns-plain-char
c !== CHAR_SHARP && // false on '#'
!(prev === CHAR_COLON && !cIsNsChar)
) || // false on ': '
(isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) || // change to true on '[^ ]#'
(prev === CHAR_COLON && cIsNsChar) // change to true on ':[^ ]'
}
// Simplified test for values allowed as the first character in plain style.
function isPlainSafeFirst (c) {
// Uses a subset of ns-char - c-indicator
// where ns-char = nb-char - s-white.
// No support of ( ( “?” | “:” | “-” ) /* Followed by an ns-plain-safe(c)) */ ) part
return isPrintable(c) &&
c !== CHAR_BOM &&
!isWhitespace(c) && // - s-white
// - (c-indicator ::=
// “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
c !== CHAR_MINUS &&
c !== CHAR_QUESTION &&
c !== CHAR_COLON &&
c !== CHAR_COMMA &&
c !== CHAR_LEFT_SQUARE_BRACKET &&
c !== CHAR_RIGHT_SQUARE_BRACKET &&
c !== CHAR_LEFT_CURLY_BRACKET &&
c !== CHAR_RIGHT_CURLY_BRACKET &&
// | “#” | “&” | “*” | “!” | “|” | “=” | “>” | “'” | “"”
c !== CHAR_SHARP &&
c !== CHAR_AMPERSAND &&
c !== CHAR_ASTERISK &&
c !== CHAR_EXCLAMATION &&
c !== CHAR_VERTICAL_LINE &&
c !== CHAR_EQUALS &&
c !== CHAR_GREATER_THAN &&
c !== CHAR_SINGLE_QUOTE &&
c !== CHAR_DOUBLE_QUOTE &&
// | “%” | “@” | “`”)
c !== CHAR_PERCENT &&
c !== CHAR_COMMERCIAL_AT &&
c !== CHAR_GRAVE_ACCENT
}
// Simplified test for values allowed as the last character in plain style.
function isPlainSafeLast (c) {
// just not whitespace or colon, it will be checked to be plain character later
return !isWhitespace(c) && c !== CHAR_COLON
}
// Same as 'string'.codePointAt(pos), but works in older browsers.
function codePointAt (string, pos) {
const first = string.charCodeAt(pos)
let second
if (first >= 0xD800 && first <= 0xDBFF && pos + 1 < string.length) {
second = string.charCodeAt(pos + 1)
if (second >= 0xDC00 && second <= 0xDFFF) {
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000
}
}
return first
}
// Determines whether block indentation indicator is required.
function needIndentIndicator (string) {
const leadingSpaceRe = /^\n* /
return leadingSpaceRe.test(string)
}
const STYLE_PLAIN = 1
const STYLE_SINGLE = 2
const STYLE_LITERAL = 3
const STYLE_FOLDED = 4
const STYLE_DOUBLE = 5
// Determines which scalar styles are possible and returns the preferred style.
// lineWidth = -1 => no limit.
// Pre-conditions: str.length > 0.
// Post-conditions:
// STYLE_PLAIN or STYLE_SINGLE => no \n are in the string.
// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).
// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).
function chooseScalarStyle (string, singleLineOnly, indentPerLevel, lineWidth,
testAmbiguousType, quotingType, forceQuotes, inblock) {
let i
let char = 0
let prevChar = null
let hasLineBreak = false
let hasFoldableLine = false // only checked if shouldTrackWidth
const shouldTrackWidth = lineWidth !== -1
let previousLineBreak = -1 // count the first line correctly
let plain = isPlainSafeFirst(codePointAt(string, 0)) &&
isPlainSafeLast(codePointAt(string, string.length - 1))
if (singleLineOnly || forceQuotes) {
// Case: no block styles.
// Check for disallowed characters to rule out plain and single.
for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
char = codePointAt(string, i)
if (!isPrintable(char)) {
return STYLE_DOUBLE
}
plain = plain && isPlainSafe(char, prevChar, inblock)
prevChar = char
}
} else {
// Case: block styles permitted.
for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
char = codePointAt(string, i)
if (char === CHAR_LINE_FEED) {
hasLineBreak = true
// Check if any line can be folded.
if (shouldTrackWidth) {
hasFoldableLine = hasFoldableLine ||
// Foldable line = too long, and not more-indented.
(i - previousLineBreak - 1 > lineWidth &&
string[previousLineBreak + 1] !== ' ')
previousLineBreak = i
}
} else if (!isPrintable(char)) {
return STYLE_DOUBLE
}
plain = plain && isPlainSafe(char, prevChar, inblock)
prevChar = char
}
// in case the end is missing a \n
hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&
(i - previousLineBreak - 1 > lineWidth &&
string[previousLineBreak + 1] !== ' '))
}
// Although every style can represent \n without escaping, prefer block styles
// for multiline, since they're more readable and they don't add empty lines.
// Also prefer folding a super-long line.
if (!hasLineBreak && !hasFoldableLine) {
// Strings interpretable as another type have to be quoted;
// e.g. the string 'true' vs. the boolean true.
if (plain && !forceQuotes && !testAmbiguousType(string)) {
return STYLE_PLAIN
}
return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
}
// Edge case: block indentation indicator can only have one digit.
if (indentPerLevel > 9 && needIndentIndicator(string)) {
return STYLE_DOUBLE
}
// At this point we know block styles are valid.
// Prefer literal style unless we want to fold.
if (!forceQuotes) {
return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL
}
return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
}
// Note: line breaking/folding is implemented for only the folded style.
// NB. We drop the last trailing newline (if any) of a returned block scalar
// since the dumper adds its own newline. This always works:
// • No ending newline => unaffected; already using strip "-" chomping.
// • Ending newline => removed then restored.
// Importantly, this keeps the "+" chomp indicator from gaining an extra line.
function writeScalar (state, string, level, iskey, inblock) {
state.dump = (function () {
if (string.length === 0) {
return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''"
}
if (!state.noCompatMode) {
if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) {
return state.quotingType === QUOTING_TYPE_DOUBLE ? ('"' + string + '"') : ("'" + string + "'")
}
}
const indent = state.indent * Math.max(1, level) // no 0-indent scalars
// As indentation gets deeper, let the width decrease monotonically
// to the lower bound min(state.lineWidth, 40).
// Note that this implies
// state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound.
// state.lineWidth > 40 + state.indent: width decreases until the lower bound.
// This behaves better than a constant minimum width which disallows narrower options,
// or an indent threshold which causes the width to suddenly increase.
const lineWidth = (state.lineWidth === -1)
? -1
: Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent)
// Without knowing if keys are implicit/explicit, assume implicit for safety.
const singleLineOnly = iskey ||
// No block styles in flow mode.
(state.flowLevel > -1 && level >= state.flowLevel)
function testAmbiguity (string) {
return testImplicitResolving(state, string)
}
switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth,
testAmbiguity, state.quotingType, state.forceQuotes && !iskey, inblock)) {
case STYLE_PLAIN:
return string
case STYLE_SINGLE:
return "'" + string.replace(/'/g, "''") + "'"
case STYLE_LITERAL:
return '|' + blockHeader(string, state.indent) +
dropEndingNewline(indentString(string, indent))
case STYLE_FOLDED:
return '>' + blockHeader(string, state.indent) +
dropEndingNewline(indentString(foldString(string, lineWidth), indent))
case STYLE_DOUBLE:
return '"' + escapeString(string, lineWidth) + '"'
default:
throw new YAMLException('impossible error: invalid scalar style')
}
}())
}
// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
function blockHeader (string, indentPerLevel) {
const indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''
// note the special case: the string '\n' counts as a "trailing" empty line.
const clip = string[string.length - 1] === '\n'
const keep = clip && (string[string.length - 2] === '\n' || string === '\n')
const chomp = keep ? '+' : (clip ? '' : '-')
return indentIndicator + chomp + '\n'
}
// (See the note for writeScalar.)
function dropEndingNewline (string) {
return string[string.length - 1] === '\n' ? string.slice(0, -1) : string
}
// Note: a long line without a suitable break point will exceed the width limit.
// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.
function foldString (string, width) {
// In folded style, $k$ consecutive newlines output as $k+1$ newlines—
// unless they're before or after a more-indented line, or at the very
// beginning or end, in which case $k$ maps to $k$.
// Therefore, parse each chunk as newline(s) followed by a content line.
const lineRe = /(\n+)([^\n]*)/g
// first line (possibly an empty line)
let result = (function () {
let nextLF = string.indexOf('\n')
nextLF = nextLF !== -1 ? nextLF : string.length
lineRe.lastIndex = nextLF
return foldLine(string.slice(0, nextLF), width)
}())
// If we haven't reached the first content line yet, don't add an extra \n.
let prevMoreIndented = string[0] === '\n' || string[0] === ' '
let moreIndented
// rest of the lines
let match
while ((match = lineRe.exec(string))) {
const prefix = match[1]
const line = match[2]
moreIndented = (line[0] === ' ')
result += prefix +
((!prevMoreIndented && !moreIndented && line !== '') ? '\n' : '') +
foldLine(line, width)
prevMoreIndented = moreIndented
}
return result
}
// Greedy line breaking.
// Picks the longest line under the limit each time,
// otherwise settles for the shortest line over the limit.
// NB. More-indented lines *cannot* be folded, as that would add an extra \n.
function foldLine (line, width) {
if (line === '' || line[0] === ' ') return line
// Since a more-indented line adds a \n, breaks can't be followed by a space.
const breakRe = / [^ ]/g // note: the match index will always be <= length-2.
let match
// start is an inclusive index. end, curr, and next are exclusive.
let start = 0
let end
let curr = 0
let next = 0
let result = ''
// Invariants: 0 <= start <= length-1.
// 0 <= curr <= next <= max(0, length-2). curr - start <= width.
// Inside the loop:
// A match implies length >= 2, so curr and next are <= length-2.
while ((match = breakRe.exec(line))) {
next = match.index
// maintain invariant: curr - start <= width
if (next - start > width) {
end = (curr > start) ? curr : next // derive end <= length-2
result += '\n' + line.slice(start, end)
// skip the space that was output as \n
start = end + 1 // derive start <= length-1
}
curr = next
}
// By the invariants, start <= length-1, so there is something left over.
// It is either the whole string or a part starting from non-whitespace.
result += '\n'
// Insert a break if the remainder is too long and there is a break available.
if (line.length - start > width && curr > start) {
result += line.slice(start, curr) + '\n' + line.slice(curr + 1)
} else {
result += line.slice(start)
}
return result.slice(1) // drop extra \n joiner
}
// Escapes a double-quoted string.
function escapeString (string) {
let result = ''
let char = 0
for (let i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
char = codePointAt(string, i)
const escapeSeq = ESCAPE_SEQUENCES[char]
if (!escapeSeq && isPrintable(char)) {
result += string[i]
if (char >= 0x10000) result += string[i + 1]
} else {
result += escapeSeq || encodeHex(char)
}
}
return result
}
function writeFlowSequence (state, level, object) {
let _result = ''
const _tag = state.tag
for (let index = 0, length = object.length; index < length; index += 1) {
let value = object[index]
if (state.replacer) {
value = state.replacer.call(object, String(index), value)
}
// Write only valid elements, put null instead of invalid elements.
if (writeNode(state, level, value, false, false) ||
(typeof value === 'undefined' &&
writeNode(state, level, null, false, false))) {
if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '')
_result += state.dump
}
}
state.tag = _tag
state.dump = '[' + _result + ']'
}
function writeBlockSequence (state, level, object, compact) {
let _result = ''
const _tag = state.tag
for (let index = 0, length = object.length; index < length; index += 1) {
let value = object[index]
if (state.replacer) {
value = state.replacer.call(object, String(index), value)
}
// Write only valid elements, put null instead of invalid elements.
if (writeNode(state, level + 1, value, true, true, false, true) ||
(typeof value === 'undefined' &&
writeNode(state, level + 1, null, true, true, false, true))) {
if (!compact || _result !== '') {
_result += generateNextLine(state, level)
}
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
_result += '-'
} else {
_result += '- '
}
_result += state.dump
}
}
state.tag = _tag
state.dump = _result || '[]' // Empty sequence if no valid values.
}
function writeFlowMapping (state, level, object) {
let _result = ''
const _tag = state.tag
const objectKeyList = Object.keys(object)
for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
let pairBuffer = ''
if (_result !== '') pairBuffer += ', '
if (state.condenseFlow) pairBuffer += '"'
const objectKey = objectKeyList[index]
let objectValue = object[objectKey]
if (state.replacer) {
objectValue = state.replacer.call(object, objectKey, objectValue)
}
if (!writeNode(state, level, objectKey, false, false)) {
continue // Skip this pair because of invalid key;
}
if (state.dump.length > 1024) pairBuffer += '? '
pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' ')
if (!writeNode(state, level, objectValue, false, false)) {
continue // Skip this pair because of invalid value.
}
pairBuffer += state.dump
// Both key and value are valid.
_result += pairBuffer
}
state.tag = _tag
state.dump = '{' + _result + '}'
}
function writeBlockMapping (state, level, object, compact) {
let _result = ''
const _tag = state.tag
const objectKeyList = Object.keys(object)
// Allow sorting keys so that the output file is deterministic
if (state.sortKeys === true) {
// Default sorting
objectKeyList.sort()
} else if (typeof state.sortKeys === 'function') {
// Custom sort function
objectKeyList.sort(state.sortKeys)
} else if (state.sortKeys) {
// Something is wrong
throw new YAMLException('sortKeys must be a boolean or a function')
}
for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
let pairBuffer = ''
if (!compact || _result !== '') {
pairBuffer += generateNextLine(state, level)
}
const objectKey = objectKeyList[index]
let objectValue = object[objectKey]
if (state.replacer) {
objectValue = state.replacer.call(object, objectKey, objectValue)
}
if (!writeNode(state, level + 1, objectKey, true, true, true)) {
continue // Skip this pair because of invalid key.
}
const explicitPair = (state.tag !== null && state.tag !== '?') ||
(state.dump && state.dump.length > 1024)
if (explicitPair) {
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
pairBuffer += '?'
} else {
pairBuffer += '? '
}
}
pairBuffer += state.dump
if (explicitPair) {
pairBuffer += generateNextLine(state, level)
}
if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
continue // Skip this pair because of invalid value.
}
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
pairBuffer += ':'
} else {
pairBuffer += ': '
}
pairBuffer += state.dump
// Both key and value are valid.
_result += pairBuffer
}
state.tag = _tag
state.dump = _result || '{}' // Empty mapping if no valid pairs.
}
function detectType (state, object, explicit) {
const typeList = explicit ? state.explicitTypes : state.implicitTypes
for (let index = 0, length = typeList.length; index < length; index += 1) {
const type = typeList[index]
if ((type.instanceOf || type.predicate) &&
(!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&
(!type.predicate || type.predicate(object))) {
if (explicit) {
if (type.multi && type.representName) {
state.tag = type.representName(object)
} else {
state.tag = type.tag
}
} else {
state.tag = '?'
}
if (type.represent) {
const style = state.styleMap[type.tag] || type.defaultStyle
let _result
if (_toString.call(type.represent) === '[object Function]') {
_result = type.represent(object, style)
} else if (_hasOwnProperty.call(type.represent, style)) {
_result = type.represent[style](object, style)
} else {
throw new YAMLException('!<' + type.tag + '> tag resolver accepts not "' + style + '" style')
}
state.dump = _result
}
return true
}
}
return false
}
// Serializes `object` and writes it to global `result`.
// Returns true on success, or false on invalid object.
//
function writeNode (state, level, object, block, compact, iskey, isblockseq) {
state.tag = null
state.dump = object
if (!detectType(state, object, false)) {
detectType(state, object, true)
}
const type = _toString.call(state.dump)
const inblock = block
if (block) {
block = (state.flowLevel < 0 || state.flowLevel > level)
}
const objectOrArray = type === '[object Object]' || type === '[object Array]'
let duplicateIndex
let duplicate
if (objectOrArray) {
duplicateIndex = state.duplicates.indexOf(object)
duplicate = duplicateIndex !== -1
}
if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {
compact = false
}
if (duplicate && state.usedDuplicates[duplicateIndex]) {
state.dump = '*ref_' + duplicateIndex
} else {
if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
state.usedDuplicates[duplicateIndex] = true
}
if (type === '[object Object]') {
if (block && (Object.keys(state.dump).length !== 0)) {
writeBlockMapping(state, level, state.dump, compact)
if (duplicate) {
state.dump = '&ref_' + duplicateIndex + state.dump
}
} else {
writeFlowMapping(state, level, state.dump)
if (duplicate) {
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
}
}
} else if (type === '[object Array]') {
if (block && (state.dump.length !== 0)) {
if (state.noArrayIndent && !isblockseq && level > 0) {
writeBlockSequence(state, level - 1, state.dump, compact)
} else {
writeBlockSequence(state, level, state.dump, compact)
}
if (duplicate) {
state.dump = '&ref_' + duplicateIndex + state.dump
}
} else {
writeFlowSequence(state, level, state.dump)
if (duplicate) {
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
}
}
} else if (type === '[object String]') {
if (state.tag !== '?') {
writeScalar(state, state.dump, level, iskey, inblock)
}
} else if (type === '[object Undefined]') {
return false
} else {
if (state.skipInvalid) return false
throw new YAMLException('unacceptable kind of an object to dump ' + type)
}
if (state.tag !== null && state.tag !== '?') {
// Need to encode all characters except those allowed by the spec:
//
// [35] ns-dec-digit ::= [#x30-#x39] /* 0-9 */
// [36] ns-hex-digit ::= ns-dec-digit
// | [#x41-#x46] /* A-F */ | [#x61-#x66] /* a-f */
// [37] ns-ascii-letter ::= [#x41-#x5A] /* A-Z */ | [#x61-#x7A] /* a-z */
// [38] ns-word-char ::= ns-dec-digit | ns-ascii-letter | “-”
// [39] ns-uri-char ::= “%” ns-hex-digit ns-hex-digit | ns-word-char | “#”
// | “;” | “/” | “?” | “:” | “@” | “&” | “=” | “+” | “$” | “,”
// | “_” | “.” | “!” | “~” | “*” | “'” | “(” | “)” | “[” | “]”
//
// Also need to encode '!' because it has special meaning (end of tag prefix).
//
let tagStr = encodeURI(
state.tag[0] === '!' ? state.tag.slice(1) : state.tag
).replace(/!/g, '%21')
if (state.tag[0] === '!') {
tagStr = '!' + tagStr
} else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') {
tagStr = '!!' + tagStr.slice(18)
} else {
tagStr = '!<' + tagStr + '>'
}
state.dump = tagStr + ' ' + state.dump
}
}
return true
}
function getDuplicateReferences (object, state) {
const objects = []
const duplicatesIndexes = []
inspectNode(object, objects, duplicatesIndexes)
const length = duplicatesIndexes.length
for (let index = 0; index < length; index += 1) {
state.duplicates.push(objects[duplicatesIndexes[index]])
}
state.usedDuplicates = new Array(length)
}
function inspectNode (object, objects, duplicatesIndexes) {
if (object !== null && typeof object === 'object') {
const index = objects.indexOf(object)
if (index !== -1) {
if (duplicatesIndexes.indexOf(index) === -1) {
duplicatesIndexes.push(index)
}
} else {
objects.push(object)
if (Array.isArray(object)) {
for (let i = 0, length = object.length; i < length; i += 1) {
inspectNode(object[i], objects, duplicatesIndexes)
}
} else {
const objectKeyList = Object.keys(object)
for (let i = 0, length = objectKeyList.length; i < length; i += 1) {
inspectNode(object[objectKeyList[i]], objects, duplicatesIndexes)
}
}
}
}
}
function dump (input, options) {
options = options || {}
const state = new State(options)
if (!state.noRefs) getDuplicateReferences(input, state)
let value = input
if (state.replacer) {
value = state.replacer.call({ '': value }, '', value)
}
if (writeNode(state, 0, value, true, true)) return state.dump + '\n'
return ''
}
module.exports.dump = dump

View File

@@ -0,0 +1,51 @@
// YAML error class. http://stackoverflow.com/questions/8458984
//
'use strict'
function formatError (exception, compact) {
let where = ''
const message = exception.reason || '(unknown reason)'
if (!exception.mark) return message
if (exception.mark.name) {
where += 'in "' + exception.mark.name + '" '
}
where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')'
if (!compact && exception.mark.snippet) {
where += '\n\n' + exception.mark.snippet
}
return message + ' ' + where
}
function YAMLException (reason, mark) {
// Super constructor
Error.call(this)
this.name = 'YAMLException'
this.reason = reason
this.mark = mark
this.message = formatError(this, false)
// Include stack trace in error object
if (Error.captureStackTrace) {
// Chrome and NodeJS
Error.captureStackTrace(this, this.constructor)
} else {
// FF, IE 10+ and Safari 6+. Fallback for others
this.stack = (new Error()).stack || ''
}
}
// Inherit from Error
YAMLException.prototype = Object.create(Error.prototype)
YAMLException.prototype.constructor = YAMLException
YAMLException.prototype.toString = function toString (compact) {
return this.name + ': ' + formatError(this, compact)
}
module.exports = YAMLException

View File

@@ -0,0 +1,37 @@
import yaml from '../index.js'
const {
Type,
Schema,
FAILSAFE_SCHEMA,
JSON_SCHEMA,
CORE_SCHEMA,
DEFAULT_SCHEMA,
load,
loadAll,
dump,
YAMLException,
types,
safeLoad,
safeLoadAll,
safeDump
} = yaml
export {
Type,
Schema,
FAILSAFE_SCHEMA,
JSON_SCHEMA,
CORE_SCHEMA,
DEFAULT_SCHEMA,
load,
loadAll,
dump,
YAMLException,
types,
safeLoad,
safeLoadAll,
safeDump
}
export default yaml

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
'use strict'
const YAMLException = require('./exception')
const Type = require('./type')
function compileList (schema, name) {
const result = []
schema[name].forEach(function (currentType) {
let newIndex = result.length
result.forEach(function (previousType, previousIndex) {
if (previousType.tag === currentType.tag &&
previousType.kind === currentType.kind &&
previousType.multi === currentType.multi) {
newIndex = previousIndex
}
})
result[newIndex] = currentType
})
return result
}
function compileMap (/* lists... */) {
const result = {
scalar: {},
sequence: {},
mapping: {},
fallback: {},
multi: {
scalar: [],
sequence: [],
mapping: [],
fallback: []
}
}
function collectType (type) {
if (type.multi) {
result.multi[type.kind].push(type)
result.multi['fallback'].push(type)
} else {
result[type.kind][type.tag] = result['fallback'][type.tag] = type
}
}
for (let index = 0, length = arguments.length; index < length; index += 1) {
arguments[index].forEach(collectType)
}
return result
}
function Schema (definition) {
return this.extend(definition)
}
Schema.prototype.extend = function extend (definition) {
let implicit = []
let explicit = []
if (definition instanceof Type) {
// Schema.extend(type)
explicit.push(definition)
} else if (Array.isArray(definition)) {
// Schema.extend([ type1, type2, ... ])
explicit = explicit.concat(definition)
} else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) {
// Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] })
if (definition.implicit) implicit = implicit.concat(definition.implicit)
if (definition.explicit) explicit = explicit.concat(definition.explicit)
} else {
throw new YAMLException('Schema.extend argument should be a Type, [ Type ], ' +
'or a schema definition ({ implicit: [...], explicit: [...] })')
}
implicit.forEach(function (type) {
if (!(type instanceof Type)) {
throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
}
if (type.loadKind && type.loadKind !== 'scalar') {
throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.')
}
if (type.multi) {
throw new YAMLException('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.')
}
})
explicit.forEach(function (type) {
if (!(type instanceof Type)) {
throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
}
})
const result = Object.create(Schema.prototype)
result.implicit = (this.implicit || []).concat(implicit)
result.explicit = (this.explicit || []).concat(explicit)
result.compiledImplicit = compileList(result, 'implicit')
result.compiledExplicit = compileList(result, 'explicit')
result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit)
return result
}
module.exports = Schema

View File

@@ -0,0 +1,9 @@
// Standard YAML's Core schema.
// http://www.yaml.org/spec/1.2/spec.html#id2804923
//
// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
// So, Core schema has no distinctions from JSON schema is JS-YAML.
'use strict'
module.exports = require('./json')

View File

@@ -0,0 +1,20 @@
// JS-YAML's default schema for `safeLoad` function.
// It is not described in the YAML specification.
//
// This schema is based on standard YAML's Core schema and includes most of
// extra types described at YAML tag repository. (http://yaml.org/type/)
'use strict'
module.exports = require('./core').extend({
implicit: [
require('../type/timestamp'),
require('../type/merge')
],
explicit: [
require('../type/binary'),
require('../type/omap'),
require('../type/pairs'),
require('../type/set')
]
})

View File

@@ -0,0 +1,14 @@
// Standard YAML's Failsafe schema.
// http://www.yaml.org/spec/1.2/spec.html#id2802346
'use strict'
const Schema = require('../schema')
module.exports = new Schema({
explicit: [
require('../type/str'),
require('../type/seq'),
require('../type/map')
]
})

View File

@@ -0,0 +1,17 @@
// Standard YAML's JSON schema.
// http://www.yaml.org/spec/1.2/spec.html#id2803231
//
// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
// So, this schema is not such strict as defined in the YAML specification.
// It allows numbers in binary notaion, use `Null` and `NULL` as `null`, etc.
'use strict'
module.exports = require('./failsafe').extend({
implicit: [
require('../type/null'),
require('../type/bool'),
require('../type/int'),
require('../type/float')
]
})

View File

@@ -0,0 +1,96 @@
'use strict'
const common = require('./common')
// get snippet for a single line, respecting maxLength
function getLine (buffer, lineStart, lineEnd, position, maxLineLength) {
let head = ''
let tail = ''
const maxHalfLength = Math.floor(maxLineLength / 2) - 1
if (position - lineStart > maxHalfLength) {
head = ' ... '
lineStart = position - maxHalfLength + head.length
}
if (lineEnd - position > maxHalfLength) {
tail = ' ...'
lineEnd = position + maxHalfLength - tail.length
}
return {
str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, '→') + tail,
pos: position - lineStart + head.length // relative position
}
}
function padStart (string, max) {
return common.repeat(' ', max - string.length) + string
}
function makeSnippet (mark, options) {
options = Object.create(options || null)
if (!mark.buffer) return null
if (!options.maxLength) options.maxLength = 79
if (typeof options.indent !== 'number') options.indent = 1
if (typeof options.linesBefore !== 'number') options.linesBefore = 3
if (typeof options.linesAfter !== 'number') options.linesAfter = 2
const re = /\r?\n|\r|\0/g
const lineStarts = [0]
const lineEnds = []
let match
let foundLineNo = -1
while ((match = re.exec(mark.buffer))) {
lineEnds.push(match.index)
lineStarts.push(match.index + match[0].length)
if (mark.position <= match.index && foundLineNo < 0) {
foundLineNo = lineStarts.length - 2
}
}
if (foundLineNo < 0) foundLineNo = lineStarts.length - 1
let result = ''
const lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length
const maxLineLength = options.maxLength - (options.indent + lineNoLength + 3)
for (let i = 1; i <= options.linesBefore; i++) {
if (foundLineNo - i < 0) break
const line = getLine(
mark.buffer,
lineStarts[foundLineNo - i],
lineEnds[foundLineNo - i],
mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),
maxLineLength
)
result = common.repeat(' ', options.indent) + padStart((mark.line - i + 1).toString(), lineNoLength) +
' | ' + line.str + '\n' + result
}
const line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength)
result += common.repeat(' ', options.indent) + padStart((mark.line + 1).toString(), lineNoLength) +
' | ' + line.str + '\n'
result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\n'
for (let i = 1; i <= options.linesAfter; i++) {
if (foundLineNo + i >= lineEnds.length) break
const line = getLine(
mark.buffer,
lineStarts[foundLineNo + i],
lineEnds[foundLineNo + i],
mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),
maxLineLength
)
result += common.repeat(' ', options.indent) + padStart((mark.line + i + 1).toString(), lineNoLength) +
' | ' + line.str + '\n'
}
return result.replace(/\n$/, '')
}
module.exports = makeSnippet

View File

@@ -0,0 +1,66 @@
'use strict'
const YAMLException = require('./exception')
const TYPE_CONSTRUCTOR_OPTIONS = [
'kind',
'multi',
'resolve',
'construct',
'instanceOf',
'predicate',
'represent',
'representName',
'defaultStyle',
'styleAliases'
]
const YAML_NODE_KINDS = [
'scalar',
'sequence',
'mapping'
]
function compileStyleAliases (map) {
const result = {}
if (map !== null) {
Object.keys(map).forEach(function (style) {
map[style].forEach(function (alias) {
result[String(alias)] = style
})
})
}
return result
}
function Type (tag, options) {
options = options || {}
Object.keys(options).forEach(function (name) {
if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {
throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.')
}
})
// TODO: Add tag format check.
this.options = options // keep original options in case user wants to extend this type later
this.tag = tag
this.kind = options['kind'] || null
this.resolve = options['resolve'] || function () { return true }
this.construct = options['construct'] || function (data) { return data }
this.instanceOf = options['instanceOf'] || null
this.predicate = options['predicate'] || null
this.represent = options['represent'] || null
this.representName = options['representName'] || null
this.defaultStyle = options['defaultStyle'] || null
this.multi = options['multi'] || false
this.styleAliases = compileStyleAliases(options['styleAliases'] || null)
if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {
throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.')
}
}
module.exports = Type

View File

@@ -0,0 +1,122 @@
'use strict'
const Type = require('../type')
// [ 64, 65, 66 ] -> [ padding, CR, LF ]
const BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'
function resolveYamlBinary (data) {
if (data === null) return false
let bitlen = 0
const max = data.length
const map = BASE64_MAP
// Convert one by one.
for (let idx = 0; idx < max; idx++) {
const code = map.indexOf(data.charAt(idx))
// Skip CR/LF
if (code > 64) continue
// Fail on illegal characters
if (code < 0) return false
bitlen += 6
}
// If there are any bits left, source was corrupted
return (bitlen % 8) === 0
}
function constructYamlBinary (data) {
const input = data.replace(/[\r\n=]/g, '') // remove CR/LF & padding to simplify scan
const max = input.length
const map = BASE64_MAP
let bits = 0
const result = []
// Collect by 6*4 bits (3 bytes)
for (let idx = 0; idx < max; idx++) {
if ((idx % 4 === 0) && idx) {
result.push((bits >> 16) & 0xFF)
result.push((bits >> 8) & 0xFF)
result.push(bits & 0xFF)
}
bits = (bits << 6) | map.indexOf(input.charAt(idx))
}
// Dump tail
const tailbits = (max % 4) * 6
if (tailbits === 0) {
result.push((bits >> 16) & 0xFF)
result.push((bits >> 8) & 0xFF)
result.push(bits & 0xFF)
} else if (tailbits === 18) {
result.push((bits >> 10) & 0xFF)
result.push((bits >> 2) & 0xFF)
} else if (tailbits === 12) {
result.push((bits >> 4) & 0xFF)
}
return new Uint8Array(result)
}
function representYamlBinary (object /*, style */) {
let result = ''
let bits = 0
const max = object.length
const map = BASE64_MAP
// Convert every three bytes to 4 ASCII characters.
for (let idx = 0; idx < max; idx++) {
if ((idx % 3 === 0) && idx) {
result += map[(bits >> 18) & 0x3F]
result += map[(bits >> 12) & 0x3F]
result += map[(bits >> 6) & 0x3F]
result += map[bits & 0x3F]
}
bits = (bits << 8) + object[idx]
}
// Dump tail
const tail = max % 3
if (tail === 0) {
result += map[(bits >> 18) & 0x3F]
result += map[(bits >> 12) & 0x3F]
result += map[(bits >> 6) & 0x3F]
result += map[bits & 0x3F]
} else if (tail === 2) {
result += map[(bits >> 10) & 0x3F]
result += map[(bits >> 4) & 0x3F]
result += map[(bits << 2) & 0x3F]
result += map[64]
} else if (tail === 1) {
result += map[(bits >> 2) & 0x3F]
result += map[(bits << 4) & 0x3F]
result += map[64]
result += map[64]
}
return result
}
function isBinary (obj) {
return Object.prototype.toString.call(obj) === '[object Uint8Array]'
}
module.exports = new Type('tag:yaml.org,2002:binary', {
kind: 'scalar',
resolve: resolveYamlBinary,
construct: constructYamlBinary,
predicate: isBinary,
represent: representYamlBinary
})

View File

@@ -0,0 +1,35 @@
'use strict'
const Type = require('../type')
function resolveYamlBoolean (data) {
if (data === null) return false
const max = data.length
return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||
(max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'))
}
function constructYamlBoolean (data) {
return data === 'true' ||
data === 'True' ||
data === 'TRUE'
}
function isBoolean (object) {
return Object.prototype.toString.call(object) === '[object Boolean]'
}
module.exports = new Type('tag:yaml.org,2002:bool', {
kind: 'scalar',
resolve: resolveYamlBoolean,
construct: constructYamlBoolean,
predicate: isBoolean,
represent: {
lowercase: function (object) { return object ? 'true' : 'false' },
uppercase: function (object) { return object ? 'TRUE' : 'FALSE' },
camelcase: function (object) { return object ? 'True' : 'False' }
},
defaultStyle: 'lowercase'
})

View File

@@ -0,0 +1,99 @@
'use strict'
const common = require('../common')
const Type = require('../type')
const YAML_FLOAT_PATTERN = new RegExp(
// 2.5e4, 2.5 and integers
'^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?' +
// .2e4, .2
// special case, seems not from spec
'|\\.[0-9]+(?:[eE][-+]?[0-9]+)?' +
// .inf
'|[-+]?\\.(?:inf|Inf|INF)' +
// .nan
'|\\.(?:nan|NaN|NAN))$')
const YAML_FLOAT_SPECIAL_PATTERN = new RegExp(
'^(?:' +
// .inf
'[-+]?\\.(?:inf|Inf|INF)' +
// .nan
'|\\.(?:nan|NaN|NAN))$')
function resolveYamlFloat (data) {
if (data === null) return false
if (!YAML_FLOAT_PATTERN.test(data)) {
return false
}
if (Number.isFinite(parseFloat(data, 10))) {
return true
}
return YAML_FLOAT_SPECIAL_PATTERN.test(data)
}
function constructYamlFloat (data) {
let value = data.toLowerCase()
const sign = value[0] === '-' ? -1 : 1
if ('+-'.indexOf(value[0]) >= 0) {
value = value.slice(1)
}
if (value === '.inf') {
return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY
} else if (value === '.nan') {
return NaN
}
return sign * parseFloat(value, 10)
}
const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/
function representYamlFloat (object, style) {
if (isNaN(object)) {
switch (style) {
case 'lowercase': return '.nan'
case 'uppercase': return '.NAN'
case 'camelcase': return '.NaN'
}
} else if (Number.POSITIVE_INFINITY === object) {
switch (style) {
case 'lowercase': return '.inf'
case 'uppercase': return '.INF'
case 'camelcase': return '.Inf'
}
} else if (Number.NEGATIVE_INFINITY === object) {
switch (style) {
case 'lowercase': return '-.inf'
case 'uppercase': return '-.INF'
case 'camelcase': return '-.Inf'
}
} else if (common.isNegativeZero(object)) {
return '-0.0'
}
const res = object.toString(10)
// JS stringifier can build scientific format without dots: 5e-100,
// while YAML requres dot: 5.e-100. Fix it with simple hack
return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res
}
function isFloat (object) {
return (Object.prototype.toString.call(object) === '[object Number]') &&
(object % 1 !== 0 || common.isNegativeZero(object))
}
module.exports = new Type('tag:yaml.org,2002:float', {
kind: 'scalar',
resolve: resolveYamlFloat,
construct: constructYamlFloat,
predicate: isFloat,
represent: representYamlFloat,
defaultStyle: 'lowercase'
})

View File

@@ -0,0 +1,142 @@
'use strict'
const common = require('../common')
const Type = require('../type')
function isHexCode (c) {
return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */)) ||
((c >= 0x41/* A */) && (c <= 0x46/* F */)) ||
((c >= 0x61/* a */) && (c <= 0x66/* f */))
}
function isOctCode (c) {
return ((c >= 0x30/* 0 */) && (c <= 0x37/* 7 */))
}
function isDecCode (c) {
return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */))
}
function resolveYamlInteger (data) {
if (data === null) return false
const max = data.length
let index = 0
let hasDigits = false
if (!max) return false
let ch = data[index]
// sign
if (ch === '-' || ch === '+') {
ch = data[++index]
}
if (ch === '0') {
// 0
if (index + 1 === max) return true
ch = data[++index]
// base 2, base 8, base 16
if (ch === 'b') {
// base 2
index++
for (; index < max; index++) {
ch = data[index]
if (ch !== '0' && ch !== '1') return false
hasDigits = true
}
return hasDigits && Number.isFinite(parseYamlInteger(data))
}
if (ch === 'x') {
// base 16
index++
for (; index < max; index++) {
if (!isHexCode(data.charCodeAt(index))) return false
hasDigits = true
}
return hasDigits && Number.isFinite(parseYamlInteger(data))
}
if (ch === 'o') {
// base 8
index++
for (; index < max; index++) {
if (!isOctCode(data.charCodeAt(index))) return false
hasDigits = true
}
return hasDigits && Number.isFinite(parseYamlInteger(data))
}
}
// base 10 (except 0)
for (; index < max; index++) {
if (!isDecCode(data.charCodeAt(index))) {
return false
}
hasDigits = true
}
if (!hasDigits) return false
return Number.isFinite(parseYamlInteger(data))
}
function parseYamlInteger (data) {
let value = data
let sign = 1
let ch = value[0]
if (ch === '-' || ch === '+') {
if (ch === '-') sign = -1
value = value.slice(1)
ch = value[0]
}
if (value === '0') return 0
if (ch === '0') {
if (value[1] === 'b') return sign * parseInt(value.slice(2), 2)
if (value[1] === 'x') return sign * parseInt(value.slice(2), 16)
if (value[1] === 'o') return sign * parseInt(value.slice(2), 8)
}
return sign * parseInt(value, 10)
}
function constructYamlInteger (data) {
return parseYamlInteger(data)
}
function isInteger (object) {
return (Object.prototype.toString.call(object)) === '[object Number]' &&
(object % 1 === 0 && !common.isNegativeZero(object))
}
module.exports = new Type('tag:yaml.org,2002:int', {
kind: 'scalar',
resolve: resolveYamlInteger,
construct: constructYamlInteger,
predicate: isInteger,
represent: {
binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1) },
octal: function (obj) { return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1) },
decimal: function (obj) { return obj.toString(10) },
hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1) }
},
defaultStyle: 'decimal',
styleAliases: {
binary: [2, 'bin'],
octal: [8, 'oct'],
decimal: [10, 'dec'],
hexadecimal: [16, 'hex']
}
})

View File

@@ -0,0 +1,8 @@
'use strict'
const Type = require('../type')
module.exports = new Type('tag:yaml.org,2002:map', {
kind: 'mapping',
construct: function (data) { return data !== null ? data : {} }
})

View File

@@ -0,0 +1,12 @@
'use strict'
const Type = require('../type')
function resolveYamlMerge (data) {
return data === '<<' || data === null
}
module.exports = new Type('tag:yaml.org,2002:merge', {
kind: 'scalar',
resolve: resolveYamlMerge
})

View File

@@ -0,0 +1,35 @@
'use strict'
const Type = require('../type')
function resolveYamlNull (data) {
if (data === null) return true
const max = data.length
return (max === 1 && data === '~') ||
(max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'))
}
function constructYamlNull () {
return null
}
function isNull (object) {
return object === null
}
module.exports = new Type('tag:yaml.org,2002:null', {
kind: 'scalar',
resolve: resolveYamlNull,
construct: constructYamlNull,
predicate: isNull,
represent: {
canonical: function () { return '~' },
lowercase: function () { return 'null' },
uppercase: function () { return 'NULL' },
camelcase: function () { return 'Null' },
empty: function () { return '' }
},
defaultStyle: 'lowercase'
})

View File

@@ -0,0 +1,45 @@
'use strict'
const Type = require('../type')
const _hasOwnProperty = Object.prototype.hasOwnProperty
const _toString = Object.prototype.toString
function resolveYamlOmap (data) {
if (data === null) return true
const objectKeys = []
const object = data
for (let index = 0, length = object.length; index < length; index += 1) {
const pair = object[index]
let pairHasKey = false
if (_toString.call(pair) !== '[object Object]') return false
let pairKey
for (pairKey in pair) {
if (_hasOwnProperty.call(pair, pairKey)) {
if (!pairHasKey) pairHasKey = true
else return false
}
}
if (!pairHasKey) return false
if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey)
else return false
}
return true
}
function constructYamlOmap (data) {
return data !== null ? data : []
}
module.exports = new Type('tag:yaml.org,2002:omap', {
kind: 'sequence',
resolve: resolveYamlOmap,
construct: constructYamlOmap
})

View File

@@ -0,0 +1,50 @@
'use strict'
const Type = require('../type')
const _toString = Object.prototype.toString
function resolveYamlPairs (data) {
if (data === null) return true
const object = data
const result = new Array(object.length)
for (let index = 0, length = object.length; index < length; index += 1) {
const pair = object[index]
if (_toString.call(pair) !== '[object Object]') return false
const keys = Object.keys(pair)
if (keys.length !== 1) return false
result[index] = [keys[0], pair[keys[0]]]
}
return true
}
function constructYamlPairs (data) {
if (data === null) return []
const object = data
const result = new Array(object.length)
for (let index = 0, length = object.length; index < length; index += 1) {
const pair = object[index]
const keys = Object.keys(pair)
result[index] = [keys[0], pair[keys[0]]]
}
return result
}
module.exports = new Type('tag:yaml.org,2002:pairs', {
kind: 'sequence',
resolve: resolveYamlPairs,
construct: constructYamlPairs
})

View File

@@ -0,0 +1,8 @@
'use strict'
const Type = require('../type')
module.exports = new Type('tag:yaml.org,2002:seq', {
kind: 'sequence',
construct: function (data) { return data !== null ? data : [] }
})

View File

@@ -0,0 +1,29 @@
'use strict'
const Type = require('../type')
const _hasOwnProperty = Object.prototype.hasOwnProperty
function resolveYamlSet (data) {
if (data === null) return true
const object = data
for (const key in object) {
if (_hasOwnProperty.call(object, key)) {
if (object[key] !== null) return false
}
}
return true
}
function constructYamlSet (data) {
return data !== null ? data : {}
}
module.exports = new Type('tag:yaml.org,2002:set', {
kind: 'mapping',
resolve: resolveYamlSet,
construct: constructYamlSet
})

View File

@@ -0,0 +1,8 @@
'use strict'
const Type = require('../type')
module.exports = new Type('tag:yaml.org,2002:str', {
kind: 'scalar',
construct: function (data) { return data !== null ? data : '' }
})

View File

@@ -0,0 +1,88 @@
'use strict'
const Type = require('../type')
const YAML_DATE_REGEXP = new RegExp(
'^([0-9][0-9][0-9][0-9])' + // [1] year
'-([0-9][0-9])' + // [2] month
'-([0-9][0-9])$') // [3] day
const YAML_TIMESTAMP_REGEXP = new RegExp(
'^([0-9][0-9][0-9][0-9])' + // [1] year
'-([0-9][0-9]?)' + // [2] month
'-([0-9][0-9]?)' + // [3] day
'(?:[Tt]|[ \\t]+)' + // ...
'([0-9][0-9]?)' + // [4] hour
':([0-9][0-9])' + // [5] minute
':([0-9][0-9])' + // [6] second
'(?:\\.([0-9]*))?' + // [7] fraction
'(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tzHour
'(?::([0-9][0-9]))?))?$') // [11] tzMinute
function resolveYamlTimestamp (data) {
if (data === null) return false
if (YAML_DATE_REGEXP.exec(data) !== null) return true
if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true
return false
}
function constructYamlTimestamp (data) {
let fraction = 0
let delta = null
let match = YAML_DATE_REGEXP.exec(data)
if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data)
if (match === null) throw new Error('Date resolve error')
// match: [1] year [2] month [3] day
const year = +(match[1])
const month = +(match[2]) - 1 // JS month starts with 0
const day = +(match[3])
if (!match[4]) { // no hour
return new Date(Date.UTC(year, month, day))
}
// match: [4] hour [5] minute [6] second [7] fraction
const hour = +(match[4])
const minute = +(match[5])
const second = +(match[6])
if (match[7]) {
fraction = match[7].slice(0, 3)
while (fraction.length < 3) { // milli-seconds
fraction += '0'
}
fraction = +fraction
}
// match: [8] tz [9] tz_sign [10] tzHour [11] tzMinute
if (match[9]) {
const tzHour = +(match[10])
const tzMinute = +(match[11] || 0)
delta = (tzHour * 60 + tzMinute) * 60000 // delta in mili-seconds
if (match[9] === '-') delta = -delta
}
const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction))
if (delta) date.setTime(date.getTime() - delta)
return date
}
function representYamlTimestamp (object /*, style */) {
return object.toISOString()
}
module.exports = new Type('tag:yaml.org,2002:timestamp', {
kind: 'scalar',
resolve: resolveYamlTimestamp,
construct: constructYamlTimestamp,
instanceOf: Date,
represent: representYamlTimestamp
})

View File

@@ -0,0 +1,77 @@
{
"name": "js-yaml",
"version": "4.2.0",
"description": "YAML 1.2 parser and serializer",
"keywords": [
"yaml",
"parser",
"serializer",
"pyyaml"
],
"author": "Vladimir Zapparov <dervus.grim@gmail.com>",
"contributors": [
"Aleksey V Zapparov <ixti@member.fsf.org> (http://www.ixti.net/)",
"Vitaly Puzrin <vitaly@rcdesign.ru> (https://github.com/puzrin)",
"Martin Grenfell <martin.grenfell@gmail.com> (http://got-ravings.blogspot.com)"
],
"license": "MIT",
"repository": "nodeca/js-yaml",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"files": [
"index.js",
"lib/",
"bin/",
"dist/"
],
"bin": {
"js-yaml": "bin/js-yaml.js"
},
"module": "./dist/js-yaml.mjs",
"exports": {
".": {
"import": "./dist/js-yaml.mjs",
"require": "./index.js"
},
"./package.json": "./package.json"
},
"scripts": {
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"test": "npm run lint && npm run test:core && npm run test:build",
"test:core": "node --test test/core/*.test.*",
"test:build": "npm run build && node --test test/build/*.test.*",
"coverage": "npm run build && c8 --include 'lib/**' -r text -r html -r lcov node --test test/core/*.test.*",
"build": "node support/build-dist.mjs",
"build:demo": "npm run lint && node support/build_demo.mjs",
"gh-demo": "npm run build:demo && gh-pages -d demo -f",
"prepack": "npm test && npm run build && npm run build:demo",
"postpublish": "npm run gh-demo"
},
"unpkg": "dist/js-yaml.min.js",
"jsdelivr": "dist/js-yaml.min.js",
"dependencies": {
"argparse": "^2.0.1"
},
"devDependencies": {
"c8": "^11.0.0",
"codemirror": "^5.65.21",
"eslint": "^9.39.4",
"fast-check": "^4.8.0",
"gh-pages": "^6.3.0",
"neostandard": "^0.13.0",
"tinybench": "^6.0.2",
"vite": "^8.0.14",
"vite-plugin-node-polyfills": "^0.28.0",
"vite-plugin-singlefile": "^2.3.3",
"workerpool": "^10.0.2"
}
}