@node-core/utils 7.4.0 → 7.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/lib/update-v8/deps.js +4 -1
  2. package/node_modules/branch-diff/.github/dependabot.yml +14 -2
  3. package/node_modules/branch-diff/.github/workflows/test-and-release.yml +4 -15
  4. package/node_modules/branch-diff/CHANGELOG.md +24 -0
  5. package/node_modules/branch-diff/package.json +35 -3
  6. package/node_modules/js-yaml/dist/browser/js-yaml.esm.min.mjs +2 -2
  7. package/node_modules/js-yaml/dist/browser/js-yaml.esm.min.mjs.map +1 -1
  8. package/node_modules/js-yaml/dist/browser/js-yaml.umd.min.js +1 -1
  9. package/node_modules/js-yaml/dist/browser/js-yaml.umd.min.js.map +1 -1
  10. package/node_modules/js-yaml/dist/js-yaml.cjs.js +2 -1
  11. package/node_modules/js-yaml/dist/js-yaml.cjs.js.map +1 -1
  12. package/node_modules/js-yaml/dist/js-yaml.mjs +2 -1
  13. package/node_modules/js-yaml/dist/js-yaml.mjs.map +1 -1
  14. package/node_modules/js-yaml/package.json +1 -1
  15. package/node_modules/smol-toml/README.md +460 -273
  16. package/node_modules/smol-toml/dist/date.js +50 -21
  17. package/node_modules/smol-toml/dist/error.d.ts +1 -2
  18. package/node_modules/smol-toml/dist/error.js +6 -2
  19. package/node_modules/smol-toml/dist/extract.js +240 -20
  20. package/node_modules/smol-toml/dist/index.cjs +409 -373
  21. package/node_modules/smol-toml/dist/index.d.ts +5 -4
  22. package/node_modules/smol-toml/dist/index.js +2 -1
  23. package/node_modules/smol-toml/dist/parse.d.ts +38 -1
  24. package/node_modules/smol-toml/dist/parse.js +52 -37
  25. package/node_modules/smol-toml/dist/primitive.js +23 -110
  26. package/node_modules/smol-toml/dist/stringify.d.ts +2 -1
  27. package/node_modules/smol-toml/dist/stringify.js +74 -36
  28. package/node_modules/smol-toml/dist/struct.js +76 -99
  29. package/node_modules/smol-toml/dist/util.d.ts +6 -1
  30. package/node_modules/smol-toml/dist/util.js +4 -36
  31. package/node_modules/smol-toml/package.json +8 -8
  32. package/package.json +1 -1
@@ -30,6 +30,11 @@ import { stringify } from './stringify.js';
30
30
  import { TomlDate } from './date.js';
31
31
  import { TomlError } from './error.js';
32
32
  export type { TomlValue, TomlTable, TomlValueWithoutBigInt, TomlTableWithoutBigInt } from './util.js';
33
+ export { parse, stringify, TomlDate, TomlError };
34
+ export type {
35
+ /** @deprecated use {@link TomlValue} instead */
36
+ TomlValue as TomlPrimitive, } from './util.js';
37
+ /** @deprecated use `import * as ... from 'smol-toml'` instead */
33
38
  declare const _default: {
34
39
  parse: typeof parse;
35
40
  stringify: typeof stringify;
@@ -37,7 +42,3 @@ declare const _default: {
37
42
  TomlError: typeof TomlError;
38
43
  };
39
44
  export default _default;
40
- export { parse, stringify, TomlDate, TomlError };
41
- export type {
42
- /** @deprecated use TomlValue instead */
43
- TomlValue as TomlPrimitive, } from './util.js';
@@ -29,5 +29,6 @@ import { parse } from './parse.js';
29
29
  import { stringify } from './stringify.js';
30
30
  import { TomlDate } from './date.js';
31
31
  import { TomlError } from './error.js';
32
- export default { parse, stringify, TomlDate, TomlError };
33
32
  export { parse, stringify, TomlDate, TomlError };
33
+ /** @deprecated use `import * as ... from 'smol-toml'` instead */
34
+ export default { parse, stringify, TomlDate, TomlError };
@@ -26,9 +26,46 @@
26
26
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
  import { type IntegersAsBigInt, type TomlTable, type TomlTableWithoutBigInt } from './util.js';
29
+ export type UnsafeKeyBehaviour =
30
+ /** Preserve the unsafe key in the final object. */
31
+ 'keep'
32
+ /** Silently drop the unsafe key from the final object. */
33
+ | 'drop'
34
+ /** Reject documents with unsafe keys. */
35
+ | 'throw';
29
36
  export interface ParseOptions {
30
- maxDepth?: number;
37
+ /**
38
+ * Whether to parse integers as {@link BigInt} or not.
39
+ *
40
+ * Use the special value `"asNeeded"` to only use {@link BigInt} for
41
+ * integers that cannot be safely represented as JavaScript numbers.
42
+ *
43
+ * @defaultValue `false`
44
+ * @since 1.4.0
45
+ */
31
46
  integersAsBigInt?: IntegersAsBigInt;
47
+ /**
48
+ * Whether to use the legacy {@link TomlDate}, instead of the new
49
+ * {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Temporal | Temporal API}.
50
+ *
51
+ * @defaultValue `true`
52
+ * @since 1.9.0
53
+ */
54
+ useLegacyDate?: boolean;
55
+ /**
56
+ * Behaviour of the library when encountering a potentially unsafe property (`__proto__`, `constructor`).
57
+ *
58
+ * @defaultValue `'keep'`
59
+ * @since 1.9.0
60
+ */
61
+ unsafeKeyBehaviour?: UnsafeKeyBehaviour;
62
+ /**
63
+ * Maximum permitted inline object/array depth.
64
+ *
65
+ * @defaultValue `1000`
66
+ * @since 1.9.0
67
+ */
68
+ maxDepth?: number;
32
69
  }
33
70
  export declare function parse(toml: string, options?: ParseOptions & {
34
71
  integersAsBigInt: Exclude<IntegersAsBigInt, undefined | false>;
@@ -29,7 +29,7 @@ import { parseKey } from './struct.js';
29
29
  import { extractValue } from './extract.js';
30
30
  import { skipVoid } from './util.js';
31
31
  import { TomlError } from './error.js';
32
- function peekTable(key, table, meta, type) {
32
+ function peekTable(ctx, key, table, meta, type) {
33
33
  let t = table;
34
34
  let m = meta;
35
35
  let k;
@@ -37,7 +37,7 @@ function peekTable(key, table, meta, type) {
37
37
  let state;
38
38
  for (let i = 0; i < key.length; i++) {
39
39
  if (i) {
40
- t = hasOwn ? t[k] : (t[k] = {});
40
+ t = hasOwn ? t[k] : (t[k] = Object.create(null));
41
41
  m = (state = m[k]).c;
42
42
  if (type === 0 /* Type.DOTTED */ && (state.t === 1 /* Type.EXPLICIT */ || state.t === 2 /* Type.ARRAY */)) {
43
43
  return null;
@@ -53,7 +53,10 @@ function peekTable(key, table, meta, type) {
53
53
  return null;
54
54
  }
55
55
  if (!hasOwn) {
56
- if (k === '__proto__') {
56
+ let unsafe = k === '__proto__';
57
+ if (ctx.uk && (unsafe || k === 'constructor'))
58
+ return false;
59
+ if (unsafe) {
57
60
  Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
58
61
  Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
59
62
  }
@@ -62,7 +65,7 @@ function peekTable(key, table, meta, type) {
62
65
  ? 3 /* Type.ARRAY_DOTTED */ : type,
63
66
  d: false,
64
67
  i: 0,
65
- c: {},
68
+ c: Object.create(null),
66
69
  };
67
70
  }
68
71
  }
@@ -76,8 +79,8 @@ function peekTable(key, table, meta, type) {
76
79
  state.d = true;
77
80
  t[k] = [];
78
81
  }
79
- t[k].push(t = {});
80
- state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: {} });
82
+ t[k].push(t = Object.create(null));
83
+ state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: Object.create(null) });
81
84
  }
82
85
  if (state.d) {
83
86
  // Redefining a table!
@@ -85,63 +88,75 @@ function peekTable(key, table, meta, type) {
85
88
  }
86
89
  state.d = true;
87
90
  if (type === 1 /* Type.EXPLICIT */) {
88
- t = hasOwn ? t[k] : (t[k] = {});
91
+ t = hasOwn ? t[k] : (t[k] = Object.create(null));
89
92
  }
90
93
  else if (type === 0 /* Type.DOTTED */ && hasOwn) {
91
94
  return null;
92
95
  }
93
96
  return [k, t, state.c];
94
97
  }
95
- export function parse(toml, { maxDepth = 1000, integersAsBigInt } = {}) {
96
- let ctx = { s: toml, p: 0, d: maxDepth };
97
- let res = {};
98
- let meta = {};
98
+ function validateTablePeek(ctx, peek, ptr) {
99
+ if (peek === null || ctx.uk === 2)
100
+ TomlError.x(peek === null
101
+ ? 'trying to redefine an already defined table or value'
102
+ : 'document contains an unsafe property', ctx, ptr);
103
+ }
104
+ export function parse(toml, options = {}) {
105
+ let ctx = {
106
+ s: toml,
107
+ p: 0,
108
+ d: options.maxDepth ?? 1000,
109
+ bi: options.integersAsBigInt ?? false,
110
+ ld: options.useLegacyDate ?? true,
111
+ uk: options.unsafeKeyBehaviour === 'throw' ? 2 : options.unsafeKeyBehaviour === 'drop' ? 1 : 0,
112
+ };
113
+ let res = Object.create(null);
114
+ let meta = Object.create(null);
99
115
  let tmp;
116
+ let skipping = false;
100
117
  let tbl = res;
101
118
  let m = meta;
119
+ // BOM is allowed, skip.
120
+ // JS is UTF-16, so we have to check for the UTF-16 BOM instead of the UTF-8 BOM sequence!
121
+ if (toml.charCodeAt(0) === 0xfeff)
122
+ ctx.p++;
102
123
  skipVoid(ctx);
103
124
  while (ctx.p < toml.length) {
104
125
  if (toml.charCodeAt(ctx.p) === 0x5b /* [ */) {
105
126
  let isTableArray = toml.charCodeAt(++ctx.p) === 0x5b; /* [ */
106
127
  tmp = ctx.p += +isTableArray;
107
- let k = parseKey(ctx, ']');
128
+ skipping = false;
129
+ let k = parseKey(ctx, 0x5d /* ] */);
108
130
  if (isTableArray) {
109
- if (toml.charCodeAt(ctx.p - 1) !== 0x5d /* ] */) {
110
- throw new TomlError('expected end of table declaration', {
111
- toml: toml,
112
- ptr: ctx.p - 1,
113
- });
131
+ if (toml.charCodeAt(ctx.p) !== 0x5d /* ] */) {
132
+ TomlError.x('expected end of table array declaration', ctx);
114
133
  }
115
134
  ctx.p++;
116
135
  }
117
- let p = peekTable(k, res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
136
+ let p = peekTable(ctx, k, res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
118
137
  if (!p) {
119
- throw new TomlError('trying to redefine an already defined table or value', {
120
- toml: toml,
121
- ptr: tmp,
122
- });
138
+ validateTablePeek(ctx, p, tmp);
139
+ skipping = true;
140
+ }
141
+ else {
142
+ m = p[2];
143
+ tbl = p[1];
123
144
  }
124
- m = p[2];
125
- tbl = p[1];
126
145
  }
127
146
  else {
128
147
  tmp = ctx.p;
129
148
  let k = parseKey(ctx);
130
- let p = peekTable(k, tbl, m, 0 /* Type.DOTTED */);
131
- if (!p) {
132
- throw new TomlError('trying to redefine an already defined table or value', {
133
- toml: toml,
134
- ptr: tmp,
135
- });
136
- }
137
- p[1][p[0]] = extractValue(ctx, void 0, integersAsBigInt);
149
+ let p = peekTable(ctx, k, tbl, m, 0 /* Type.DOTTED */);
150
+ if (!p && !skipping)
151
+ validateTablePeek(ctx, p, tmp);
152
+ skipVoid(ctx, true, true);
153
+ let v = extractValue(ctx, void 0);
154
+ if (p && !skipping)
155
+ p[1][p[0]] = v;
138
156
  }
139
157
  skipVoid(ctx, true);
140
- if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 0xa /* \n */ && tmp !== 0xd /* \r */) {
141
- throw new TomlError('each key-value declaration must be followed by an end-of-line', {
142
- toml: toml,
143
- ptr: ctx.p,
144
- });
158
+ if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 0xa /* \n */ && (tmp !== 0xd /* \r */ || toml.charCodeAt(ctx.p + 1) !== 0xa /* \n */)) {
159
+ TomlError.x('each key-value declaration must be followed by an end-of-line', ctx);
145
160
  }
146
161
  skipVoid(ctx);
147
162
  }
@@ -25,16 +25,10 @@
25
25
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
26
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
- import { TomlDate } from './date.js';
29
28
  import { TomlError } from './error.js';
30
- import { skipComment, skipUntil } from './util.js';
31
- // let CTRL_REGEX = /[\x00-\x08\x0f-\x1f\x7f]/
32
- let INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
33
- let FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
34
- let LEADING_ZERO = /^[+-]?0[0-9_]/;
35
29
  /** @internal */
36
30
  export function parseString(ctx) {
37
- let start = ctx.p;
31
+ let startPtr = ctx.p;
38
32
  let c = ctx.s.charCodeAt(ctx.p++);
39
33
  let first = c;
40
34
  let isLiteral = c === 0x27; /* ' */
@@ -46,37 +40,8 @@ export function parseString(ctx) {
46
40
  else if (c === 0xd /* \r */ && ctx.s.charCodeAt(ctx.p + 1) === 0xa /* \n */)
47
41
  ctx.p += 2;
48
42
  }
49
- /*
50
- The fast path does not seem to bring significant performance gains, so it's commented out.
51
- Kept for reference and/or future fafoing.
52
-
53
- Without: spec 5.08 µs/iter 3.88 ipc (99.44% cache) 23.90 branch misses 28.61k cycles 111.01k instructions
54
- 5MB 115.73 ms/iter 2.51 ipc (98.36% cache) 3.12M branch misses 619.30M cycles 1.56G instructions
55
-
56
- With: spec 5.09 µs/iter 3.90 ipc (99.46% cache) 24.42 branch misses 28.57k cycles 111.49k instructions
57
- 5MB 113.89 ms/iter 2.47 ipc (98.38% cache) 3.12M branch misses 611.94M cycles 1.51G instructions
58
-
59
- if (c === "'") {
60
- // Literal strings fast path - no transform needs to occur; just grab the str and that's it
61
- let endPtr = str.indexOf(isMultiline ? "'''" : "'", ptr)
62
- if (endPtr < 0) {
63
- throw new TomlError("unfinished string literal", { toml: str, ptr })
64
- }
65
-
66
- if (isMultiline) {
67
- // If the string ends with 4-5 quotes, then the first 1-2 are part of the string
68
- if (str[endPtr + 3] === "'") endPtr++
69
- if (str[endPtr + 3] === "'") endPtr++
70
- }
71
-
72
- let string = str.slice(ptr, endPtr)
73
- if (CTRL_REGEX.test(string)) {
74
- let match = string.match(CTRL_REGEX)!
75
- throw new TomlError('control characters are not allowed in strings', { toml: str, ptr: ptr + (match.index ?? 0) })
76
- }
77
- return [string, endPtr + (isMultiline ? 3 : 1)]
78
- }
79
- */
43
+ // For the record: it is not worth it to use a fast-path for literal strings using `indexOf`.
44
+ // Doing a char-by-char iteration ends up dispatching less instructions, for the same wall-clock runtime.
80
45
  let parsed = '';
81
46
  let sliceStart = ctx.p;
82
47
  // states:
@@ -93,10 +58,7 @@ export function parseString(ctx) {
93
58
  }
94
59
  // Control characters are banned in TOML, so we throw an error if we encounter them
95
60
  else if ((c < 0x20 && c !== 0x9 /* \t */) || c === 0x7f) {
96
- throw new TomlError('control characters are not allowed in strings', {
97
- toml: ctx.s,
98
- ptr: ctx.p,
99
- });
61
+ TomlError.x('control characters are not allowed in strings', ctx);
100
62
  }
101
63
  // The string might terminate while we're parsing through a newline escape.
102
64
  // It must have encountered a newline; otherwise, it'll simply fail in another branch.
@@ -109,11 +71,15 @@ export function parseString(ctx) {
109
71
  ctx.p++;
110
72
  }
111
73
  // If we're in a newline escape still, then there's nothing to add.
112
- if (!state)
113
- parsed += ctx.s.slice(sliceStart, ctx.p);
74
+ if (!state) {
75
+ // Avoid a useless concat operation if the string can be used as-is.
76
+ let s = ctx.s.slice(sliceStart, ctx.p);
77
+ parsed = parsed ? parsed + s : s;
78
+ }
114
79
  ctx.p += isMultiline ? 3 : 1;
115
80
  return parsed;
116
81
  }
82
+ // Baseline state; keep moving forward unless an escape sequence starts
117
83
  else if (!state) {
118
84
  if (!isLiteral && c === 0x5c /* \ */) {
119
85
  parsed += ctx.s.slice(sliceStart, (sliceStart = ctx.p));
@@ -122,29 +88,32 @@ export function parseString(ctx) {
122
88
  }
123
89
  else if (state === 1) {
124
90
  if (c === 0x78 /* x */ || c === 0x75 /* u */ || c === 0x55 /* U */) { // Unicode escape
91
+ let errPtr = ctx.p++ - 1;
125
92
  let value = 0;
126
93
  let len = c === 0x78 /* x */ ? 2 : c === 0x75 /* u */ ? 4 : 8;
127
94
  for (let j = 0; j < len; j++, ctx.p++) {
128
- let hex = ctx.s.charCodeAt(ctx.p + 1);
95
+ let hex = ctx.s.charCodeAt(ctx.p);
129
96
  let digit =
130
97
  /* 0-9 */ hex >= 0x30 && hex <= 0x39 ? hex - 0x30 :
131
98
  /* A-F */ hex >= 0x41 && hex <= 0x46 ? hex - 0x41 + 10 :
132
99
  /* a-f */ hex >= 0x61 && hex <= 0x66 ? hex - 0x61 + 10 : -1;
133
100
  if (digit < 0)
134
- throw new TomlError('invalid non-hex character in unicode escape', { toml: ctx.s, ptr: ctx.p + 1 });
101
+ TomlError.x('invalid non-hex character in unicode escape', ctx);
135
102
  value = (value << 4) | digit;
136
103
  }
137
104
  // Because JS does bitwise on signed 32bit integers, all 0xfzzzzzzz values are actually seen as negative
138
105
  if (value < 0 || value > 0x10ffff || (value >= 0xd800 && value <= 0xdfff)) {
139
- throw new TomlError('invalid unicode escape', { toml: ctx.s, ptr: ctx.p });
106
+ TomlError.x('invalid unicode escape', ctx, errPtr);
140
107
  }
141
108
  parsed += String.fromCodePoint(value);
142
- sliceStart = ctx.p + 1;
109
+ sliceStart = ctx.p--;
143
110
  state = 0;
144
111
  }
145
- else if (c === 0x20 || c === 0x9 /* \t */) { // If it was a newline, it'd have been handled earlier
112
+ // Newline escape sequence. We only need to care about spaces and tabs; newlines are dealt with earlier
113
+ else if (isMultiline && (c === 0x20 || c === 0x9 /* \t */)) {
146
114
  state = 2;
147
115
  }
116
+ // Basic escape sequence
148
117
  else {
149
118
  if (c === 0x62 /* b */)
150
119
  parsed += '\b';
@@ -163,76 +132,20 @@ export function parseString(ctx) {
163
132
  else if (c === 0x5c /* \ */)
164
133
  parsed += '\\';
165
134
  else
166
- throw new TomlError('unrecognized escape sequence', { toml: ctx.s, ptr: ctx.p });
135
+ TomlError.x('unrecognised escape sequence', ctx);
167
136
  sliceStart = ctx.p + 1;
168
137
  state = 0;
169
138
  }
170
139
  }
140
+ // Newline escape continuation: keep moving forward until the first non-whitespace char
171
141
  else if (c !== 0x20 && c !== 0x9 /* \t */) {
172
- if (state === 2) {
173
- throw new TomlError('invalid escape: only line-ending whitespace may be escaped', {
174
- toml: ctx.s,
175
- ptr: sliceStart,
176
- });
177
- }
142
+ if (state === 2)
143
+ TomlError.x('invalid escape: only line-ending whitespace may be escaped', ctx, sliceStart);
178
144
  // State cannot be zero, or we'd have branched earlier already.
179
145
  // If it's a backslash, immediately transition to the escape state so it can be processed.
180
146
  state = !isLiteral && c === 0x5c /* \ */ ? 1 : 0;
181
147
  sliceStart = ctx.p;
182
148
  }
183
149
  }
184
- throw new TomlError('unfinished string', { toml: ctx.s, ptr: start });
185
- }
186
- function sliceAndTrimEndOf(ctx, start, end) {
187
- let value = ctx.s.slice(start, end);
188
- let commentIdx = value.indexOf('#');
189
- if (commentIdx > 0) {
190
- // The call to skipComment allows to "validate" the comment
191
- // (absence of control characters)
192
- skipComment({ s: value, p: commentIdx, d: 0 });
193
- value = value.slice(0, commentIdx);
194
- }
195
- return value.trimEnd();
196
- }
197
- /** @internal */
198
- export function parseValue(ctx, integersAsBigInt, end) {
199
- let ptr = ctx.p;
200
- let err = { toml: ctx.s, ptr };
201
- skipUntil(ctx, 0x2c /* , */, end);
202
- let value = sliceAndTrimEndOf(ctx, ptr, ctx.p);
203
- if (!value)
204
- throw new TomlError('incomplete declaration: value expected', err);
205
- if (value === '-inf')
206
- return -Infinity;
207
- if (value === 'inf' || value === '+inf')
208
- return Infinity;
209
- if (value === 'nan' || value === '+nan' || value === '-nan')
210
- return NaN;
211
- // Avoid FP representation of -0
212
- if (value === '-0')
213
- return integersAsBigInt ? 0n : 0;
214
- // Numbers
215
- let isInt = INT_REGEX.test(value);
216
- if (isInt || FLOAT_REGEX.test(value)) {
217
- if (LEADING_ZERO.test(value)) {
218
- throw new TomlError('leading zeroes are not allowed', err);
219
- }
220
- value = value.replace(/_/g, '');
221
- let numeric = +value;
222
- if (isNaN(numeric)) {
223
- throw new TomlError('invalid number', err);
224
- }
225
- if (isInt) {
226
- if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
227
- throw new TomlError('integer value cannot be represented losslessly', err);
228
- }
229
- if (isInt || integersAsBigInt === true)
230
- numeric = BigInt(value);
231
- }
232
- return numeric;
233
- }
234
- const date = new TomlDate(value);
235
- if (!date.isValid())
236
- throw new TomlError('invalid value', err);
237
- return date;
150
+ TomlError.x('unfinished string', ctx, startPtr);
238
151
  }
@@ -25,7 +25,8 @@
25
25
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
26
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
- export declare function stringify(obj: any, { maxDepth, numbersAsFloat }?: {
28
+ export declare function stringify(obj: any, { maxDepth, numbersAsFloat, strictTemporal }?: {
29
29
  maxDepth?: number;
30
30
  numbersAsFloat?: boolean;
31
+ strictTemporal?: boolean;
31
32
  }): string;
@@ -26,22 +26,29 @@
26
26
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
27
  */
28
28
  let BARE_KEY = /^[a-z0-9-_]+$/i;
29
+ let HAS_WELLFORMED = !!(''.isWellFormed);
29
30
  function extendedTypeOf(obj) {
30
31
  let type = typeof obj;
31
32
  if (type === 'object') {
32
33
  if (Array.isArray(obj))
33
34
  return 'array';
34
- if (typeof obj?.getUTCDate === 'function' && obj instanceof Date)
35
+ if (typeof obj.getUTCDate === 'function' && obj instanceof Date)
35
36
  return 'date';
36
- if (globalThis.Temporal &&
37
- // check for the 'since' property as an early bailout that avoids running all 5 instanceof checks
38
- typeof obj?.since === 'function' &&
39
- (obj instanceof Temporal.Instant ||
40
- obj instanceof Temporal.PlainDate ||
41
- obj instanceof Temporal.PlainDateTime ||
42
- obj instanceof Temporal.PlainTime ||
43
- obj instanceof Temporal.ZonedDateTime)) {
44
- return 'temporal';
37
+ if (globalThis.Temporal) {
38
+ if (obj.until) {
39
+ if (obj instanceof Temporal.ZonedDateTime)
40
+ return 'temporal/tz+uc';
41
+ if (obj instanceof Temporal.PlainDateTime || obj instanceof Temporal.PlainDate)
42
+ return 'temporal/uc';
43
+ if (obj instanceof Temporal.PlainTime || obj instanceof Temporal.Instant)
44
+ return 'temporal';
45
+ if (obj instanceof Temporal.PlainYearMonth)
46
+ return 'temporal/x';
47
+ }
48
+ else if ((obj.toPlainDate && obj instanceof Temporal.PlainMonthDay) ||
49
+ (obj.negated && obj instanceof Temporal.Duration)) {
50
+ return 'temporal/x';
51
+ }
45
52
  }
46
53
  }
47
54
  return type;
@@ -53,16 +60,20 @@ function isArrayOfTables(obj) {
53
60
  }
54
61
  return obj.length != 0;
55
62
  }
63
+ function formatWellFormedStringUnchecked(s) {
64
+ return JSON.stringify(s).replaceAll('\x7f', '\\u007f');
65
+ }
56
66
  function formatString(s) {
57
- return JSON.stringify(s).replace(/\x7f/g, '\\u007f');
67
+ return formatWellFormedStringUnchecked(HAS_WELLFORMED ? s.toWellFormed() : s);
58
68
  }
59
- function stringifyTemporal(temporal) {
60
- return temporal.toString({
61
- calendarName: 'never',
62
- timeZoneName: 'never',
63
- });
69
+ function formatKey(s) {
70
+ if (BARE_KEY.test(s))
71
+ return s;
72
+ if (HAS_WELLFORMED && !s.isWellFormed())
73
+ throw new RangeError('key contains illegal lone surrogates');
74
+ return formatWellFormedStringUnchecked(s);
64
75
  }
65
- function stringifyValue(val, type, depth, numberAsFloat) {
76
+ function stringifyValue(val, type, depth, numberAsFloat, strictTemporal) {
66
77
  if (depth === 0) {
67
78
  throw new Error('Could not stringify the object: maximum object depth exceeded');
68
79
  }
@@ -79,6 +90,7 @@ function stringifyValue(val, type, depth, numberAsFloat) {
79
90
  return val.toFixed(1);
80
91
  case 'bigint':
81
92
  case 'boolean':
93
+ case 'temporal':
82
94
  return val.toString();
83
95
  case 'string':
84
96
  return formatString(val);
@@ -87,14 +99,42 @@ function stringifyValue(val, type, depth, numberAsFloat) {
87
99
  throw new TypeError('cannot serialize invalid date');
88
100
  return val.toISOString();
89
101
  case 'object':
90
- return stringifyInlineTable(val, depth, numberAsFloat);
102
+ return stringifyInlineTable(val, depth, numberAsFloat, strictTemporal);
91
103
  case 'array':
92
- return stringifyArray(val, depth, numberAsFloat);
93
- case 'temporal':
94
- return stringifyTemporal(val);
104
+ return stringifyArray(val, depth, numberAsFloat, strictTemporal);
105
+ // @ts-expect-error -- intentional fallthrough case
106
+ case 'temporal/tz+uc':
107
+ if (strictTemporal) {
108
+ let tz = val.timeZoneId;
109
+ let tzc = tz.charCodeAt(0);
110
+ if (
111
+ // Classic offset
112
+ tzc !== 0x2b /* + */ && tzc !== 0x2d /* - */ &&
113
+ (
114
+ // Fast pre-check pass; see below for the actually accepted values
115
+ (tzc !== 0x55 /* U */ && tzc !== 0x47 /* G */ && tzc !== 0x5a /* Z */ && tzc !== 0x45 /* E */) ||
116
+ (
117
+ // UTC and its aliases; Temporal implementations don't all canonicalise unfortunately
118
+ tz !== 'UTC' && tz !== 'UCT' && tz !== 'Universal' && tz !== 'Zulu' &&
119
+ // GMT is a TZ but it's for all intents and purposes equivalent to UTC. Safe to downgrade.
120
+ !tz.startsWith('GMT') && tz !== 'Greenwich' &&
121
+ // Etc/* are all safe to downgrade to offset (either UTC, GMT, or offset)
122
+ !tz.startsWith('Etc/')))) {
123
+ throw new TypeError('Temporal objects with an IANA timezone are not allowed in Temporal strict mode');
124
+ }
125
+ }
126
+ case 'temporal/uc':
127
+ if (strictTemporal && val.calendarId !== 'iso8601')
128
+ throw new TypeError('Temporal objects with a non-default calendar are not allowed in Temporal strict mode');
129
+ return val.toString({
130
+ calendarName: 'never',
131
+ timeZoneName: 'never',
132
+ });
133
+ case 'temporal/x':
134
+ throw new TypeError('Unsupported ' + val[Symbol.toStringTag]);
95
135
  }
96
136
  }
97
- function stringifyInlineTable(obj, depth, numberAsFloat) {
137
+ function stringifyInlineTable(obj, depth, numberAsFloat, strictTemporal) {
98
138
  let keys = Object.keys(obj);
99
139
  if (keys.length === 0)
100
140
  return '{}';
@@ -103,13 +143,11 @@ function stringifyInlineTable(obj, depth, numberAsFloat) {
103
143
  let k = keys[i];
104
144
  if (i)
105
145
  res += ', ';
106
- res += BARE_KEY.test(k) ? k : formatString(k);
107
- res += ' = ';
108
- res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
146
+ res += formatKey(k) + ' = ' + stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat, strictTemporal);
109
147
  }
110
148
  return res + ' }';
111
149
  }
112
- function stringifyArray(array, depth, numberAsFloat) {
150
+ function stringifyArray(array, depth, numberAsFloat, strictTemporal) {
113
151
  if (array.length === 0)
114
152
  return '[]';
115
153
  let res = '[ ';
@@ -119,22 +157,22 @@ function stringifyArray(array, depth, numberAsFloat) {
119
157
  if (array[i] === null || array[i] === void 0) {
120
158
  throw new TypeError('arrays cannot contain null or undefined values');
121
159
  }
122
- res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat);
160
+ res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat, strictTemporal);
123
161
  }
124
162
  return res + ' ]';
125
163
  }
126
- function stringifyArrayTable(array, key, depth, numberAsFloat) {
164
+ function stringifyArrayTable(array, key, depth, numberAsFloat, strictTemporal) {
127
165
  if (depth === 0) {
128
166
  throw new Error('Could not stringify the object: maximum object depth exceeded');
129
167
  }
130
168
  let res = '';
131
169
  for (let i = 0; i < array.length; i++) {
132
170
  res += `${res && '\n'}[[${key}]]\n`;
133
- res += stringifyTable(0, array[i], key, depth, numberAsFloat);
171
+ res += stringifyTable(0, array[i], key, depth, numberAsFloat, strictTemporal);
134
172
  }
135
173
  return res;
136
174
  }
137
- function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
175
+ function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat, strictTemporal) {
138
176
  if (depth === 0) {
139
177
  throw new Error('Could not stringify the object: maximum object depth exceeded');
140
178
  }
@@ -148,18 +186,18 @@ function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
148
186
  if (type === 'symbol' || type === 'function') {
149
187
  throw new TypeError(`cannot serialize values of type '${type}'`);
150
188
  }
151
- let key = BARE_KEY.test(k) ? k : formatString(k);
189
+ let key = formatKey(k);
152
190
  if (type === 'array' && isArrayOfTables(obj[k])) {
153
- tables += (tables && '\n') + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
191
+ tables += (tables && '\n') + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat, strictTemporal);
154
192
  }
155
193
  else if (type === 'object') {
156
194
  let tblKey = prefix ? `${prefix}.${key}` : key;
157
- tables += (tables && '\n') + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
195
+ tables += (tables && '\n') + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat, strictTemporal);
158
196
  }
159
197
  else {
160
198
  preamble += key;
161
199
  preamble += ' = ';
162
- preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
200
+ preamble += stringifyValue(obj[k], type, depth, numberAsFloat, strictTemporal);
163
201
  preamble += '\n';
164
202
  }
165
203
  }
@@ -170,11 +208,11 @@ function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
170
208
  ? `${preamble}\n${tables}`
171
209
  : preamble || tables;
172
210
  }
173
- export function stringify(obj, { maxDepth = 1000, numbersAsFloat = false } = {}) {
211
+ export function stringify(obj, { maxDepth = 1000, numbersAsFloat = false, strictTemporal = false } = {}) {
174
212
  if (extendedTypeOf(obj) !== 'object') {
175
213
  throw new TypeError('stringify can only be called with an object');
176
214
  }
177
- let str = stringifyTable(0, obj, '', maxDepth, numbersAsFloat);
215
+ let str = stringifyTable(0, obj, '', maxDepth, numbersAsFloat, strictTemporal);
178
216
  if (str[str.length - 1] !== '\n')
179
217
  return str + '\n';
180
218
  return str;