@zappinginc/zm2 6.0.14

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 (133) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/.gitattributes +4 -0
  3. package/.mocharc.js +14 -0
  4. package/CHANGELOG.md +2416 -0
  5. package/CLAUDE.md +84 -0
  6. package/CONTRIBUTING.md +124 -0
  7. package/GNU-AGPL-3.0.txt +665 -0
  8. package/LICENSE +1 -0
  9. package/README.md +248 -0
  10. package/bin/zm2 +3 -0
  11. package/bin/zm2-dev +3 -0
  12. package/bin/zm2-docker +3 -0
  13. package/bin/zm2-runtime +3 -0
  14. package/bin/zm2-windows +3 -0
  15. package/bin/zm2.ps1 +3 -0
  16. package/bun.lock +421 -0
  17. package/constants.js +114 -0
  18. package/index.js +13 -0
  19. package/lib/API/Configuration.js +212 -0
  20. package/lib/API/Containerizer.js +335 -0
  21. package/lib/API/Dashboard.js +459 -0
  22. package/lib/API/Deploy.js +117 -0
  23. package/lib/API/Extra.js +775 -0
  24. package/lib/API/ExtraMgmt/Docker.js +30 -0
  25. package/lib/API/Log.js +315 -0
  26. package/lib/API/LogManagement.js +371 -0
  27. package/lib/API/Modules/LOCAL.js +122 -0
  28. package/lib/API/Modules/Modularizer.js +148 -0
  29. package/lib/API/Modules/NPM.js +445 -0
  30. package/lib/API/Modules/TAR.js +362 -0
  31. package/lib/API/Modules/flagExt.js +46 -0
  32. package/lib/API/Modules/index.js +120 -0
  33. package/lib/API/Monit.js +247 -0
  34. package/lib/API/Serve.js +343 -0
  35. package/lib/API/Startup.js +629 -0
  36. package/lib/API/UX/helpers.js +213 -0
  37. package/lib/API/UX/index.js +9 -0
  38. package/lib/API/UX/pm2-describe.js +193 -0
  39. package/lib/API/UX/pm2-ls-minimal.js +31 -0
  40. package/lib/API/UX/pm2-ls.js +483 -0
  41. package/lib/API/Version.js +382 -0
  42. package/lib/API/interpreter.json +12 -0
  43. package/lib/API/pm2-plus/PM2IO.js +372 -0
  44. package/lib/API/pm2-plus/auth-strategies/CliAuth.js +288 -0
  45. package/lib/API/pm2-plus/auth-strategies/WebAuth.js +187 -0
  46. package/lib/API/pm2-plus/helpers.js +97 -0
  47. package/lib/API/pm2-plus/link.js +126 -0
  48. package/lib/API/pm2-plus/pres/motd +16 -0
  49. package/lib/API/pm2-plus/pres/motd.update +26 -0
  50. package/lib/API/pm2-plus/pres/welcome +28 -0
  51. package/lib/API/pm2-plus/process-selector.js +52 -0
  52. package/lib/API/schema.json +379 -0
  53. package/lib/API.js +1931 -0
  54. package/lib/Client.js +776 -0
  55. package/lib/Common.js +911 -0
  56. package/lib/Configuration.js +304 -0
  57. package/lib/Daemon.js +456 -0
  58. package/lib/Event.js +37 -0
  59. package/lib/God/ActionMethods.js +909 -0
  60. package/lib/God/ClusterMode.js +97 -0
  61. package/lib/God/ForkMode.js +297 -0
  62. package/lib/God/Methods.js +265 -0
  63. package/lib/God/Reload.js +240 -0
  64. package/lib/God.js +632 -0
  65. package/lib/HttpInterface.js +76 -0
  66. package/lib/ProcessContainer.js +305 -0
  67. package/lib/ProcessContainerBun.js +360 -0
  68. package/lib/ProcessContainerFork.js +42 -0
  69. package/lib/ProcessContainerForkBun.js +33 -0
  70. package/lib/ProcessUtils.js +55 -0
  71. package/lib/TreeKill.js +118 -0
  72. package/lib/Utility.js +430 -0
  73. package/lib/VersionCheck.js +46 -0
  74. package/lib/Watcher.js +117 -0
  75. package/lib/Worker.js +169 -0
  76. package/lib/binaries/CLI.js +1041 -0
  77. package/lib/binaries/DevCLI.js +183 -0
  78. package/lib/binaries/Runtime.js +101 -0
  79. package/lib/binaries/Runtime4Docker.js +192 -0
  80. package/lib/completion.js +229 -0
  81. package/lib/completion.sh +40 -0
  82. package/lib/motd +36 -0
  83. package/lib/templates/Dockerfiles/Dockerfile-java.tpl +7 -0
  84. package/lib/templates/Dockerfiles/Dockerfile-nodejs.tpl +8 -0
  85. package/lib/templates/Dockerfiles/Dockerfile-ruby.tpl +7 -0
  86. package/lib/templates/ecosystem-es.tpl +24 -0
  87. package/lib/templates/ecosystem-simple-es.tpl +8 -0
  88. package/lib/templates/ecosystem-simple.tpl +6 -0
  89. package/lib/templates/ecosystem.tpl +22 -0
  90. package/lib/templates/init-scripts/launchd.tpl +35 -0
  91. package/lib/templates/init-scripts/openrc.tpl +52 -0
  92. package/lib/templates/init-scripts/pm2-init-amazon.sh +86 -0
  93. package/lib/templates/init-scripts/rcd-openbsd.tpl +41 -0
  94. package/lib/templates/init-scripts/rcd.tpl +44 -0
  95. package/lib/templates/init-scripts/smf.tpl +43 -0
  96. package/lib/templates/init-scripts/systemd-online.tpl +22 -0
  97. package/lib/templates/init-scripts/systemd.tpl +22 -0
  98. package/lib/templates/init-scripts/upstart.tpl +103 -0
  99. package/lib/templates/logrotate.d/pm2 +10 -0
  100. package/lib/templates/sample-apps/http-server/README.md +14 -0
  101. package/lib/templates/sample-apps/http-server/api.js +9 -0
  102. package/lib/templates/sample-apps/http-server/ecosystem.config.js +14 -0
  103. package/lib/templates/sample-apps/http-server/package.json +11 -0
  104. package/lib/templates/sample-apps/pm2-plus-metrics-actions/README.md +45 -0
  105. package/lib/templates/sample-apps/pm2-plus-metrics-actions/custom-metrics.js +66 -0
  106. package/lib/templates/sample-apps/pm2-plus-metrics-actions/ecosystem.config.js +12 -0
  107. package/lib/templates/sample-apps/pm2-plus-metrics-actions/package.json +11 -0
  108. package/lib/templates/sample-apps/python-app/README.md +4 -0
  109. package/lib/templates/sample-apps/python-app/echo.py +7 -0
  110. package/lib/templates/sample-apps/python-app/ecosystem.config.js +12 -0
  111. package/lib/templates/sample-apps/python-app/package.json +11 -0
  112. package/lib/tools/Config.js +248 -0
  113. package/lib/tools/IsAbsolute.js +20 -0
  114. package/lib/tools/copydirSync.js +101 -0
  115. package/lib/tools/deleteFolderRecursive.js +19 -0
  116. package/lib/tools/find-package-json.js +74 -0
  117. package/lib/tools/fmt.js +72 -0
  118. package/lib/tools/isbinaryfile.js +94 -0
  119. package/lib/tools/json5.js +752 -0
  120. package/lib/tools/open.js +63 -0
  121. package/lib/tools/passwd.js +58 -0
  122. package/lib/tools/promise.min.js +1 -0
  123. package/lib/tools/sexec.js +55 -0
  124. package/lib/tools/treeify.js +113 -0
  125. package/lib/tools/which.js +120 -0
  126. package/lib/tools/xdg-open +861 -0
  127. package/package.json +219 -0
  128. package/paths.js +93 -0
  129. package/pm2 +11 -0
  130. package/preinstall.js +24 -0
  131. package/run.sh +9 -0
  132. package/types/index.d.ts +722 -0
  133. package/types/tsconfig.json +14 -0
@@ -0,0 +1,752 @@
1
+ // json5.js
2
+ // Modern JSON. See README.md for details.
3
+ //
4
+ // This file is based directly off of Douglas Crockford's json_parse.js:
5
+ // https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js
6
+
7
+ var JSON5 = (typeof exports === 'object' ? exports : {});
8
+
9
+ JSON5.parse = (function () {
10
+ "use strict";
11
+
12
+ // This is a function that can parse a JSON5 text, producing a JavaScript
13
+ // data structure. It is a simple, recursive descent parser. It does not use
14
+ // eval or regular expressions, so it can be used as a model for implementing
15
+ // a JSON5 parser in other languages.
16
+
17
+ // We are defining the function inside of another function to avoid creating
18
+ // global variables.
19
+
20
+ var at, // The index of the current character
21
+ ch, // The current character
22
+ escapee = {
23
+ "'": "'",
24
+ '"': '"',
25
+ '\\': '\\',
26
+ '/': '/',
27
+ '\n': '', // Replace escaped newlines in strings w/ empty string
28
+ b: '\b',
29
+ f: '\f',
30
+ n: '\n',
31
+ r: '\r',
32
+ t: '\t'
33
+ },
34
+ ws = [
35
+ ' ',
36
+ '\t',
37
+ '\r',
38
+ '\n',
39
+ '\v',
40
+ '\f',
41
+ '\xA0',
42
+ '\uFEFF'
43
+ ],
44
+ text,
45
+
46
+ error = function (m) {
47
+
48
+ // Call error when something is wrong.
49
+
50
+ var error = new SyntaxError();
51
+ error.message = m;
52
+ error.at = at;
53
+ error.text = text;
54
+ throw error;
55
+ },
56
+
57
+ next = function (c) {
58
+
59
+ // If a c parameter is provided, verify that it matches the current character.
60
+
61
+ if (c && c !== ch) {
62
+ error("Expected '" + c + "' instead of '" + ch + "'");
63
+ }
64
+
65
+ // Get the next character. When there are no more characters,
66
+ // return the empty string.
67
+
68
+ ch = text.charAt(at);
69
+ at += 1;
70
+ return ch;
71
+ },
72
+
73
+ peek = function () {
74
+
75
+ // Get the next character without consuming it or
76
+ // assigning it to the ch varaible.
77
+
78
+ return text.charAt(at);
79
+ },
80
+
81
+ identifier = function () {
82
+
83
+ // Parse an identifier. Normally, reserved words are disallowed here, but we
84
+ // only use this for unquoted object keys, where reserved words are allowed,
85
+ // so we don't check for those here. References:
86
+ // - http://es5.github.com/#x7.6
87
+ // - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables
88
+ // - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
89
+
90
+ var key = ch;
91
+
92
+ // Identifiers must start with a letter, _ or $.
93
+ if ((ch !== '_' && ch !== '$') &&
94
+ (ch < 'a' || ch > 'z') &&
95
+ (ch < 'A' || ch > 'Z')) {
96
+ error("Bad identifier");
97
+ }
98
+
99
+ // Subsequent characters can contain digits.
100
+ while (next() && (
101
+ ch === '_' || ch === '$' ||
102
+ (ch >= 'a' && ch <= 'z') ||
103
+ (ch >= 'A' && ch <= 'Z') ||
104
+ (ch >= '0' && ch <= '9'))) {
105
+ key += ch;
106
+ }
107
+
108
+ return key;
109
+ },
110
+
111
+ number = function () {
112
+
113
+ // Parse a number value.
114
+
115
+ var number,
116
+ sign = '',
117
+ string = '',
118
+ base = 10;
119
+
120
+ if (ch === '-' || ch === '+') {
121
+ sign = ch;
122
+ next(ch);
123
+ }
124
+
125
+ // support for Infinity (could tweak to allow other words):
126
+ if (ch === 'I') {
127
+ number = word();
128
+ if (typeof number !== 'number' || isNaN(number)) {
129
+ error('Unexpected word for number');
130
+ }
131
+ return (sign === '-') ? -number : number;
132
+ }
133
+
134
+ // support for NaN
135
+ if (ch === 'N' ) {
136
+ number = word();
137
+ if (!isNaN(number)) {
138
+ error('expected word to be NaN');
139
+ }
140
+ // ignore sign as -NaN also is NaN
141
+ return number;
142
+ }
143
+
144
+ if (ch === '0') {
145
+ string += ch;
146
+ next();
147
+ if (ch === 'x' || ch === 'X') {
148
+ string += ch;
149
+ next();
150
+ base = 16;
151
+ } else if (ch >= '0' && ch <= '9') {
152
+ error('Octal literal');
153
+ }
154
+ }
155
+
156
+ switch (base) {
157
+ case 10:
158
+ while (ch >= '0' && ch <= '9' ) {
159
+ string += ch;
160
+ next();
161
+ }
162
+ if (ch === '.') {
163
+ string += '.';
164
+ while (next() && ch >= '0' && ch <= '9') {
165
+ string += ch;
166
+ }
167
+ }
168
+ if (ch === 'e' || ch === 'E') {
169
+ string += ch;
170
+ next();
171
+ if (ch === '-' || ch === '+') {
172
+ string += ch;
173
+ next();
174
+ }
175
+ while (ch >= '0' && ch <= '9') {
176
+ string += ch;
177
+ next();
178
+ }
179
+ }
180
+ break;
181
+ case 16:
182
+ while (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {
183
+ string += ch;
184
+ next();
185
+ }
186
+ break;
187
+ }
188
+
189
+ if(sign === '-') {
190
+ number = -string;
191
+ } else {
192
+ number = +string;
193
+ }
194
+
195
+ if (!isFinite(number)) {
196
+ error("Bad number");
197
+ } else {
198
+ return number;
199
+ }
200
+ },
201
+
202
+ string = function () {
203
+
204
+ // Parse a string value.
205
+
206
+ var hex,
207
+ i,
208
+ string = '',
209
+ delim, // double quote or single quote
210
+ uffff;
211
+
212
+ // When parsing for string values, we must look for ' or " and \ characters.
213
+
214
+ if (ch === '"' || ch === "'") {
215
+ delim = ch;
216
+ while (next()) {
217
+ if (ch === delim) {
218
+ next();
219
+ return string;
220
+ } else if (ch === '\\') {
221
+ next();
222
+ if (ch === 'u') {
223
+ uffff = 0;
224
+ for (i = 0; i < 4; i += 1) {
225
+ hex = parseInt(next(), 16);
226
+ if (!isFinite(hex)) {
227
+ break;
228
+ }
229
+ uffff = uffff * 16 + hex;
230
+ }
231
+ string += String.fromCharCode(uffff);
232
+ } else if (ch === '\r') {
233
+ if (peek() === '\n') {
234
+ next();
235
+ }
236
+ } else if (typeof escapee[ch] === 'string') {
237
+ string += escapee[ch];
238
+ } else {
239
+ break;
240
+ }
241
+ } else if (ch === '\n') {
242
+ // unescaped newlines are invalid; see:
243
+ // https://github.com/aseemk/json5/issues/24
244
+ // invalid unescaped chars?
245
+ break;
246
+ } else {
247
+ string += ch;
248
+ }
249
+ }
250
+ }
251
+ error("Bad string");
252
+ },
253
+
254
+ inlineComment = function () {
255
+
256
+ // Skip an inline comment, assuming this is one. The current character should
257
+ // be the second / character in the // pair that begins this inline comment.
258
+ // To finish the inline comment, we look for a newline or the end of the text.
259
+
260
+ if (ch !== '/') {
261
+ error("Not an inline comment");
262
+ }
263
+
264
+ do {
265
+ next();
266
+ if (ch === '\n' || ch === '\r') {
267
+ next();
268
+ return;
269
+ }
270
+ } while (ch);
271
+ },
272
+
273
+ blockComment = function () {
274
+
275
+ // Skip a block comment, assuming this is one. The current character should be
276
+ // the * character in the /* pair that begins this block comment.
277
+ // To finish the block comment, we look for an ending */ pair of characters,
278
+ // but we also watch for the end of text before the comment is terminated.
279
+
280
+ if (ch !== '*') {
281
+ error("Not a block comment");
282
+ }
283
+
284
+ do {
285
+ next();
286
+ while (ch === '*') {
287
+ next('*');
288
+ if (ch === '/') {
289
+ next('/');
290
+ return;
291
+ }
292
+ }
293
+ } while (ch);
294
+
295
+ error("Unterminated block comment");
296
+ },
297
+
298
+ comment = function () {
299
+
300
+ // Skip a comment, whether inline or block-level, assuming this is one.
301
+ // Comments always begin with a / character.
302
+
303
+ if (ch !== '/') {
304
+ error("Not a comment");
305
+ }
306
+
307
+ next('/');
308
+
309
+ if (ch === '/') {
310
+ inlineComment();
311
+ } else if (ch === '*') {
312
+ blockComment();
313
+ } else {
314
+ error("Unrecognized comment");
315
+ }
316
+ },
317
+
318
+ white = function () {
319
+
320
+ // Skip whitespace and comments.
321
+ // Note that we're detecting comments by only a single / character.
322
+ // This works since regular expressions are not valid JSON(5), but this will
323
+ // break if there are other valid values that begin with a / character!
324
+
325
+ while (ch) {
326
+ if (ch === '/') {
327
+ comment();
328
+ } else if (ws.indexOf(ch) >= 0) {
329
+ next();
330
+ } else {
331
+ return;
332
+ }
333
+ }
334
+ },
335
+
336
+ word = function () {
337
+
338
+ // true, false, or null.
339
+
340
+ switch (ch) {
341
+ case 't':
342
+ next('t');
343
+ next('r');
344
+ next('u');
345
+ next('e');
346
+ return true;
347
+ case 'f':
348
+ next('f');
349
+ next('a');
350
+ next('l');
351
+ next('s');
352
+ next('e');
353
+ return false;
354
+ case 'n':
355
+ next('n');
356
+ next('u');
357
+ next('l');
358
+ next('l');
359
+ return null;
360
+ case 'I':
361
+ next('I');
362
+ next('n');
363
+ next('f');
364
+ next('i');
365
+ next('n');
366
+ next('i');
367
+ next('t');
368
+ next('y');
369
+ return Infinity;
370
+ case 'N':
371
+ next( 'N' );
372
+ next( 'a' );
373
+ next( 'N' );
374
+ return NaN;
375
+ }
376
+ error("Unexpected '" + ch + "'");
377
+ },
378
+
379
+ value, // Place holder for the value function.
380
+
381
+ array = function () {
382
+
383
+ // Parse an array value.
384
+
385
+ var array = [];
386
+
387
+ if (ch === '[') {
388
+ next('[');
389
+ white();
390
+ while (ch) {
391
+ if (ch === ']') {
392
+ next(']');
393
+ return array; // Potentially empty array
394
+ }
395
+ // ES5 allows omitting elements in arrays, e.g. [,] and
396
+ // [,null]. We don't allow this in JSON5.
397
+ if (ch === ',') {
398
+ error("Missing array element");
399
+ } else {
400
+ array.push(value());
401
+ }
402
+ white();
403
+ // If there's no comma after this value, this needs to
404
+ // be the end of the array.
405
+ if (ch !== ',') {
406
+ next(']');
407
+ return array;
408
+ }
409
+ next(',');
410
+ white();
411
+ }
412
+ }
413
+ error("Bad array");
414
+ },
415
+
416
+ object = function () {
417
+
418
+ // Parse an object value.
419
+
420
+ var key,
421
+ object = {};
422
+
423
+ if (ch === '{') {
424
+ next('{');
425
+ white();
426
+ while (ch) {
427
+ if (ch === '}') {
428
+ next('}');
429
+ return object; // Potentially empty object
430
+ }
431
+
432
+ // Keys can be unquoted. If they are, they need to be
433
+ // valid JS identifiers.
434
+ if (ch === '"' || ch === "'") {
435
+ key = string();
436
+ } else {
437
+ key = identifier();
438
+ }
439
+
440
+ white();
441
+ next(':');
442
+ object[key] = value();
443
+ white();
444
+ // If there's no comma after this pair, this needs to be
445
+ // the end of the object.
446
+ if (ch !== ',') {
447
+ next('}');
448
+ return object;
449
+ }
450
+ next(',');
451
+ white();
452
+ }
453
+ }
454
+ error("Bad object");
455
+ };
456
+
457
+ value = function () {
458
+
459
+ // Parse a JSON value. It could be an object, an array, a string, a number,
460
+ // or a word.
461
+
462
+ white();
463
+ switch (ch) {
464
+ case '{':
465
+ return object();
466
+ case '[':
467
+ return array();
468
+ case '"':
469
+ case "'":
470
+ return string();
471
+ case '-':
472
+ case '+':
473
+ case '.':
474
+ return number();
475
+ default:
476
+ return ch >= '0' && ch <= '9' ? number() : word();
477
+ }
478
+ };
479
+
480
+ // Return the json_parse function. It will have access to all of the above
481
+ // functions and variables.
482
+
483
+ return function (source, reviver) {
484
+ var result;
485
+
486
+ text = String(source);
487
+ at = 0;
488
+ ch = ' ';
489
+ result = value();
490
+ white();
491
+ if (ch) {
492
+ error("Syntax error");
493
+ }
494
+
495
+ // If there is a reviver function, we recursively walk the new structure,
496
+ // passing each name/value pair to the reviver function for possible
497
+ // transformation, starting with a temporary root object that holds the result
498
+ // in an empty key. If there is not a reviver function, we simply return the
499
+ // result.
500
+
501
+ return typeof reviver === 'function' ? (function walk(holder, key) {
502
+ var k, v, value = holder[key];
503
+ if (value && typeof value === 'object') {
504
+ for (k in value) {
505
+ if (Object.prototype.hasOwnProperty.call(value, k)) {
506
+ v = walk(value, k);
507
+ if (v !== undefined) {
508
+ value[k] = v;
509
+ } else {
510
+ delete value[k];
511
+ }
512
+ }
513
+ }
514
+ }
515
+ return reviver.call(holder, key, value);
516
+ }({'': result}, '')) : result;
517
+ };
518
+ }());
519
+
520
+ // JSON5 stringify will not quote keys where appropriate
521
+ JSON5.stringify = function (obj, replacer, space) {
522
+ if (replacer && (typeof(replacer) !== "function" && !isArray(replacer))) {
523
+ throw new Error('Replacer must be a function or an array');
524
+ }
525
+ var getReplacedValueOrUndefined = function(holder, key, isTopLevel) {
526
+ var value = holder[key];
527
+
528
+ // Replace the value with its toJSON value first, if possible
529
+ if (value && value.toJSON && typeof value.toJSON === "function") {
530
+ value = value.toJSON();
531
+ }
532
+
533
+ // If the user-supplied replacer if a function, call it. If it's an array, check objects' string keys for
534
+ // presence in the array (removing the key/value pair from the resulting JSON if the key is missing).
535
+ if (typeof(replacer) === "function") {
536
+ return replacer.call(holder, key, value);
537
+ } else if(replacer) {
538
+ if (isTopLevel || isArray(holder) || replacer.indexOf(key) >= 0) {
539
+ return value;
540
+ } else {
541
+ return undefined;
542
+ }
543
+ } else {
544
+ return value;
545
+ }
546
+ };
547
+
548
+ function isWordChar(char) {
549
+ return (char >= 'a' && char <= 'z') ||
550
+ (char >= 'A' && char <= 'Z') ||
551
+ (char >= '0' && char <= '9') ||
552
+ char === '_' || char === '$';
553
+ }
554
+
555
+ function isWordStart(char) {
556
+ return (char >= 'a' && char <= 'z') ||
557
+ (char >= 'A' && char <= 'Z') ||
558
+ char === '_' || char === '$';
559
+ }
560
+
561
+ function isWord(key) {
562
+ if (typeof key !== 'string') {
563
+ return false;
564
+ }
565
+ if (!isWordStart(key[0])) {
566
+ return false;
567
+ }
568
+ var i = 1, length = key.length;
569
+ while (i < length) {
570
+ if (!isWordChar(key[i])) {
571
+ return false;
572
+ }
573
+ i++;
574
+ }
575
+ return true;
576
+ }
577
+
578
+ // export for use in tests
579
+ JSON5.isWord = isWord;
580
+
581
+ // polyfills
582
+ function isArray(obj) {
583
+ if (Array.isArray) {
584
+ return Array.isArray(obj);
585
+ } else {
586
+ return Object.prototype.toString.call(obj) === '[object Array]';
587
+ }
588
+ }
589
+
590
+ function isDate(obj) {
591
+ return Object.prototype.toString.call(obj) === '[object Date]';
592
+ }
593
+
594
+ isNaN = isNaN || function(val) {
595
+ return typeof val === 'number' && val !== val;
596
+ };
597
+
598
+ var objStack = [];
599
+ function checkForCircular(obj) {
600
+ for (var i = 0; i < objStack.length; i++) {
601
+ if (objStack[i] === obj) {
602
+ throw new TypeError("Converting circular structure to JSON");
603
+ }
604
+ }
605
+ }
606
+
607
+ function makeIndent(str, num, noNewLine) {
608
+ if (!str) {
609
+ return "";
610
+ }
611
+ // indentation no more than 10 chars
612
+ if (str.length > 10) {
613
+ str = str.substring(0, 10);
614
+ }
615
+
616
+ var indent = noNewLine ? "" : "\n";
617
+ for (var i = 0; i < num; i++) {
618
+ indent += str;
619
+ }
620
+
621
+ return indent;
622
+ }
623
+
624
+ var indentStr;
625
+ if (space) {
626
+ if (typeof space === "string") {
627
+ indentStr = space;
628
+ } else if (typeof space === "number" && space >= 0) {
629
+ indentStr = makeIndent(" ", space, true);
630
+ } else {
631
+ // ignore space parameter
632
+ }
633
+ }
634
+
635
+ // Copied from Crokford's implementation of JSON
636
+ // See https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195
637
+ // Begin
638
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
639
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
640
+ meta = { // table of character substitutions
641
+ '\b': '\\b',
642
+ '\t': '\\t',
643
+ '\n': '\\n',
644
+ '\f': '\\f',
645
+ '\r': '\\r',
646
+ '"' : '\\"',
647
+ '\\': '\\\\'
648
+ };
649
+ function escapeString(string) {
650
+
651
+ // If the string contains no control characters, no quote characters, and no
652
+ // backslash characters, then we can safely slap some quotes around it.
653
+ // Otherwise we must also replace the offending characters with safe escape
654
+ // sequences.
655
+ escapable.lastIndex = 0;
656
+ return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
657
+ var c = meta[a];
658
+ return typeof c === 'string' ?
659
+ c :
660
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
661
+ }) + '"' : '"' + string + '"';
662
+ }
663
+ // End
664
+
665
+ function internalStringify(holder, key, isTopLevel) {
666
+ var buffer, res;
667
+
668
+ // Replace the value, if necessary
669
+ var obj_part = getReplacedValueOrUndefined(holder, key, isTopLevel);
670
+
671
+ if (obj_part && !isDate(obj_part)) {
672
+ // unbox objects
673
+ // don't unbox dates, since will turn it into number
674
+ obj_part = obj_part.valueOf();
675
+ }
676
+ switch(typeof obj_part) {
677
+ case "boolean":
678
+ return obj_part.toString();
679
+
680
+ case "number":
681
+ if (isNaN(obj_part) || !isFinite(obj_part)) {
682
+ return "null";
683
+ }
684
+ return obj_part.toString();
685
+
686
+ case "string":
687
+ return escapeString(obj_part.toString());
688
+
689
+ case "object":
690
+ if (obj_part === null) {
691
+ return "null";
692
+ } else if (isArray(obj_part)) {
693
+ checkForCircular(obj_part);
694
+ buffer = "[";
695
+ objStack.push(obj_part);
696
+
697
+ for (var i = 0; i < obj_part.length; i++) {
698
+ res = internalStringify(obj_part, i, false);
699
+ buffer += makeIndent(indentStr, objStack.length);
700
+ if (res === null || typeof res === "undefined") {
701
+ buffer += "null";
702
+ } else {
703
+ buffer += res;
704
+ }
705
+ if (i < obj_part.length-1) {
706
+ buffer += ",";
707
+ } else if (indentStr) {
708
+ buffer += "\n";
709
+ }
710
+ }
711
+ objStack.pop();
712
+ buffer += makeIndent(indentStr, objStack.length, true) + "]";
713
+ } else {
714
+ checkForCircular(obj_part);
715
+ buffer = "{";
716
+ var nonEmpty = false;
717
+ objStack.push(obj_part);
718
+ for (var prop in obj_part) {
719
+ if (obj_part.hasOwnProperty(prop)) {
720
+ var value = internalStringify(obj_part, prop, false);
721
+ isTopLevel = false;
722
+ if (typeof value !== "undefined" && value !== null) {
723
+ buffer += makeIndent(indentStr, objStack.length);
724
+ nonEmpty = true;
725
+ var key = isWord(prop) ? prop : escapeString(prop);
726
+ buffer += key + ":" + (indentStr ? ' ' : '') + value + ",";
727
+ }
728
+ }
729
+ }
730
+ objStack.pop();
731
+ if (nonEmpty) {
732
+ buffer = buffer.substring(0, buffer.length-1) + makeIndent(indentStr, objStack.length) + "}";
733
+ } else {
734
+ buffer = '{}';
735
+ }
736
+ }
737
+ return buffer;
738
+ default:
739
+ // functions and undefined should be ignored
740
+ return undefined;
741
+ }
742
+ }
743
+
744
+ // special case...when undefined is used inside of
745
+ // a compound object/array, return null.
746
+ // but when top-level, return undefined
747
+ var topLevelHolder = {"":obj};
748
+ if (obj === undefined) {
749
+ return getReplacedValueOrUndefined(topLevelHolder, '', true);
750
+ }
751
+ return internalStringify(topLevelHolder, '', true);
752
+ };