mirror of https://github.com/01-edu/public.git
2 changed files with 454 additions and 0 deletions
@ -0,0 +1,402 @@ |
|||||||
|
import * as cp from 'child_process' |
||||||
|
import fs from 'fs/promises' |
||||||
|
import { tmpdir } from 'os' |
||||||
|
import { promisify } from 'util' |
||||||
|
const mkdir = fs.mkdir |
||||||
|
const writeFile = fs.writeFile |
||||||
|
const readFile = fs.readFile |
||||||
|
|
||||||
|
const exec = promisify(cp.exec) |
||||||
|
|
||||||
|
export const tests = [] |
||||||
|
|
||||||
|
export const setup = async ({ path }) => { |
||||||
|
const tmpPath = `${tmpdir()}/happiness-manager` |
||||||
|
|
||||||
|
await mkdir(tmpPath) |
||||||
|
await mkdir(`${tmpPath}/guests`) |
||||||
|
const run = async (folder, file) => { |
||||||
|
const output = await exec( |
||||||
|
`node ${path} ${tmpPath}/${folder} ${tmpPath}/${file}`, |
||||||
|
) |
||||||
|
const fileContent = await readFile(`${tmpPath}/${file}`, 'utf8').catch( |
||||||
|
(err) => (err.code === 'ENOENT' ? 'output file not found' : err), |
||||||
|
) |
||||||
|
|
||||||
|
return { |
||||||
|
data: |
||||||
|
fileContent === 'output file not found' |
||||||
|
? fileContent |
||||||
|
: JSON.parse(fileContent), |
||||||
|
stdout: output.stdout.trim(), |
||||||
|
stderr: output.stderr.trim(), |
||||||
|
} |
||||||
|
} |
||||||
|
const resetAnswersIn = async ({ folder }) => { |
||||||
|
const dir = await fs.readdir(`${tmpPath}/${folder}`) |
||||||
|
await Promise.all(dir.map((file) => fs.rm(`${tmpPath}/${folder}/${file}`))) |
||||||
|
} |
||||||
|
const createAnswers = (nb, elem) => [...Array(nb).keys()].map(() => elem) |
||||||
|
const setAnswersIn = async ({ answers, folder }) => { |
||||||
|
await resetAnswersIn({ folder }) |
||||||
|
await Promise.all( |
||||||
|
answers.map( |
||||||
|
async (content, idx) => |
||||||
|
await writeFile( |
||||||
|
`${tmpPath}/${folder}/${idx}.json`, |
||||||
|
JSON.stringify(content, null, '\t'), |
||||||
|
'utf8', |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
return { run, tmpPath, createAnswers, resetAnswersIn, setAnswersIn } |
||||||
|
} |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test with no vips (no {answer: yes})
|
||||||
|
// no file should be created, a special message should appear in console
|
||||||
|
const answers = ctx.createAnswers(2, { answer: 'no' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { stdout, data } = await ctx.run('guests', 'happy-list.json') |
||||||
|
return eq( |
||||||
|
{ stdout, data }, |
||||||
|
{ stdout: 'No one is coming.', data: 'output file not found' }, |
||||||
|
) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'carnivores' }
|
||||||
|
// should create a list with burgers and potatoes
|
||||||
|
const answers = ctx.createAnswers(2, { answer: 'yes', food: 'carnivore' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-carn-list.json') |
||||||
|
return eq(data, { burgers: 2, potatoes: 2 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'fish' }
|
||||||
|
// should create a list with sardines and potatoes
|
||||||
|
const answers = [ |
||||||
|
{ answer: 'no', food: 'fish' }, |
||||||
|
...ctx.createAnswers(3, { answer: 'yes', food: 'fish' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-fish-list.json') |
||||||
|
return eq(data, { potatoes: 3, sardines: 3 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'everything' }
|
||||||
|
// should create a list with kebabs and potatoes
|
||||||
|
const answers = [ |
||||||
|
{ answer: 'no', food: 'everything' }, |
||||||
|
...ctx.createAnswers(3, { answer: 'yes', food: 'everything' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-omni-list.json') |
||||||
|
return eq(data, { potatoes: 3, kebabs: 3 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'beer' }
|
||||||
|
// should create a list with 6-packs-beers and potatoes
|
||||||
|
const answers = [ |
||||||
|
{ answer: 'no', drink: 'beer' }, |
||||||
|
...ctx.createAnswers(1, { answer: 'yes', drink: 'beer' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-beer-list.json') |
||||||
|
return eq(data, { potatoes: 1, '6-packs-beers': 1 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'beer' }
|
||||||
|
// should create a list with 6-packs-beers and potatoes
|
||||||
|
const answers = [ |
||||||
|
{ answer: 'no', drink: 'beer' }, |
||||||
|
...ctx.createAnswers(6, { answer: 'yes', drink: 'beer' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-beer-pack-list.json') |
||||||
|
return eq(data, { potatoes: 6, '6-packs-beers': 1 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'wine' }
|
||||||
|
// should create a list with wine-bottles and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(3, { answer: 'no', drink: 'wine' }), |
||||||
|
...ctx.createAnswers(5, { answer: 'yes', drink: 'wine' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-wine-list.json') |
||||||
|
return eq(data, { potatoes: 5, 'wine-bottles': 2 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'wine' }
|
||||||
|
// should create a list with wine-bottles and potatoes
|
||||||
|
const answers = ctx.createAnswers(8, { answer: 'yes', drink: 'wine' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-wine-bottle-list.json') |
||||||
|
return eq(data, { potatoes: 8, 'wine-bottles': 2 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'water' }
|
||||||
|
// should create a list with water-bottles and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(2, { answer: 'no', drink: 'water' }), |
||||||
|
...ctx.createAnswers(2, { answer: 'yes', drink: 'water' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-water-list.json') |
||||||
|
return eq(data, { potatoes: 2, 'water-bottles': 1 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'water' }
|
||||||
|
// should create a list with water-bottles and potatoes
|
||||||
|
const answers = ctx.createAnswers(7, { answer: 'yes', drink: 'water' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-water-bottle-list.json') |
||||||
|
return eq(data, { potatoes: 7, 'water-bottles': 2 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'soft' }
|
||||||
|
// should create a list with soft-bottles and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(8, { answer: 'no', drink: 'soft' }), |
||||||
|
...ctx.createAnswers(12, { answer: 'yes', drink: 'soft' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-soft-list.json') |
||||||
|
return eq(data, { potatoes: 12, 'soft-bottles': 3 }) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { drink: 'soft' }
|
||||||
|
// should create a list with soft-bottles and potatoes
|
||||||
|
const answers = ctx.createAnswers(13, { answer: 'yes', drink: 'soft' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-soft-bottle-list.json') |
||||||
|
return eq(data, { potatoes: 13, 'soft-bottles': 4 }) |
||||||
|
}) |
||||||
|
|
||||||
|
// tests with veggstuff
|
||||||
|
// 1) vegan but no veggie
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'vegan' }
|
||||||
|
// should create a list with eggplants, mushrooms, courgettes and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(2, { answer: 'no', food: 'vegan' }), |
||||||
|
...ctx.createAnswers(4, { answer: 'yes', food: 'vegan' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-vegan-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 4, |
||||||
|
mushrooms: 4, |
||||||
|
eggplants: 2, |
||||||
|
courgettes: 2, |
||||||
|
hummus: 2, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'vegan' }
|
||||||
|
// should create a list with eggplants, mushrooms, hummus, courgettes and potatoes
|
||||||
|
const answers = ctx.createAnswers(6, { answer: 'yes', food: 'vegan' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-vegan-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 6, |
||||||
|
mushrooms: 6, |
||||||
|
eggplants: 2, |
||||||
|
courgettes: 2, |
||||||
|
hummus: 2, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// 2) veggie but no vegan
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'veggie' }
|
||||||
|
// should create a list with eggplants, mushrooms, courgettes and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(2, { answer: 'no', food: 'veggie' }), |
||||||
|
...ctx.createAnswers(4, { answer: 'yes', food: 'veggie' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-veggie-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 4, |
||||||
|
mushrooms: 4, |
||||||
|
eggplants: 2, |
||||||
|
courgettes: 2, |
||||||
|
hummus: 2, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'veggie' }
|
||||||
|
// should create a list with eggplants, mushrooms, hummus, courgettes and potatoes
|
||||||
|
const answers = ctx.createAnswers(6, { answer: 'yes', food: 'veggie' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-veggie-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 6, |
||||||
|
mushrooms: 6, |
||||||
|
eggplants: 2, |
||||||
|
courgettes: 2, |
||||||
|
hummus: 2, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// 3) both
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'vegan' } and { food: 'veggie' }
|
||||||
|
// should create a list with eggplants, mushrooms, courgettes and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(4, { answer: 'yes', food: 'vegan' }), |
||||||
|
...ctx.createAnswers(2, { answer: 'yes', food: 'veggie' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-vegg-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 6, |
||||||
|
mushrooms: 6, |
||||||
|
eggplants: 2, |
||||||
|
courgettes: 2, |
||||||
|
hummus: 2, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test when vips answer { food: 'vegan' } and { food: 'veggie' }
|
||||||
|
// should create a list with eggplants, mushrooms, hummus, courgettes and potatoes
|
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(6, { answer: 'yes', food: 'vegan' }), |
||||||
|
...ctx.createAnswers(1, { answer: 'yes', food: 'veggie' }), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'happy-vegg-list.json') |
||||||
|
return eq(data, { |
||||||
|
potatoes: 7, |
||||||
|
mushrooms: 7, |
||||||
|
eggplants: 3, |
||||||
|
courgettes: 3, |
||||||
|
hummus: 3, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// test with existing file
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test with an existing file
|
||||||
|
// should add elems to the existing list
|
||||||
|
await writeFile( |
||||||
|
`${ctx.tmpPath}/old-happy-list.json`, |
||||||
|
JSON.stringify({ candies: 2000 }), |
||||||
|
) |
||||||
|
const answers = ctx.createAnswers(1, { answer: 'yes', food: 'vegan' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'old-happy-list.json') |
||||||
|
return eq(data, { |
||||||
|
candies: 2000, |
||||||
|
potatoes: 1, |
||||||
|
mushrooms: 1, |
||||||
|
eggplants: 1, |
||||||
|
courgettes: 1, |
||||||
|
hummus: 1, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test with an existing file
|
||||||
|
// should replace elems in the existing list (if already there)
|
||||||
|
await writeFile( |
||||||
|
`${ctx.tmpPath}/old-happy-list.json`, |
||||||
|
JSON.stringify({ candies: 2000, potatoes: 32 }), |
||||||
|
) |
||||||
|
const answers = ctx.createAnswers(1, { answer: 'yes', food: 'vegan' }) |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'old-happy-list.json') |
||||||
|
return eq(data, { |
||||||
|
candies: 2000, |
||||||
|
potatoes: 1, |
||||||
|
mushrooms: 1, |
||||||
|
eggplants: 1, |
||||||
|
courgettes: 1, |
||||||
|
hummus: 1, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// test with a little bit of everything
|
||||||
|
tests.push(async ({ path, eq, ctx }) => { |
||||||
|
// test with mix of everything
|
||||||
|
await writeFile( |
||||||
|
`${ctx.tmpPath}/party.json`, |
||||||
|
JSON.stringify({ 'super-gift': 1, balloons: 100, 'flower-bouquet': 7 }), |
||||||
|
) |
||||||
|
const answers = [ |
||||||
|
...ctx.createAnswers(7, { answer: 'no', food: 'vegan', drink: 'water' }), |
||||||
|
...ctx.createAnswers(2, { |
||||||
|
answer: 'yes', |
||||||
|
food: 'carnivore', |
||||||
|
drink: 'soft', |
||||||
|
}), |
||||||
|
...ctx.createAnswers(6, { answer: 'yes', food: 'vegan', drink: 'water' }), |
||||||
|
...ctx.createAnswers(2, { answer: 'yes', food: 'veggie', drink: 'water' }), |
||||||
|
...ctx.createAnswers(3, { answer: 'yes', food: 'veggie', drink: 'beer' }), |
||||||
|
...ctx.createAnswers(11, { answer: 'yes', food: 'fish', drink: 'wine' }), |
||||||
|
...ctx.createAnswers(4, { |
||||||
|
answer: 'yes', |
||||||
|
food: 'everything', |
||||||
|
drink: 'beer', |
||||||
|
}), |
||||||
|
] |
||||||
|
await ctx.setAnswersIn({ folder: 'guests', answers }) |
||||||
|
|
||||||
|
const { data } = await ctx.run('guests', 'party.json') |
||||||
|
return eq(data, { |
||||||
|
'super-gift': 1, |
||||||
|
balloons: 100, |
||||||
|
'flower-bouquet': 7, |
||||||
|
potatoes: 28, |
||||||
|
mushrooms: 11, |
||||||
|
eggplants: 4, |
||||||
|
courgettes: 4, |
||||||
|
hummus: 4, |
||||||
|
sardines: 11, |
||||||
|
burgers: 2, |
||||||
|
kebabs: 4, |
||||||
|
'6-packs-beers': 2, |
||||||
|
'wine-bottles': 3, |
||||||
|
'water-bottles': 2, |
||||||
|
'soft-bottles': 1, |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
Object.freeze(tests) |
@ -0,0 +1,52 @@ |
|||||||
|
## happiness-manager |
||||||
|
|
||||||
|
### Instructions |
||||||
|
|
||||||
|
<3 Your pleasure? 💖 That of others Ɛ> |
||||||
|
|
||||||
|
As you're smart, you asked every guest of the party to precise in their answer the kind of drink they would enjoy and the kind of food they would die for. |
||||||
|
|
||||||
|
Create a `happiness-manager.mjs` script that sort, in the vips, who wants to drink what and who wants to eat what and integrate that in your barbecue's shopping list! |
||||||
|
|
||||||
|
The script must: |
||||||
|
- Take a folder as first argument (the `guest` folder) |
||||||
|
- Take a file `.json` as second argument: |
||||||
|
- If the file already exists, it will add the informations to it. |
||||||
|
- If it doesn't, the script must handle the creation of the file. |
||||||
|
- Handle case when no one answered yes to the invitation: |
||||||
|
- `No one is coming.` has to appear in console. |
||||||
|
- No file is updated/created. |
||||||
|
- Handle cases when answers contains no "food" information, or no "drink" information |
||||||
|
- Handle cases when no one has chosen a cotagory (for example: no one chose to drink softs). This category should not appear in the final list. |
||||||
|
|
||||||
|
You have to handle the info like this: |
||||||
|
- Drinks: |
||||||
|
- Beers: 1 pack / 6 vips (rounded up). Expected key: `6-packs-beers`. |
||||||
|
- Water, wine, softs: 1 bottle / 4 vips in each category (rounded up). Expected keys: `wine-bottles`, `water-bottles`, `soft-bottles`. |
||||||
|
- Food: |
||||||
|
- Veggies and vegans: 1 eggplant, 1 courgette, 3 mushrooms and 1 hummus / 3 vips in these categories put together. Expected keys: `eggplants`, `mushrooms`, `hummus`, `courgettes`. |
||||||
|
- Carnivores: 1 burger per person. Expected key: `burgers`. |
||||||
|
- Fish lovers: 1 sardine per person. Expected key: `sardines`. |
||||||
|
- Omnivores: 1 chicken+shrimps+pepper kebab / person. Expected key: `kebabs`. |
||||||
|
- Bonus: you'll add 1 potatoe per person (all categories put together). Expected key: `potatoes`. |
||||||
|
|
||||||
|
The infos have to be formated like this in the `.json` file: |
||||||
|
```json |
||||||
|
{ |
||||||
|
"key": 1 // according to actual number associated to the elem |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
### Notions |
||||||
|
|
||||||
|
- [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) |
||||||
|
- [`Array.prototype.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Map) |
||||||
|
- [`Math.ceil()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil) |
||||||
|
- [Node file system: `stats`](https://nodejs.org/api/fs.html#fs_fspromises_stat_path_options) |
||||||
|
- [Node file system: `readdir`](https://nodejs.org/api/fs.html#fs_fspromises_readdir_path_options) |
||||||
|
- [Node file system: `readFile`](https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_options) |
||||||
|
- [Node file system: `writeFile`](https://nodejs.org/api/fs.html#fs_fspromises_writefile_file_data_options) |
||||||
|
- [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) |
||||||
|
- [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) |
||||||
|
- [Node process: `exit`](https://nodejs.org/api/process.html#process_process_exit_code) |
||||||
|
|
Loading…
Reference in new issue