From 21b259136ba362270bf9b5e92208fc688a91096c Mon Sep 17 00:00:00 2001 From: tuandev6688 Date: Wed, 9 Feb 2022 10:06:50 +0700 Subject: [PATCH] Formatting, Prettier and Editorconfig --- docs/.editorconfig | 12 + docs/.eslintrc.js | 20 + docs/.prettierrc | 7 + docs/blog/2021-03-08-mern.md | 191 +- docs/blog/2021-03-09-postgres-data-gen.md | 169 +- docs/blog/2021-08-09-macro.md | 52 +- .../2021-08-31-seeding-databases-tutorial.md | 241 +- .../2021-09-07-mocking-a-production-api.md | 94 +- docs/blog/2021-09-27-crash.md | 159 +- ...2021-10-07-building-a-startup-with-rust.md | 36 +- docs/blog/2021-10-11-nightly.md | 12 +- docs/docs/content/array.md | 4 +- docs/docs/content/bool.md | 5 +- docs/docs/content/datasource.md | 8 +- docs/docs/content/date-time.md | 11 +- docs/docs/content/index.md | 76 +- docs/docs/content/modifiers.md | 6 +- docs/docs/content/null.md | 4 +- docs/docs/content/number.md | 27 +- docs/docs/content/object.md | 2 + docs/docs/content/one-of.md | 47 +- docs/docs/content/same-as.md | 2 +- docs/docs/content/series.md | 304 +- docs/docs/content/string.md | 527 ++-- docs/docs/content/toc.md | 80 +- docs/docs/content/unique.md | 66 +- docs/docs/examples/bank.md | 9 +- docs/docs/getting_started/core-concepts.md | 19 +- docs/docs/getting_started/hello-world.md | 29 +- docs/docs/getting_started/how-it-works.md | 3 +- docs/docs/getting_started/installation.md | 1 + docs/docs/getting_started/schema.md | 48 +- docs/docs/getting_started/synth.md | 4 +- docs/docs/integrations/postgres.md | 11 +- docs/docs/other/telemetry.md | 38 +- .../tutorials/creating-logs-with-synth.md | 552 ++-- docs/docusaurus.config.js | 345 ++- docs/package-lock.json | 1585 ++++++++++- docs/package.json | 10 +- docs/sidebars.js | 39 +- docs/src/css/custom.css | 13 +- docs/src/lib/fathom.js | 74 +- docs/src/lib/playground.ts | 102 +- docs/src/pages/index.tsx | 5 +- docs/src/theme/CodeBlock/index.tsx | 565 ++-- docs/src/theme/PlaygroundBlock/index.tsx | 115 +- docs/tsconfig.json | 2 +- docs/vercel.json | 2 +- www/.editorconfig | 12 + www/.eslintrc.js | 20 + www/.gitignore | 2 + www/.prettierrc | 7 + www/README.md | 2 +- www/components/AccentButton.tsx | 73 +- www/components/AnnouncementBar/index.tsx | 62 +- .../AnnouncementBar/styles.module.css | 70 +- www/components/CallToAction.tsx | 47 +- www/components/Card.tsx | 76 +- www/components/CardNoLink.tsx | 58 +- www/components/Code.tsx | 48 +- www/components/Contact.tsx | 166 +- www/components/Container.tsx | 20 +- www/components/Download.tsx | 300 +- www/components/Examples.tsx | 170 +- www/components/Features.tsx | 87 +- www/components/Footer.tsx | 262 +- www/components/Hero.tsx | 103 +- www/components/HowItWorks.tsx | 44 +- www/components/LearnMore.tsx | 38 +- www/components/Logo.tsx | 56 +- www/components/NavBarMenu.tsx | 142 +- www/components/Navbar.tsx | 106 +- www/components/PrettyCode.tsx | 70 +- www/components/Privacy.tsx | 860 ++++-- www/components/Section.tsx | 58 +- www/components/SmallCard.tsx | 69 +- www/components/Snippets.tsx | 207 +- www/components/Synth.tsx | 12 +- www/components/Terms.tsx | 994 ++++--- www/components/UseCases.tsx | 69 +- www/lib/constants.ts | 5 +- www/lib/helpers.ts | 49 +- www/lib/playground.ts | 98 +- www/next-sitemap.js | 6 +- www/next.config.js | 2 +- www/package-lock.json | 2481 ++++++++++++++++- www/package.json | 10 +- www/pages/404.tsx | 26 +- www/pages/_app.tsx | 104 +- www/pages/_document.tsx | 24 +- www/pages/api/install.tsx | 141 +- www/pages/contact.tsx | 6 +- www/pages/download.tsx | 4 +- www/pages/index.tsx | 16 +- www/pages/privacy.tsx | 7 +- www/pages/terms.tsx | 6 +- www/styles/index.css | 25 +- www/tailwind.config.js | 175 +- www/tsconfig.json | 20 +- www/vercel.json | 4 +- 100 files changed, 8817 insertions(+), 4435 deletions(-) create mode 100644 docs/.editorconfig create mode 100644 docs/.eslintrc.js create mode 100644 docs/.prettierrc create mode 100644 www/.editorconfig create mode 100644 www/.eslintrc.js create mode 100644 www/.prettierrc diff --git a/docs/.editorconfig b/docs/.editorconfig new file mode 100644 index 00000000..ebe51d3b --- /dev/null +++ b/docs/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js new file mode 100644 index 00000000..5ac4e62a --- /dev/null +++ b/docs/.eslintrc.js @@ -0,0 +1,20 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: ['plugin:react/recommended', 'google', 'prettier'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['react', '@typescript-eslint'], + rules: {}, + react: { + version: 'latest', + }, +}; diff --git a/docs/.prettierrc b/docs/.prettierrc new file mode 100644 index 00000000..cc84f72e --- /dev/null +++ b/docs/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "trailingComma": "es5", + "singleQuote": true, + "semi": true +} diff --git a/docs/blog/2021-03-08-mern.md b/docs/blog/2021-03-08-mern.md index 19e5e44f..6083230f 100644 --- a/docs/blog/2021-03-08-mern.md +++ b/docs/blog/2021-03-08-mern.md @@ -10,9 +10,9 @@ image: https://i.imgur.com/mErPwqL.png hide_table_of_contents: false --- -So we've all been in this situation. You're building a Web App, you're super productive in your stack and you can go quickly - however generating lot's of data to see what your app will look like with enough users and traffic is a pain. +So we've all been in this situation. You're building a Web App, you're super productive in your stack and you can go quickly - however generating lot's of data to see what your app will look like with enough users and traffic is a pain. -Either you're going to spend a lot of time manually inputting data or you're going to write some scripts to generate that data for you. There *must* be a better way. +Either you're going to spend a lot of time manually inputting data or you're going to write some scripts to generate that data for you. There _must_ be a better way. In this post we're going to explore how we can solve this problem using the open-source project [Synth][synth]. Synth is a state-of-the-art declarative data generator - you tell Synth what you want your data to look like and Synth will generate that data for you. @@ -40,7 +40,7 @@ docker run -d --name mongo-on-docker -p 27017:27017 mongo The repository we just cloned contains a working end-to-end web-app running on a MERN stack. It's a super simple CRUD application enabling the user to add / remove some movie reviews which are persisted on a MongoDB database. -The app consists of 2 main components, a `nodejs` server which lives under the `movies-app/server/` sub-directory, and a `React` front-end which lives under the `movies-app/client` sub-directory. +The app consists of 2 main components, a `nodejs` server which lives under the `movies-app/server/` sub-directory, and a `React` front-end which lives under the `movies-app/client` sub-directory. The client and server talk to each other using a standard HTTP API under `/movie`. @@ -87,9 +87,9 @@ To install Synth on MacOS / Linux, visit the [docs](/) and choose the appropriat Synth uses a declarative data model to specify how data is generated. -Hmmm, so what is a declarative model you may ask? A **declarative model**, as opposed to an imperative model, is where you 'declare' your desired end state and the underlying program will figure out how to get there. +Hmmm, so what is a declarative model you may ask? A **declarative model**, as opposed to an imperative model, is where you 'declare' your desired end state and the underlying program will figure out how to get there. -On the other had, an imperative model (which is what we are mostly used to), is step by step instructions on how to get to our end-state. Most popular programming languages like Java or C are *imperative* - your code is step-by-step instructions on how to reach an end state. +On the other had, an imperative model (which is what we are mostly used to), is step by step instructions on how to get to our end-state. Most popular programming languages like Java or C are _imperative_ - your code is step-by-step instructions on how to reach an end state. Programming frameworks like SQL or React or Terraform are declarative. You don't specify how to get to your end-state, you just specify what you want and the underlying program will figure out how to get there. @@ -99,7 +99,7 @@ With Synth you specify what your desired dataset should look like, not how to ma A **workspace** represents a set of synthetic data namespaces managed by Synth. Workspaces are marked by `.synth/` sub-directory. -A workspace can have *zero or more namespaces*, where the namespaces are just represented as sub-directories. All information pertaining to a workspace is in its directory. +A workspace can have _zero or more namespaces_, where the namespaces are just represented as sub-directories. All information pertaining to a workspace is in its directory. So let's create sub-directory called `data/` and initialize our Synth workspace. @@ -174,7 +174,7 @@ $ synth generate cinema/ } ``` -So now we've generated data with the same schema as the original - but the value of the data points doesn't really line up with the semantic meaning of our dataset. For example, the `time` array is just garbled text, not actual times of the day. +So now we've generated data with the same schema as the original - but the value of the data points doesn't really line up with the semantic meaning of our dataset. For example, the `time` array is just garbled text, not actual times of the day. The last steps is to tweak the Synth schema and create some realistic looking data! @@ -242,9 +242,9 @@ So let's open `cinema/movies.json` in our favorite text editor and take a look a } ``` -There is a lot going on here but let's break it down. +There is a lot going on here but let's break it down. -The top-level object (which represents our `movies` collection) is of type `array` - where the `content` of the array is an object with 4 fields, `_id`, `name`, `time`, and `rating`. +The top-level object (which represents our `movies` collection) is of type `array` - where the `content` of the array is an object with 4 fields, `_id`, `name`, `time`, and `rating`. We can completely remove the field `_id` since this is automatically managed by MongoDB and get started in making our data look real. You may want to have the [Generators Reference](/docs/content/null) open here for reference. @@ -254,13 +254,13 @@ First let's change the `rating` field. Our app can only accept numbers between 0 ```json synth { - "range": { - "high": 10, - "low": 0, - "step": 0.5 - }, - "subtype": "f64", - "type": "number" + "range": { + "high": 10, + "low": 0, + "step": 0.5 + }, + "subtype": "f64", + "type": "number" } ``` @@ -270,31 +270,31 @@ The `time` field has been correctly detected as an array of values. First of all ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u64", - "range": { - "low": 1, - "high": 5, - "step": 1 - } - }, - "content": { - "type": "one_of", - "variants": [ - { - "weight": 1.0, - "type": "string", - "date_time": { - "subtype": "naive_time", - "format": "%H:%M", - "begin": "12:00", - "end": "23:59" - } - } - ] + "type": "array", + "length": { + "type": "number", + "subtype": "u64", + "range": { + "low": 1, + "high": 5, + "step": 1 } + }, + "content": { + "type": "one_of", + "variants": [ + { + "weight": 1.0, + "type": "string", + "date_time": { + "subtype": "naive_time", + "format": "%H:%M", + "begin": "12:00", + "end": "23:59" + } + } + ] + } } ``` @@ -314,11 +314,11 @@ So let's use the `String::Faker` content type to generate some fake movie names! ```json synth { - "type": "string", - "faker": { - "generator": "text", - "max_nb_chars": 20 - } + "type": "string", + "faker": { + "generator": "text", + "max_nb_chars": 20 + } } ``` @@ -328,63 +328,63 @@ So, making all the changes above, we can use our beautiful finished schema to ge ```json synth { - "type": "array", - "length": { + "type": "array", + "length": { + "type": "number", + "subtype": "u64", + "range": { + "low": 1, + "high": 2, + "step": 1 + } + }, + "content": { + "type": "object", + "name": { + "type": "string", + "faker": { + "generator": "text", + "max_nb_chars": 20 + } + }, + "time": { + "optional": false, + "type": "array", + "length": { "type": "number", "subtype": "u64", "range": { - "low": 1, - "high": 2, - "step": 1 + "low": 1, + "high": 5, + "step": 1 } - }, - "content": { - "type": "object", - "name": { + }, + "content": { + "type": "one_of", + "variants": [ + { + "weight": 1.0, "type": "string", - "faker": { - "generator": "text", - "max_nb_chars": 20 - } - }, - "time": { - "optional": false, - "type": "array", - "length": { - "type": "number", - "subtype": "u64", - "range": { - "low": 1, - "high": 5, - "step": 1 - } - }, - "content": { - "type": "one_of", - "variants": [ - { - "weight": 1.0, - "type": "string", - "date_time": { - "subtype": "naive_time", - "format": "%H:%M", - "begin": "00:00", - "end": "23:59" - } - } - ] + "date_time": { + "subtype": "naive_time", + "format": "%H:%M", + "begin": "00:00", + "end": "23:59" } - }, - "rating" : { - "range": { - "high": 10, - "low": 0, - "step": 0.5 - }, - "subtype": "f64", - "type": "number" - } + } + ] + } + }, + "rating": { + "range": { + "high": 10, + "low": 0, + "step": 0.5 + }, + "subtype": "f64", + "type": "number" } + } } ``` @@ -466,5 +466,4 @@ This post was a summary of how you can use Synth to generate realistic looking t To check out the Synth source code you can visit the Synth repo on [GitHub](https://github.com/getsynth/synth/), and to join the conversation hop-on the the [Synth discord server](https://discord.com/invite/wwJVAFKKkq). - [synth]: https://github.com/getsynth/synth diff --git a/docs/blog/2021-03-09-postgres-data-gen.md b/docs/blog/2021-03-09-postgres-data-gen.md index c0bb4f65..585a6422 100644 --- a/docs/blog/2021-03-09-postgres-data-gen.md +++ b/docs/blog/2021-03-09-postgres-data-gen.md @@ -15,13 +15,13 @@ hide_table_of_contents: false Developing high quality software inevitably requires some testing data. You could be: + - Integration testing your application for correctness and regressions - Testing the bounds of your application in your QA process - Testing the performance of queries as the size of your dataset increases Either way, the software development lifecycle requires testing data as an integral part of developer workflow. In this article, we'll be exploring 3 different methods for generating test data for a Postgres database. - ## Setup In this example we'll be using Docker to host our Postgres database. @@ -32,13 +32,12 @@ To get started you'll need to [install docker](https://docs.docker.com/get-docke % docker run -p 5432:5432 -d -e POSTGRES_PASSWORD=1234 -e POSTGRES_USER=postgres -e POSTGRES_DB=dev postgres ``` -As you can see, we've set very insecure default credentials. This is *not* meant to be a robust / productionised instance, but it'll do for our testing harness. - +As you can see, we've set very insecure default credentials. This is _not_ meant to be a robust / productionised instance, but it'll do for our testing harness. ## Our Schema In this example we'll setup a very simple schema. We're creating a basic app where we have a bunch of companies, and those companies have contacts. - + ```sql CREATE TABLE companies( company_id SERIAL PRIMARY KEY, @@ -52,10 +51,10 @@ CREATE TABLE contacts( phone VARCHAR(25), email VARCHAR(100), CONSTRAINT fk_company - FOREIGN KEY(company_id) + FOREIGN KEY(company_id) REFERENCES companies(company_id) ); -``` +``` This schema captures some business logic of our app. We have unique primary keys, we have foreign key constraints, and we have some domain-specific data types which have 'semantic meaning'. For example, the random string `_SX Æ A-ii` is not a valid phone number. @@ -68,8 +67,8 @@ The first thing you can do which works well when you're starting your project is ```sql INSERT INTO companies(company_name) VALUES('BlueBird Inc'), - ('Dolphin LLC'); - + ('Dolphin LLC'); + INSERT INTO contacts(company_id, contact_name, phone, email) VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'), (1,'Jane Doe','(408)-111-1235','jane.doe@bluebird.dev'), @@ -78,17 +77,17 @@ VALUES(1,'John Doe','(408)-111-1234','john.doe@bluebird.dev'), So here we're inserting directly into our database. This method is straight forward but does not scale when you need more data or the complexity of your schema increases. Also, testing for edge cases requires your hard-coding edge cases in the inserted data - resulting in a linear amount of work for the bugs you want to catch. -|contact_id|company_id|contact_name |phone |email | -|----------|----------|--------------------------------|--------------------|--------------------------------| -|1 |1 |John Doe |(408)-111-1234 |john.doe@bluebird.dev | -|2 |1 |Jane Doe |(408)-111-1235 |jane.doe@bluebird.dev | -|3 |2 |David Wright |(408)-222-1234 |david.wright@dolphin.dev | +| contact_id | company_id | contact_name | phone | email | +| ---------- | ---------- | ------------ | -------------- | ------------------------ | +| 1 | 1 | John Doe | (408)-111-1234 | john.doe@bluebird.dev | +| 2 | 1 | Jane Doe | (408)-111-1235 | jane.doe@bluebird.dev | +| 3 | 2 | David Wright | (408)-222-1234 | david.wright@dolphin.dev | ## Using generate_series to automate the process Since you're a programmer, you don't like manual work. You like things to be seamless and most importantly automated! -Postgres comes with a handy function called `generate_series` which, ...*drum roll*... generates series! We can use this to generate as much data as we want without writing it by hand. +Postgres comes with a handy function called `generate_series` which, ..._drum roll_... generates series! We can use this to generate as much data as we want without writing it by hand. Let's use `generate_series` to create 100 companies and 100 contacts @@ -98,23 +97,23 @@ SELECT md5(random()::text) FROM generate_series(1,100); INSERT INTO contacts(company_id, contact_name, phone, email) -SELECT id, md5(random()::text), md5(random()::text)::varchar(20), md5(random()::text) +SELECT id, md5(random()::text), md5(random()::text)::varchar(20), md5(random()::text) FROM generate_series(1,100) id; -``` +``` -|contact_id|company_id|contact_name |phone |email | -|----------|----------|--------------------------------|--------------------|--------------------------------| -|1 |1 |81cc02c106b7c30d4e2b032c91cdb75a|d056f1eee1dca55db03c|cd0da2eef81aaa02d6ba15ef4551fb9f| -|2 |2 |d2b0112bc9bbec85c5229a4b4f28a350|07ba86b1dc24cdadfd24|7404f5b502084563f2ac20c29ed0e584| -|3 |3 |64005702ecaff9f489e8074d6a718aae|50db9534b58e0616cd34|3ea36293665aa1ac38e7d6371893046a| -|4 |4 |202e87bc3d0c8c080048b2c0138c709b|65f6ea317bd0f2c950dc|8b8d9b92916f4cf77c38308f6ac4391b| -|5 |5 |8b2fd25d7b95158df5af671cb3255755|3e6ddc67aabe7164ce9a|ed32035400a7500203352f3597d2548f| +| contact_id | company_id | contact_name | phone | email | +| ---------- | ---------- | -------------------------------- | -------------------- | -------------------------------- | +| 1 | 1 | 81cc02c106b7c30d4e2b032c91cdb75a | d056f1eee1dca55db03c | cd0da2eef81aaa02d6ba15ef4551fb9f | +| 2 | 2 | d2b0112bc9bbec85c5229a4b4f28a350 | 07ba86b1dc24cdadfd24 | 7404f5b502084563f2ac20c29ed0e584 | +| 3 | 3 | 64005702ecaff9f489e8074d6a718aae | 50db9534b58e0616cd34 | 3ea36293665aa1ac38e7d6371893046a | +| 4 | 4 | 202e87bc3d0c8c080048b2c0138c709b | 65f6ea317bd0f2c950dc | 8b8d9b92916f4cf77c38308f6ac4391b | +| 5 | 5 | 8b2fd25d7b95158df5af671cb3255755 | 3e6ddc67aabe7164ce9a | ed32035400a7500203352f3597d2548f | -We generated 100 companies and contacts here, the types are correct, *but* the output is underwhelming. First of all, every company has exactly 1 contact, and more importantly the actual data looks completely useless. +We generated 100 companies and contacts here, the types are correct, _but_ the output is underwhelming. First of all, every company has exactly 1 contact, and more importantly the actual data looks completely useless. If you care about your data being semantically correct (i.e. text in your `phone` column actually being a phone number) we need to get more sophisticated. -We could define functions ourselves to generate names / phone numbers / emails etc, but why re-invent the wheel? +We could define functions ourselves to generate names / phone numbers / emails etc, but why re-invent the wheel? ## Using a data generator like Synth @@ -125,14 +124,14 @@ Synth uses declarative configuration files (just JSON don't worry) to define how The first step to use Synth is to create a workspace. A workspace is just a directory in your filesystem that tell Synth that this is where you are going to be storing configuration: ```bash -$ mkdir workspace && cd workspace && synth init +$ mkdir workspace && cd workspace && synth init ``` Next we want to create a namespace (basically a stand-alone data model) for this schema. We do this by simply creating a subdirectory and Synth will treat it as a separate schema: ```bash $ mkdir my_app -``` +``` Now comes the fun part! Using Synth's configuration language we can specify how our data is generated. Let's start with the smaller table `companies`. @@ -140,24 +139,24 @@ To tell Synth that `companies` is a table (or collection in the Synth lingo) we' ```json { - "type": "array", - "length": { - "type": "number", - "constant": 1 + "type": "array", + "length": { + "type": "number", + "constant": 1 + }, + "content": { + "type": "object", + "company_id": { + "type": "number", + "id": {} }, - "content": { - "type": "object", - "company_id": { - "type": "number", - "id": {} - }, - "company_name": { - "type": "string", - "faker": { - "generator": "company_name" - } - } + "company_name": { + "type": "string", + "faker": { + "generator": "company_name" + } } + } } ``` @@ -183,42 +182,41 @@ $ synth generate my_app/ --size 2 Now we can do the same thing for the `contacts` table by create a file `my_app/contacts.json`. Here we have the added complexity of a foreign key constraints to the company table, but we can solve it easily using Synth's [`same_as`](/docs/content/same-as) generator. - ```json { - "type": "array", - "length": { - "type": "number", - "constant": 1 + "type": "array", + "length": { + "type": "number", + "constant": 1 + }, + "content": { + "type": "object", + "company_id": { + "type": "same_as", + "ref": "companies.content.company_id" + }, + "contact_name": { + "type": "string", + "faker": { + "generator": "name" + } }, - "content": { - "type": "object", - "company_id": { - "type": "same_as", - "ref":"companies.content.company_id" - }, - "contact_name": { - "type": "string", - "faker": { - "generator": "name" - } - }, - "phone": { - "type": "string", - "faker": { - "generator": "phone_number", - "locales": ["en_GB"] - } - }, - "email": { - "type": "string", - "faker": { - "generator": "safe_email" - } - } + "phone": { + "type": "string", + "faker": { + "generator": "phone_number", + "locales": ["en_GB"] + } + }, + "email": { + "type": "string", + "faker": { + "generator": "safe_email" + } } + } } -``` +``` There is quite a bit going on here - to get an in-depth understanding of the synth configuration refer I'd recommend reading the comprehensive docs. There are tons of cool features which this schema can't really explore! @@ -230,20 +228,18 @@ $ synth generate my_app/ --to postgres://postgres:1234@localhost:5432/dev Taking a look at the company table: -|contact_id|company_id|contact_name |phone |email | -|----------|----------|--------------------------------|--------------------|--------------------------------| -|1 |1 |Carrie Walsh |+44(0)117 496 0785 |espinozabetty@hotmail.com | -|2 |2 |Brittany Flores |+441632 960 480 |osharp@mcdaniel.com | -|3 |3 |Tammy Rodriguez |01632960737 |brenda82@ward.org | -|4 |4 |Amanda Marks |(0808) 1570096 |hwilcox@gonzalez.com | -|5 |5 |Kimberly Delacruz MD |+44(0)114 4960207 |pgarcia@thompson.com | -|6 |6 |Jordan Williamson |(0121) 4960483 |jamesmiles@weber.org | -|7 |7 |Nicholas Williams |(0131) 496 0974 |fordthomas@gmail.com | - +| contact_id | company_id | contact_name | phone | email | +| ---------- | ---------- | -------------------- | ------------------ | ------------------------- | +| 1 | 1 | Carrie Walsh | +44(0)117 496 0785 | espinozabetty@hotmail.com | +| 2 | 2 | Brittany Flores | +441632 960 480 | osharp@mcdaniel.com | +| 3 | 3 | Tammy Rodriguez | 01632960737 | brenda82@ward.org | +| 4 | 4 | Amanda Marks | (0808) 1570096 | hwilcox@gonzalez.com | +| 5 | 5 | Kimberly Delacruz MD | +44(0)114 4960207 | pgarcia@thompson.com | +| 6 | 6 | Jordan Williamson | (0121) 4960483 | jamesmiles@weber.org | +| 7 | 7 | Nicholas Williams | (0131) 496 0974 | fordthomas@gmail.com | Much better :) - ## Conclusion We explored 3 different ways to generate data. @@ -252,5 +248,4 @@ We explored 3 different ways to generate data. - **Postgres generate_series**: This method scales better than manual insertion - but if you care about the contents of your data and have foreign key constraints you'll need to write quite a bit of bespoke SQL by hand. - [**Synth**](https://github.com/getsynth/synth): Synth has a small learning curve, but to create realistic testing data at scale it reduces most of the manual labour. - -In the next post we'll explore how to subset your existing database for testing purposes. And don't worry if you have sensitive / personal data - we'll cover that too. \ No newline at end of file +In the next post we'll explore how to subset your existing database for testing purposes. And don't worry if you have sensitive / personal data - we'll cover that too. diff --git a/docs/blog/2021-08-09-macro.md b/docs/blog/2021-08-09-macro.md index c7b0fbc2..46c434e6 100644 --- a/docs/blog/2021-08-09-macro.md +++ b/docs/blog/2021-08-09-macro.md @@ -18,34 +18,34 @@ With synth, we are building a declarative command line test data generator. For ```json synth { - "type": "array", - "length": { - "type": "number", - "constant": 3 + "type": "array", + "length": { + "type": "number", + "constant": 3 + }, + "content": { + "type": "object", + "id": { + "type": "number", + "id": {} }, - "content": { - "type": "object", - "id": { - "type": "number", - "id": {} - }, - "name": { - "type": "string", - "faker": { - "generator": "name" - } - }, - "email": { - "type": "string", - "faker": { - "generator": "ascii_email" - } - } + "name": { + "type": "string", + "faker": { + "generator": "name" + } + }, + "email": { + "type": "string", + "faker": { + "generator": "ascii_email" + } } + } } ``` -However, it’s also not very nice to write (for example JSON has no comments, no formulas, etc.), so we wanted to bind our specification to a scripting language. Our end goal is to extend the language (both in terms of builtin functions and syntax) to make the configuration really elegant. After some testing and benchmarking different runtimes, our choice fell on [koto](https://github.com/koto-lang/koto), a nice little scripting language that was built foremost for live coding. +However, it’s also not very nice to write (for example JSON has no comments, no formulas, etc.), so we wanted to bind our specification to a scripting language. Our end goal is to extend the language (both in terms of builtin functions and syntax) to make the configuration really elegant. After some testing and benchmarking different runtimes, our choice fell on [koto](https://github.com/koto-lang/koto), a nice little scripting language that was built foremost for live coding. Unfortunately, koto has a very bare interface to bind to external Rust code. Since we are talking about a rather large number of types we want to include, it was clear from the start that we would want to generate the code to bind to koto. @@ -83,7 +83,7 @@ fn bindlang_main(arg: TokenStream) -> TokenStream { I also wrote a derive macro to implement the marshalling traits. This worked well for a small example that was entirely contained within one module, but failed once the code was spread out through multiple modules: The functions would no longer be in the same scope and therefore stopped finding each other. -Worse, I needed a number of pre-defined maps with functions for method dispatch for our external types within koto. A type in Rust can have an arbitrary number of impl blocks but I needed exactly *one* table, and I couldn’t simply daisy-chain those. +Worse, I needed a number of pre-defined maps with functions for method dispatch for our external types within koto. A type in Rust can have an arbitrary number of impl blocks but I needed exactly _one_ table, and I couldn’t simply daisy-chain those. It was clear I needed a different solution. After thinking long and hard I came to the conclusion that I needed to pull all the code together in one scope, by the `bindlang_main!()` macro. My idea was that I create a `HashMap` of `syn::Item`s to be quoted together into one `TokenStream`. A lazy static `Arc>>` was to collect the information from multiple attribute invocations: @@ -147,7 +147,7 @@ Having this in a separate trait allows us to distinguish types where the borrow let mut expr: Expr = parse_quote! { #path(#(#inner_idents),*) }; for (i, ((a, v), mode)) in idents.iter().zip(args.iter()).enumerate().rev() { expr = if mode.is_ref() { - parse_quote! { + parse_quote! { ::lang_bindings::RefFromValue::ref_from_value( &::lang_bindings::KeyPath::Index(#i, None), #a, @@ -159,7 +159,7 @@ for (i, ((a, v), mode)) in idents.iter().zip(args.iter()).enumerate().rev() { match (::lang_bindings::FromValue::from_value( &::lang_bindings::KeyPath::Index(#i, None), #a - )?) { + )?) { (#v) => #expr } } diff --git a/docs/blog/2021-08-31-seeding-databases-tutorial.md b/docs/blog/2021-08-31-seeding-databases-tutorial.md index 7287abe0..7b33e4ef 100644 --- a/docs/blog/2021-08-31-seeding-databases-tutorial.md +++ b/docs/blog/2021-08-31-seeding-databases-tutorial.md @@ -3,7 +3,19 @@ title: Seeding test databases in 2021 - best practices author_url: https://github.com/brokad/ author: Damien B. (@brokad) author_image_url: https://avatars.githubusercontent.com/u/13315034?v=4 -tags: [postgres, test data, data generation, tutorial, beginners guide, seeding, prisma, schema, data model, orm] +tags: + [ + postgres, + test data, + data generation, + tutorial, + beginners guide, + seeding, + prisma, + schema, + data model, + orm, + ] description: In this tutorial, we'll learn how to design a Prisma data model for a basic message board and how to seed test databases with mock data using open-source tools. image: https://storage.googleapis.com/getsynth-public/media/orm_small.jpg hide_table_of_contents: false @@ -34,7 +46,7 @@ The crux of the problem of data modeling is to summarize and write down what constitutes useful entities and how they relate to one another in a graph of connections. -You may wonder what constitutes a *useful* entity. It is indeed the toughest +You may wonder what constitutes a _useful_ entity. It is indeed the toughest question to answer. It is very difficult to tackle it without a good combined idea of what you are building, the database you are building on top of and what the most common queries, operations and aggregate statistics are. There @@ -55,7 +67,7 @@ on top of data models really enjoyable. One of them is Prisma. ### Prisma is awesome -[Prisma][prisma] is an ORM, an *object relational mapping*. It is a powerful +[Prisma][prisma] is an ORM, an _object relational mapping_. It is a powerful framework that lets you specify your data model using a database agnostic domain specific language (called the [Prisma schema][prisma-schema]). It uses [pluggable generators][prisma-generate] to build a nice javascript API and @@ -66,8 +78,8 @@ addition to a powerful query engine. Let's walk through a example. We want to get a sense for what it'll take to design the data model for a simple message board a little like [Reddit][reddit] or [YCombinator's Hacker News][hacker-news]. At the very minimum, we want to -have a concept of *users*: people should be able to register for an account. -Beyond that, we need a concept of *posts*: some structure, attached to users, +have a concept of _users_: people should be able to register for an account. +Beyond that, we need a concept of _posts_: some structure, attached to users, that holds the content they publish. Using the [Prisma schema][prisma-schema] language, which is very expressive even @@ -117,7 +129,7 @@ one-to-many relationship between users and posts. You may have noticed that the `User` and `Post` models have an attribute which we haven't mentioned. The `objectId` property is -an [internal unique identifier][mongodb-objectid] used by [mongoDB][mongodb] +an [internal unique identifier][mongodb-objectid] used by [mongoDB][mongodb] (the database we're choosing to implement our data model on in this tutorial). ::: @@ -171,27 +183,27 @@ This creates a `tsconfig.json` file which configures the behavior of the typescript compiler. Create a directory `src/` and add the following `index.ts`: ```javascript -import {PrismaClient} from '@prisma/client' +import { PrismaClient } from '@prisma/client'; -const prisma = new PrismaClient() +const prisma = new PrismaClient(); const main = async () => { - const user = await prisma.user.findFirst() - if (user === null) { - throw Error("No user data.") - } - console.log(`found username: ${user.nickname}`) - process.exit(0) -} + const user = await prisma.user.findFirst(); + if (user === null) { + throw Error('No user data.'); + } + console.log(`found username: ${user.nickname}`); + process.exit(0); +}; main().catch((e) => { - console.error(e) - process.exit(1) -}) + console.error(e); + process.exit(1); +}); ``` Then create a `prisma/` directory and add a `schema.prisma` file containing -the Prisma code for the two entities `User` and `Post`. +the Prisma code for the two entities `User` and `Post`. Finally, to our `schema.prisma` file, we need to add configuration for our local dev database and the generation of the client: @@ -225,7 +237,7 @@ script with: { ... "test": "tsc --project ./ && node ." - ... + ... } ``` @@ -284,7 +296,7 @@ possible? The faster the cycle is, the better your productivity becomes. One of the keys to shortening a development cycle is making testing easy. When playing with databases and data models, it is something that is often hacky. In fact there are very few tools that let you iterate quickly on data models, much -less *developer-friendly* tools. +less _developer-friendly_ tools. The core issue at hand is that between iterations on ideas and features, we will need to make small and quick changes to our data model. What happens to our @@ -359,7 +371,7 @@ schema files. ├── prisma/ ├── synth/ └── src/ -``` +``` Each file we will put in the `synth/` directory that ends in `.json` will be opened by [`synth`][synth-cli], parsed and interpreted as part of our data @@ -480,7 +492,7 @@ is `Int`: ```graphql id Int @unique @default(autoincrement()) -``` +``` and the attribute indicates that the field is meant to increment sequentially, going through values 0, 1, 2 etc. @@ -499,12 +511,12 @@ For example, a [`range`][synth-range] variant would look like ```json synth { - "type": "number", - "range": { - "low": 5, - "high": 10, - "step": 1 - } + "type": "number", + "range": { + "low": 5, + "high": 10, + "step": 1 + } } ``` @@ -512,8 +524,8 @@ whereas a [`constant`][synth-constant] variant would look like ```json synth { - "type": "number", - "constant": 42 + "type": "number", + "constant": 42 } ``` @@ -523,12 +535,12 @@ can see it behaves as expected: ```json synth { - "type": "array", - "length": 10, - "content": { - "type": "number", - "id": {} - } + "type": "array", + "length": 10, + "content": { + "type": "number", + "id": {} + } } ``` @@ -559,10 +571,10 @@ generators for common properties like usernames, addresses and emails: ```json synth { - "type": "string", - "faker": { - "generator": "safe_email" - } + "type": "string", + "faker": { + "generator": "safe_email" + } } ``` @@ -574,17 +586,17 @@ together in one object. For that we need the [`object`][synth-object] type: ```json synth { - "type": "object", - "id": { - "type": "number", - "id": {} - }, - "email": { - "type": "string", - "faker": { - "generator": "safe_email" - } + "type": "object", + "id": { + "type": "number", + "id": {} + }, + "email": { + "type": "string", + "faker": { + "generator": "safe_email" } + } } ``` @@ -599,34 +611,34 @@ Here is the finished result for our `User.json` collection: ```json synth { - "type": "array", - "length": 3, - "content": { - "type": "object", - "id": { - "type": "number", - "id": {} - }, - "createdAt": { - "type": "string", - "date_time": { - "format": "%Y-%m-%d %H:%M:%S", - "begin": "2020-01-01 12:00:00" - } - }, - "email": { - "type": "string", - "faker": { - "generator": "safe_email" - } - }, - "nickname": { - "type": "string", - "faker": { - "generator": "username" - } - } + "type": "array", + "length": 3, + "content": { + "type": "object", + "id": { + "type": "number", + "id": {} + }, + "createdAt": { + "type": "string", + "date_time": { + "format": "%Y-%m-%d %H:%M:%S", + "begin": "2020-01-01 12:00:00" + } + }, + "email": { + "type": "string", + "faker": { + "generator": "safe_email" + } + }, + "nickname": { + "type": "string", + "faker": { + "generator": "username" + } } + } } ``` @@ -663,9 +675,9 @@ randomly generate something or nothing: ```json synth { - "type": "number", - "optional": true, - "constant": 42 + "type": "number", + "optional": true, + "constant": 42 } ``` @@ -676,11 +688,11 @@ modifier to the `email` field: ```json synth { - "type": "string", - "unique": true, - "faker": { - "generator": "safe_email" - } + "type": "string", + "unique": true, + "faker": { + "generator": "safe_email" + } } ``` @@ -779,109 +791,56 @@ also have a very [active Discord server][discord] where many members of the community would be happy to help if you encounter an issue! [binary]: https://en.wikipedia.org/wiki/Executable - [set-theory]: https://en.wikipedia.org/wiki/Set_theory - [prisma]: https://www.prisma.io/ - [prisma-generate]: https://www.prisma.io/docs/concepts/components/prisma-schema/generators - [prisma-schema]: https://www.prisma.io/docs/concepts/components/prisma-schema - [typescript]: https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html - [getsynth]: https://getsynth.com - -[JSON]: https://www.json.org/json-en.html - +[json]: https://www.json.org/json-en.html [synth-repo]: https://github.com/getsynth/synth - [installation]: /docs/getting_started/installation - [development-cycle]: https://en.wikipedia.org/wiki/Systems_development_life_cycle - [agile-framework]: https://en.wikipedia.org/wiki/Agile_software_development#Iterative,_incremental,_and_evolutionary - [postgres]: https://www.postgresql.org/ - [mongodb]: https://www.mongodb.com/ - [mongodb-objectid]: https://docs.mongodb.com/manual/reference/method/ObjectId/ - [mongo-collection]: https://docs.mongodb.com/manual/core/databases-and-collections/ - [reddit]: https://www.reddit.com/ - [hacker-news]: https://news.ycombinator.com/ - [data-modeling-101]: https://www.prisma.io/dataguide/ - [foreign-key]: https://en.wikipedia.org/wiki/Foreign_key - [primary-key]: https://en.wikipedia.org/wiki/Primary_key - [prisma-client]: https://www.prisma.io/docs/concepts/components/prisma-client - [prisma-one-to-many]: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-one-relations - [table]: https://en.wikipedia.org/wiki/Table_(information) - [synth-array]: /docs/content/array - [prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator - [synth-schema]: /docs/getting_started/schema - [synth-generators]: /docs/content/null - [synth-number]: /docs/content/number - [synth-id]: /docs/content/number#id - [synth-constant]: /docs/content/number#constant - [synth-range]: /docs/content/number#range - [synth-string]: /docs/content/string - [synth-faker]: /docs/content/string#faker - [synth-object]: /docs/content/object - [synth-modifiers]: /docs/content/modifiers - [synth-datetime]: /docs/content/date-time - [synth-optional]: /docs/content/modifiers#optional - [synth-unique]: /docs/content/modifiers#unique - [synth-same-as]: /docs/content/same-as - [n-to-1]: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-many-relations - [discord]: https://discord.com/invite/H33rRDTm3p - [synth-contributors]: https://github.com/getsynth/synth#contributors- - [synth-twitter]: https://twitter.com/getsynth - [synth-cli]: /docs/getting_started/command-line - [docker-mongo]: https://hub.docker.com/_/mongo - [docker-mysql]: https://hub.docker.com/_/mysql - [docker-postgres]: https://hub.docker.com/_/postgres - [repo-users-json]: https://github.com/getsynth/synth/tree/master/examples/message_board/synth/User.json - [repo-posts-json]: https://github.com/getsynth/synth/tree/master/examples/message_board/synth/Post.json - [repo-schema]: https://github.com/getsynth/synth/tree/master/examples/message_board/prisma/schema.prisma - [repo-complete]: https://github.com/getsynth/synth/tree/master/examples/message_board - [npm-script]: https://github.com/brokad/synth/tree/master/examples/message_board/helpers/db.js - -[prisma-relation-mongo]: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-many-relations \ No newline at end of file +[prisma-relation-mongo]: https://www.prisma.io/docs/concepts/components/prisma-schema/relations/one-to-many-relations diff --git a/docs/blog/2021-09-07-mocking-a-production-api.md b/docs/blog/2021-09-07-mocking-a-production-api.md index 4cc83678..3259c7da 100644 --- a/docs/blog/2021-09-07-mocking-a-production-api.md +++ b/docs/blog/2021-09-07-mocking-a-production-api.md @@ -1,11 +1,11 @@ --- -title: So you want to mock an API -author: Christos Hadjiaslanis -author_title: Founder +title: So you want to mock an API +author: Christos Hadjiaslanis +author_title: Founder author_url: https://github.com/getsynth author_image_url: https://avatars.githubusercontent.com/u/14791384?s=460&v=4 tags: [synth, prisma, testing, mocking, story] -description: This blog post is an overview of a 5 day prototyping journey building a mock API +description: This blog post is an overview of a 5 day prototyping journey building a mock API image: https://storage.googleapis.com/getsynth-public/media/api.jpg hide_table_of_contents: false --- @@ -57,8 +57,8 @@ Lo and behold! the internet responded. A bunch of people responded and primarily complained about payment processors (except for Stripe which was explicitly praised yet again!). A few products and companies came up repeatedly as being difficult to test against. We qualitatively evaluated the internet's feedback -and reviewed documentation from the different APIs mentioned to understand -the implementation complexity. After all we had 3.5 days left, so we couldn't +and reviewed documentation from the different APIs mentioned to understand +the implementation complexity. After all we had 3.5 days left, so we couldn't pick anything too complex. In the end we decided to go with the [Shopify API](https://shopify.dev/api/)! Just as a disclaimer we have absolutely no issues with Shopify, it just so @@ -78,26 +78,26 @@ Event can be seen below: ```json { - // Refers to a certain event and its resources. - "arguments": "Ipod Nano - 8GB", - // A text field containing information about the event. - "body": null, - // The date and time (ISO 8601 format) when the event was created. - "created_at": "2015-04-20T08:33:57-11:00", - // The ID of the event. - "id": 164748010, - // A human readable description of the event. - "desciption": "Received a new order", - // A relative URL to the resource the event is for, if applicable. - "path": "/admin/orders/406514653/transactions/#1145", - // A human readable description of the event. Can contain some HTML formatting. - "message": "Received a new order", - // The ID of the resource that generated the event. - "subject_id": 406514653, - // The type of the resource that generated the event. - "subject_type": "Order", - // The type of event that occurred. - "verb": "confirmed" + // Refers to a certain event and its resources. + "arguments": "Ipod Nano - 8GB", + // A text field containing information about the event. + "body": null, + // The date and time (ISO 8601 format) when the event was created. + "created_at": "2015-04-20T08:33:57-11:00", + // The ID of the event. + "id": 164748010, + // A human readable description of the event. + "desciption": "Received a new order", + // A relative URL to the resource the event is for, if applicable. + "path": "/admin/orders/406514653/transactions/#1145", + // A human readable description of the event. Can contain some HTML formatting. + "message": "Received a new order", + // The ID of the resource that generated the event. + "subject_id": 406514653, + // The type of the resource that generated the event. + "subject_type": "Order", + // The type of event that occurred. + "verb": "confirmed" } ``` @@ -108,24 +108,24 @@ good news - it means we can showcase some complex data generation logic that's built into [synth](https://github.com/getsynth/synth). Since we don't have access to the code that runs the Shopify API, we have to -simulate the behaviour of the Event data model. There are varying degrees of +simulate the behaviour of the Event data model. There are varying degrees of depth into which one can go, and we broke it into 4 levels: 1. Level 1 - **Stub**: Level 1 is just about exposing an endpoint where the data - on a *per element* basis 'looks' right. You have the correct types, but you + on a _per element_ basis 'looks' right. You have the correct types, but you don't really care about correctness across elements. For example, you care that `path` has the correct `subject_id` in the URI, but you don't care that a given Order goes from `placed` to `closed` to `re_opened`etc... 2. Level 2 - **Mock**: Level 2 involves maintaining the semantics of the Events - *collection* as a whole. For example `created_at` should always increase + _collection_ as a whole. For example `created_at` should always increase as `id` increases (a larger `id` means an event was generated at a later date). `verb`s should follow proper causality (as per the order example above). etc. -3. Level 3 - **Emulate**: Level 3 is about maintaining semantics *across - endpoints*. For example creating an order in a different Shopify API endpoint +3. Level 3 - **Emulate**: Level 3 is about maintaining semantics _across + endpoints_. For example creating an order in a different Shopify API endpoint should create an `order_placed` event in the Event API. 4. Level 4 - **Simulate**: Here you are basically reverse engineering all the - business logic of the API. It should be *indistinguishable* from the real + business logic of the API. It should be _indistinguishable_ from the real thing. Really these levels can be seen as increasing in scope as you simulate @@ -163,7 +163,7 @@ fields to return for a given event. Again should be easy enough. We decided not to touch authentication for now as the scope would blow up for a 5-day POC. Interestingly we got a bunch of feedback that mocking OAuth flows or -similar would be *really* useful, regardless of any specific API. We may come +similar would be _really_ useful, regardless of any specific API. We may come back to this at a future date. ## Day 3: Evaluating Implementation Alternatives @@ -174,7 +174,7 @@ faithfully represent the implementation. As any self-respecting engineer would do, we decided to scour the internet for off-the-shelf solutions to automate as much of the grunt work as possible. Some -naive Googling brought up a mock server called +naive Googling brought up a mock server called [JSON server](https://github.com/typicode/json-server) - an API automation solution which spins up a REST API for you given a data definition. Excited by this we quickly wrote up 2 fake Event API events, and @@ -213,11 +213,11 @@ We decided to reproduce the API at level 1-2 since we didn't really have any other endpoints. We used [synth](https://github.com/getsynth/synth) to quickly whip up a [data model](https://github.com/getsynth/model-repository/blob/main/shopify/shopify/events.json.json) -that generates data that looks like responses from the Event API. I won't go +that generates data that looks like responses from the Event API. I won't go into depth on how this works here as it's been covered -in [other posts](2021-08-31-seeding-databases-tutorial.md). In about 15 -minutes of tweaking the `synth` schema, we generated ~10 Mb data that looks +in [other posts](2021-08-31-seeding-databases-tutorial.md). In about 15 +minutes of tweaking the `synth` schema, we generated ~10 Mb data that looks like this: ```json @@ -290,7 +290,7 @@ source [here](https://github.com/getsynth/model-repository/blob/main/shopify/src The data is ready, the API is ready, time to package this thing up and give it to people to actually use. Let's see if our experiment was a success. -While strategising about distributing our API, we were optimising for two +While strategising about distributing our API, we were optimising for two things: 1. Ease of use - how simple it is for someone to download this thing and get @@ -303,7 +303,7 @@ We needed to pack the data, database and a node runtime to actually run the server. Our initial idea was to use `docker-compose` with 2 services, the database and web-server, and then the network plumbing to get it to work. After discussing this for a few minutes, we decided that `docker-compose` may be an -off-ramp for some users as they don't have it installed or are not familiar +off-ramp for some users as they don't have it installed or are not familiar with how it works. This went against our first tenet which is 'ease of use'. So we decided to take the slightly harder and hackier route of packaging the @@ -328,17 +328,17 @@ agnostic one liner. ## Was it a success? An important aspect of this experiment was to see if we could conceive, -research, design and implement a PoC in a week (as a side project, we were -working on `synth` at the same time). I can safely say this was a -success! We got it done to spec. +research, design and implement a PoC in a week (as a side project, we were +working on `synth` at the same time). I can safely say this was a +success! We got it done to spec. -An interesting thing to note is that **60%** of the time was spent on +An interesting thing to note is that **60%** of the time was spent on ideating, researching and planning - and only 40% of the time on the actual -implementation. However, spending all that time planning before writing code -definitely saved us a bunch of time, and if we didn't plan so much the project would have +implementation. However, spending all that time planning before writing code +definitely saved us a bunch of time, and if we didn't plan so much the project would have overshot or failed. -Now if the PoC itself was a success is a different question. This is where *you* +Now if the PoC itself was a success is a different question. This is where _you_ come in. If you're using the Event API, build the image and play around with it. You can get started by quickly cloning @@ -357,5 +357,5 @@ curl "localhost:3000/admin/api/2021-07/events.json" We'd like to keep iterating on the Shopify API and improve it. If there is interest we'll add more endpoints and improve the existing Event data model. -If you'd like to contribute, or are interested mocks for other APIs other than +If you'd like to contribute, or are interested mocks for other APIs other than Shopify, feel free to open an issue on GitHub! diff --git a/docs/blog/2021-09-27-crash.md b/docs/blog/2021-09-27-crash.md index 4c4cf2c6..13b413b8 100644 --- a/docs/blog/2021-09-27-crash.md +++ b/docs/blog/2021-09-27-crash.md @@ -1,6 +1,6 @@ --- title: 50 ways to crash our product -author: Andre Bogus +author: Andre Bogus author_title: Chief Rustacean author_url: https://github.com/getsynth author_image_url: https://avatars.githubusercontent.com/u/4200835?v=4 @@ -10,7 +10,6 @@ image: https://storage.googleapis.com/getsynth-public/media/crash.svg hide_table_of_contents: false --- - ![50 ways to crash our product](https://storage.googleapis.com/getsynth-public/media/crash.svg) I personally think that the software we build should make more people's lives better than it makes worse. So when users recently started filing bug reports, I read them with mixed feelings. On one hand, it meant that those particular users were actually using [synth](https://getsynth.com), on the other hand, it also meant that we were failing to give them a polished experience. So when it was my turn to write more stuff about what we do here, I set myself a challenge: Find as many ways as I can to break our product. @@ -30,11 +29,11 @@ My plan was to look at each of the components and see if I can find inputs to br ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object" - } + "type": "array", + "length": 1, + "content": { + "type": "object" + } } ``` @@ -44,17 +43,17 @@ I then called out `synth generate` until finding a problem. First, I attempted t ```json synth { - "type": "array", - "length": -1, - "content": { - "type": "object" - } + "type": "array", + "length": -1, + "content": { + "type": "object" + } } ``` Which was met with `BadRequest: could not convert from value 'i64(-1)': Type { expected: "U32", got: "i64(-1)" }`. Not exactly a crash, but the error message could be friendlier and have more context. I should note that this is a very unspecialized error variant within the generator framework. It would make sense to validate this before compiling the generator and emit a more user-friendly error. -*Bonus*: If we make the length `"optional": true` (which could happen because of a copy & paste error), depending on the seed, we will get another `BadRequest` error. The evil thing is that this will only happen with about half of the seeds, so you may or may not be lucky here (or may even become unlucky if another version would slightly change the seed handling). +_Bonus_: If we make the length `"optional": true` (which could happen because of a copy & paste error), depending on the seed, we will get another `BadRequest` error. The evil thing is that this will only happen with about half of the seeds, so you may or may not be lucky here (or may even become unlucky if another version would slightly change the seed handling). \#2 Changing the `length` field to `{}` makes for another befuddling error: @@ -73,14 +72,14 @@ The line number is wrong here, the length should be in line six in the `content` ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u32" - }, - "content": { - "type": "object" - } + "type": "array", + "length": { + "type": "number", + "subtype": "u32" + }, + "content": { + "type": "object" + } } ``` @@ -90,17 +89,17 @@ This might be done very quickly, but far more likely it will work for a long tim ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u32" - }, - "content": { - "type": "object", - "s": { - "type": "string" - } + "type": "array", + "length": { + "type": "number", + "subtype": "u32" + }, + "content": { + "type": "object", + "s": { + "type": "string" } + } } ``` @@ -119,21 +118,21 @@ Caused by: ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u32" - }, - "content": { - "type": "object", - "s": { - "type": "string", - "format": "say my {name}", - "arguments": { - "name": "name" - } - } + "type": "array", + "length": { + "type": "number", + "subtype": "u32" + }, + "content": { + "type": "object", + "s": { + "type": "string", + "format": "say my {name}", + "arguments": { + "name": "name" + } } + } } ``` @@ -150,18 +149,18 @@ Caused by: ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u32" - }, - "content": { - "type": "object", - "name": { - "type": "string", - "faker": "name" - } + "type": "array", + "length": { + "type": "number", + "subtype": "u32" + }, + "content": { + "type": "object", + "name": { + "type": "string", + "faker": "name" } + } } ``` @@ -178,7 +177,7 @@ Caused by: One could say that the error is not exactly misleading, but not exactly helpful either. As I've tried a number of things already, I'll take it. Once I get the syntax right (`"faker": { "generator": "name" }`, the rest of the faker stuff seems to be rock solid. -\#7 Trying to mess up with `date_time`, I mistakenly specify a date format for a `naive_time` value. +\#7 Trying to mess up with `date_time`, I mistakenly specify a date format for a `naive_time` value. ```json synth { @@ -211,7 +210,7 @@ Caused by: 2: input is not enough for unique date and time at line 16 column 1 ``` -I believe since the time is not constrained in any way by the input, we should just issue a warning and generate an unconstrained time instead, so the user will at least get *some* data. Interestingly, seconds seem to be optional, so `%H:%M` works. +I believe since the time is not constrained in any way by the input, we should just issue a warning and generate an unconstrained time instead, so the user will at least get _some_ data. Interestingly, seconds seem to be optional, so `%H:%M` works. \#8 More, if I use `naive_date` instead, but make the minimum `0-0-0`, we get the technically correct but still mis-spanned: @@ -230,17 +229,17 @@ For the record, the error is on line 11. ```json { - "type": "array", - "length": 1, - "content": { - "type": "object", - "cat": { - "type": "string", - "categorical": { - "empty": 0 - } - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "cat": { + "type": "string", + "categorical": { + "empty": 0 + } } + } } ``` @@ -281,18 +280,18 @@ Since the error is mostly the same for all types, and was somewhat expected, I w ```json synth { - "type": "array", - "length": 5, - "content": { - "type": "object", - "s": { - "type": "array", - "length": 1, - "content": { - "type": "null" - } - } + "type": "array", + "length": 5, + "content": { + "type": "object", + "s": { + "type": "array", + "length": 1, + "content": { + "type": "null" + } } + } } ``` @@ -325,4 +324,4 @@ Caused by: So I give up. I've found 1 way to crash our product with an unintended error, reproduced some known limitations and outlined a number of error messages we can improve on. I fell far short of my original goal, which either means I'm really bad at finding errors, or our code is incredibly reliable. Given the track record of software written in Rust, I'd like to think it's the latter, but I'll leave judgement to you. -Anyway, this was a fun exercise and I looked at many more things that turned out to just work well, so that's a good thing™. With all the test automation we have today, it's easy to forget that the manual approach also has its upsides. So feel free and try to break your (or our) code! \ No newline at end of file +Anyway, this was a fun exercise and I looked at many more things that turned out to just work well, so that's a good thing™. With all the test automation we have today, it's easy to forget that the manual approach also has its upsides. So feel free and try to break your (or our) code! diff --git a/docs/blog/2021-10-07-building-a-startup-with-rust.md b/docs/blog/2021-10-07-building-a-startup-with-rust.md index 36b60d81..b27d0a01 100644 --- a/docs/blog/2021-10-07-building-a-startup-with-rust.md +++ b/docs/blog/2021-10-07-building-a-startup-with-rust.md @@ -47,8 +47,8 @@ these (at times cynical) thoughts. ## Development Velocity -Rust has a *really* steep learning curve. Coming from an OO background it took -me *months* to become productive in Rust. This was incredibly frustrating for me +Rust has a _really_ steep learning curve. Coming from an OO background it took +me _months_ to become productive in Rust. This was incredibly frustrating for me as I felt that my lack of productivity was impacting the team, which it was. Even when you eventually do become productive (and you will), Rust forces you to really think deeply about what you're doing and things inevitably take longer to @@ -57,7 +57,7 @@ haunt you months later. What should be a simple change or refactor can end up resulting in complete tear down as you try to appease the borrow checker. This is deadly. -The entire premise of a startup is that *you have to iterate rapidly*. Very few +The entire premise of a startup is that _you have to iterate rapidly_. Very few companies know what they should be building from day one. It's an iterative process involving a feedback loop of talking to users and making changes to reflect the feedback. The faster you can make that feedback loop, the higher @@ -99,16 +99,16 @@ than more conventional programming languages. Something spoke to these individuals when they picked up Rust, and it's hard to put your finger on it but it's that same quality that makes a great engineer. It's also been a pleasant surprise to find out that really good engineers will seek you out as an -employer *because you use Rust*. They don't want to work in *script or Java or +employer _because you use Rust_. They don't want to work in \*script or Java or C++. They want to work with Rust because it's great. ## Open Source At Synth, we've chosen to adopt an open-core business model. The idea behind an open-core business is you develop and open source product with a permissive -license which solves a real *technical* problem. You work on building a user +license which solves a real _technical_ problem. You work on building a user base, a community and a great product all out in the open. You then structure -your business model around solving the corresponding *organisational* problem - +your business model around solving the corresponding _organisational_ problem - and that's how you make money. We've been really lucky to have a really active set of contributors - giving @@ -132,23 +132,23 @@ lack of canonical libraries for doing things outside the standard library. So you want a webserver, pick from one of the 100s available. You want a crate ( Rust lingo for library) for working with JWT tokens? Here's 9, pick one. I mean, even something as fundamental as an asynchronous runtime is split -between `tokio` and `async-std` and others. As a young rustacean this can +between `tokio` and `async-std` and others. As a young rustacean this can be overwhelming. What ends up happening over time is certain libraries become implicitly canonical as they receive overwhelming support and start becoming serious -dependencies differentiating from their alternatives. Also in a project -update from RustConf 2021 it [was mentioned](https://youtu.be/ylOpCXI2EMM?t=1048) that the idea of having 'recommended crates' may be visited in the -future. +dependencies differentiating from their alternatives. Also in a project +update from RustConf 2021 it [was mentioned](https://youtu.be/ylOpCXI2EMM?t=1048) that the idea of having 'recommended crates' may be visited in the +future. The lack of canonical non-standard libraries is an issue when you're getting started - but over time this diminishes as you get a better understanding of the -ecosystem. What *has* been constantly detrimental to our development velocity -has been the lack of *client* libraries for Rust. We've had to write a -bunch of different integrations ourselves, but they're often clunky as we -don't have the time to invest in making them really high quality. For -example most of Google's products have at best an unofficial code-generated -crate maintained by the community, and at worst absolutely nothing. You +ecosystem. What _has_ been constantly detrimental to our development velocity +has been the lack of _client_ libraries for Rust. We've had to write a +bunch of different integrations ourselves, but they're often clunky as we +don't have the time to invest in making them really high quality. For +example most of Google's products have at best an unofficial code-generated +crate maintained by the community, and at worst absolutely nothing. You need to write it from scratch. ## Should you build your startup with Rust? @@ -161,6 +161,6 @@ make rapid iterations is so important for an early stage startup that it outweighs a lot of the benefits that Rust brings to the table. If your company is later stage, and you now understand exactly what you should -be building (assuming this is every the case) then I would say yes. The +be building (assuming this is every the case) then I would say yes. The 'correctness' of Rust programs and the propensity of Rust to attract great -engineers can help in building a great engineering culture and a great company. +engineers can help in building a great engineering culture and a great company. diff --git a/docs/blog/2021-10-11-nightly.md b/docs/blog/2021-10-11-nightly.md index 0152f6eb..6b936552 100644 --- a/docs/blog/2021-10-11-nightly.md +++ b/docs/blog/2021-10-11-nightly.md @@ -6,16 +6,16 @@ author_url: https://github.com/getsynth author_image_url: https://avatars.githubusercontent.com/u/4200835?v=4 tags: [synth, rust, stability, story] description: How we (mis?)used Rust nightly to create synth and what to learn from it -image: https://storage.googleapis.com/getsynth-public/media/rust-negative.jpeg +image: https://storage.googleapis.com/getsynth-public/media/rust-negative.jpeg hide_table_of_contents: false --- ![Rust Nightly](media/rust-negative.jpeg) -When my colleague Christos [wrote about using Rust for our startup](/docs/blog/2021/10/07/building-a-startup-with-rust), he made no mention of the +When my colleague Christos [wrote about using Rust for our startup](/docs/blog/2021/10/07/building-a-startup-with-rust), he made no mention of the fact that we actually use a nightly Rust compiler, and our code incorporates a set of nightly features. So I thought it might be beneficial to look at the cost-benefit ratio of using Rust nightly, for us, for the open source ecosystem, and for Rust. -*Disclaimer: I wasn't the one who introduced nightly Rust features into this particular codebase, but I have a good few years of experience with nightly Rust, working on [clippy](https://github.com/rust-lang/rust-clippy) since 2015* +_Disclaimer: I wasn't the one who introduced nightly Rust features into this particular codebase, but I have a good few years of experience with nightly Rust, working on [clippy](https://github.com/rust-lang/rust-clippy) since 2015_ ## The Good @@ -51,13 +51,13 @@ I think this case shows us two things: Regarding the pinned nightly and also the only incompatibility I ever encountered, I removed that pin when the May 2021 nightly version we had used stopped working with an updated [`proc_macro2`](https://docs.rs/proc_macro2) crate. It was later re-established with a newer version. We have this version in all our CI jobs and also in our `rust-toolchain` file. There also were a few troubles when we had CI and the main toolchain inadvertently go out of sync, but those were fixed up quickly. -For a version that hails as "unstable", Rust nightly is actually surprisingly solid. That has two reasons: 1. Every PR that gets merged has been extensively tested by CI, so obvious errors get caught out before the code even hits nightly. 2. Whenever a change looks risky, the infrastructure team supplies a "crater run". Crater is a tool that will try to compile *every crate* on crates.io with a given version of a compiler and compares if things fail to compile now. Since crates.io has 68798 crates in stock at the time of this writing, there's a pretty good chance that whatever weird thing you might encounter in live code is thrown at the compiler. I have done such a rustc change once, and it was very reassuring to know that my PR didn't break any code out there. +For a version that hails as "unstable", Rust nightly is actually surprisingly solid. That has two reasons: 1. Every PR that gets merged has been extensively tested by CI, so obvious errors get caught out before the code even hits nightly. 2. Whenever a change looks risky, the infrastructure team supplies a "crater run". Crater is a tool that will try to compile _every crate_ on crates.io with a given version of a compiler and compares if things fail to compile now. Since crates.io has 68798 crates in stock at the time of this writing, there's a pretty good chance that whatever weird thing you might encounter in live code is thrown at the compiler. I have done such a rustc change once, and it was very reassuring to know that my PR didn't break any code out there. ## The Conclusion -If you want to compile your code with the current fastest version, you can use nightly now. As long as you don't use any features, your code *should* still compile on stable (however, I would still use a stable compiler in CI to check, because some changes may become insta-stable, e.g. added trait implementations; Rust cannot put those behind a feature gate). There is a very small risk of breakage, but you can revert to a beta or stable compiler with no hassle if that happens. +If you want to compile your code with the current fastest version, you can use nightly now. As long as you don't use any features, your code _should_ still compile on stable (however, I would still use a stable compiler in CI to check, because some changes may become insta-stable, e.g. added trait implementations; Rust cannot put those behind a feature gate). There is a very small risk of breakage, but you can revert to a beta or stable compiler with no hassle if that happens. -Using nightly *features* is in a way like every other form of technical debt. A bit of risk taking that can give you some potentially big payoff now, at the price of possible future breakage. Whether you want to take that risk depends a lot on your project and the phase it lives in. If you're a startup desperate to get your project out there, not using that feature may mean that there won't be a project to fix later otherwise. On the other hand, if you are writing code that should live for a while, or a library that is aimed to be widely used, avoiding nightly features is likely your best bet. +Using nightly _features_ is in a way like every other form of technical debt. A bit of risk taking that can give you some potentially big payoff now, at the price of possible future breakage. Whether you want to take that risk depends a lot on your project and the phase it lives in. If you're a startup desperate to get your project out there, not using that feature may mean that there won't be a project to fix later otherwise. On the other hand, if you are writing code that should live for a while, or a library that is aimed to be widely used, avoiding nightly features is likely your best bet. If you are on nightly, you have two options: Go all in and embrace the instability or pin a known good version. I now think that pinning is the only sane option for all but hobby projects (where a bit of breakage can be acceptable every now and then). I note that clippy has a special place here, because it's essentially tied to the current rust version by design, and we get away with syncing every two weeks and staying on master (not even nightly) otherwise. Once you decide on a pinned version, you may as well pin all your dependencies and update them very cautiously, because any update could break your build. Even then it may be a good idea to test with a current nightly every now and then to gauge whether any incompatibility will hit you whenever you should decide to update. diff --git a/docs/docs/content/array.md b/docs/docs/content/array.md index 9387d2d1..e4b839ed 100644 --- a/docs/docs/content/array.md +++ b/docs/docs/content/array.md @@ -23,8 +23,8 @@ The example below generates arrays of credit card numbers with `3` to `10` eleme "content": { "type": "string", "faker": { - "generator": "credit_card" + "generator": "credit_card" } } } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/bool.md b/docs/docs/content/bool.md index ffa1aaba..d0338957 100644 --- a/docs/docs/content/bool.md +++ b/docs/docs/content/bool.md @@ -22,8 +22,7 @@ generated value being `true`. ```json synth { - "type": "bool", - "frequency": 0.5 + "type": "bool", + "frequency": 0.5 } ``` - diff --git a/docs/docs/content/datasource.md b/docs/docs/content/datasource.md index 7478ccac..e13d4a9c 100644 --- a/docs/docs/content/datasource.md +++ b/docs/docs/content/datasource.md @@ -1,6 +1,7 @@ --- title: datasource --- + Synth's `datasource` generator is used to pull data from an external source. The data can be simple values like a string, number, or booleans as well as complex values like an array or object. @@ -12,15 +13,12 @@ The `cycle` is optional and defaults to `false`. It allows you to read a datasou exhausted when set to `true`. ### JSON + When pulling from a JSON file, the JSON is expected to be an array with every item being the value for a single Synth generator. The following is a valid JSON datasource: ```json -[ - "21 Mary Street", - "5 Diascia Avenue", - "1062 Hill Crescent" -] +["21 Mary Street", "5 Diascia Avenue", "1062 Hill Crescent"] ``` When generating more than 3 items `cycle` will need to be `true` for this datasource. diff --git a/docs/docs/content/date-time.md b/docs/docs/content/date-time.md index 09660799..ab077de1 100644 --- a/docs/docs/content/date-time.md +++ b/docs/docs/content/date-time.md @@ -23,11 +23,11 @@ Accepted keys and values that can be contained in a `date_time` generator are as - `"format"`: a [strftime](https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html)-style parameter specifying the string formatting of the underlying `date_time` value. - `"subtype"`: one of the following - * `"naive_date"`: indicates the `date_time` value should be a simple `date` without timezone specification, - * `"naive_time"`: indicates the `date_time` value should be a simple `time` without timezone specification, - * `"naive_date_time"`: indicates the `date_time` value should be a combined `date` and `time` without timezone - specification, - * `"date_time"`: indicates the `date_time` value should be a combined `date` and `time` *with* timezone specification. + - `"naive_date"`: indicates the `date_time` value should be a simple `date` without timezone specification, + - `"naive_time"`: indicates the `date_time` value should be a simple `time` without timezone specification, + - `"naive_date_time"`: indicates the `date_time` value should be a combined `date` and `time` without timezone + specification, + - `"date_time"`: indicates the `date_time` value should be a combined `date` and `time` _with_ timezone specification. - `"begin"` and `"end"`: the lower and upper bounds of the `date_time` value to generate. The formatting of these values must adhere to the `strftime`-string specified in the `"format"` field. @@ -63,4 +63,3 @@ Or optionally both, will result in a constant time: "end": "2020-01-01T12:00:00" } ``` - diff --git a/docs/docs/content/index.md b/docs/docs/content/index.md index 08ea6614..4f80d8fc 100644 --- a/docs/docs/content/index.md +++ b/docs/docs/content/index.md @@ -6,48 +6,48 @@ Synth has the following types of generators: ## Generators -* [null](null) generates as many nulls as you ever want -* [bool](bool) generates `true` or `false`, either constant or -following a given percentage -* [number](number) generates ranges, distributions or series of -[integer or floating-point](number#subtype) numbers -* [series](series) generates streams of events (e.g. for logs) - * [incrementing](series#incrementing) emits evenly spaced events - * [poisson](series#poisson) models a random poisson process - * [cyclical](series#cyclical) models periodic events - * [zip](series#zip) combines multiple series together -* [string](string) can contain one of the following generators for -various classes of string: - * [pattern](string#pattern) takes a regular expression and - generates matching strings - * [uuid](string#uuid) generates hyphenated UUIDs - optionally with time zone - * [faker](string#faker) has a large number of generators for names, - contact information, credit card numbers, sentences, and much more - * [format](string#format) combines multiple generators to one - formatted string - * [serialized](string#serialized) JSONifies the value of the - contained generator - * [truncated](string#truncated) ensures all generated strings stay - within length limits - * [categorical](string#categorical) is like a - [one_of](one-of) specialized for strings -* [date_time](date-time) generates dates and times -* [object](object) creates an object with string keys containing -generators for the values -* [array](array) fills an array of the given length with elements of -the contained generator -* [datasource](datasource) pulls data from an external source -like a file +- [null](null) generates as many nulls as you ever want +- [bool](bool) generates `true` or `false`, either constant or + following a given percentage +- [number](number) generates ranges, distributions or series of + [integer or floating-point](number#subtype) numbers +- [series](series) generates streams of events (e.g. for logs) + - [incrementing](series#incrementing) emits evenly spaced events + - [poisson](series#poisson) models a random poisson process + - [cyclical](series#cyclical) models periodic events + - [zip](series#zip) combines multiple series together +- [string](string) can contain one of the following generators for + various classes of string: + - [pattern](string#pattern) takes a regular expression and + generates matching strings + - [uuid](string#uuid) generates hyphenated UUIDs + optionally with time zone + - [faker](string#faker) has a large number of generators for names, + contact information, credit card numbers, sentences, and much more + - [format](string#format) combines multiple generators to one + formatted string + - [serialized](string#serialized) JSONifies the value of the + contained generator + - [truncated](string#truncated) ensures all generated strings stay + within length limits + - [categorical](string#categorical) is like a + [one_of](one-of) specialized for strings +- [date_time](date-time) generates dates and times +- [object](object) creates an object with string keys containing + generators for the values +- [array](array) fills an array of the given length with elements of + the contained generator +- [datasource](datasource) pulls data from an external source + like a file ## Modifiers [Modifiers](modifiers) encode additional constraints or variations of the generator(s) they apply to. -* [optional](modifiers#optional) makes a value nullable -* [unique](modifiers#unique) ensures the generated values don't contain +- [optional](modifiers#optional) makes a value nullable +- [unique](modifiers#unique) ensures the generated values don't contain duplicates -* [one_of](one-of) allows you to choose from a set of contained +- [one_of](one-of) allows you to choose from a set of contained generators -* [same_as](same-as) creates a reference to another field in this or - another collection \ No newline at end of file +- [same_as](same-as) creates a reference to another field in this or + another collection diff --git a/docs/docs/content/modifiers.md b/docs/docs/content/modifiers.md index 298406de..bfba4931 100644 --- a/docs/docs/content/modifiers.md +++ b/docs/docs/content/modifiers.md @@ -6,7 +6,7 @@ Modifiers are attributes that can be added to any [generator](index) to modify t ## `optional` -The `optional` modifier makes a generator nullable. It accepts a single boolean value (true or false). +The `optional` modifier makes a generator nullable. It accepts a single boolean value (true or false). ```json synth { @@ -18,7 +18,7 @@ The `optional` modifier makes a generator nullable. It accepts a single boolean ## `unique` -The `unique` modifiers ensures a generator only outputs non-repeating values. It accepts a single boolean value (true or false). +The `unique` modifiers ensures a generator only outputs non-repeating values. It accepts a single boolean value (true or false). ```json synth { @@ -35,4 +35,4 @@ The `unique` modifiers ensures a generator only outputs non-repeating values. It } } } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/null.md b/docs/docs/content/null.md index 9fa46347..cbeb297a 100644 --- a/docs/docs/content/null.md +++ b/docs/docs/content/null.md @@ -4,6 +4,6 @@ The `null` generator type simply evaluates to the JSON [`null`](https://www.json ```json synth { - "type": "null" + "type": "null" } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/number.md b/docs/docs/content/number.md index fc702955..2d800706 100644 --- a/docs/docs/content/number.md +++ b/docs/docs/content/number.md @@ -1,8 +1,9 @@ -Synth's `number` type allows for generating fixed-width numbers. +Synth's `number` type allows for generating fixed-width numbers. -### Parameters +### Parameters #### `subtype` + All the variants of `number` accept an optional `"subtype"` field to specify the width and primitive kind of the values generated. The value of `"subtype"`, if specified, must be one of `u64`, `i64`, `f64`, `u32`, `i32`, `f32`. @@ -32,6 +33,7 @@ will have different default behavior based on the value of `"subtype"`. - For float subtypes (`f32`, `f64`): `number` will default to generating from the semi-open interval `[0, 1)`. #### Example + ```json synth { "type": "number", @@ -40,6 +42,7 @@ will have different default behavior based on the value of `"subtype"`. ``` #### Example + ```json synth { "type": "number", @@ -70,9 +73,9 @@ This generates one of the integers `0, 3, 6, 9`. { "type": "number", "range": { - "low": 0, - "high": 10, - "step": 3 + "low": 0, + "high": 10, + "step": 3 } } ``` @@ -85,8 +88,8 @@ This generates one integer between `0` (included) and `122` (included). { "type": "number", "range": { - "high": 122, // the age of the oldest recorded person - "include_high": true + "high": 122, // the age of the oldest recorded person + "include_high": true } } ``` @@ -100,9 +103,9 @@ and `15000000.0` (excluded) with an approximate alignment to the second decimal. { "type": "number", "range": { - "high": 15000000.0, // temperature at sun's core in Celcius - "low": -273.15, // 0 Kelvin - "step": 0.01 + "high": 15000000.0, // temperature at sun's core in Celcius + "low": -273.15, // 0 Kelvin + "step": 0.01 } } ``` @@ -146,7 +149,7 @@ A constant number type. This will always evaluate to the same number. ```json synth { "type": "number", - "constant": 3.14159 // pi + "constant": 3.14159 // pi } ``` @@ -198,4 +201,4 @@ Synth currently supports `u64` ids. } } } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/object.md b/docs/docs/content/object.md index 7f7bf708..18900f44 100644 --- a/docs/docs/content/object.md +++ b/docs/docs/content/object.md @@ -33,6 +33,7 @@ has value a [`number`](number) type and `"name"` has value a [`string`](string) Values of objects can be made nullable by specifying the `"optional": true` attribute. #### Example + ```json synth { "type": "object", @@ -49,6 +50,7 @@ Values of objects can be made nullable by specifying the `"optional": true` attr By default, optional values that are generated as `null` will produce a key-value pair of the form `key: null`. This behavior can be controlled by specifying the `skip_when_null: true` attribute on the object generator. #### Example + ```json synth { "type": "object", diff --git a/docs/docs/content/one-of.md b/docs/docs/content/one-of.md index b81dd087..be8e0bca 100644 --- a/docs/docs/content/one-of.md +++ b/docs/docs/content/one-of.md @@ -1,8 +1,9 @@ --- title: one_of --- + Synth's `one_of` generator is a compound operator, i.e. a way to compose other generator types together. It lets you -define a new generator that samples randomly from a specified list of dependent generators (called *variants*). In that +define a new generator that samples randomly from a specified list of dependent generators (called _variants_). In that way, `one_of` is similar to [categorical `string`s](string#categorical). However, the variants of a `one_of` generator are allowed to be generated from any other Synth generator. @@ -12,14 +13,17 @@ generators. ```json synth { "type": "one_of", - "variants": [ { - "weight": 0.5, - "type": "string", - "pattern": "M|F" - }, { - "weight": 0.5, - "type": "null" - } ] + "variants": [ + { + "weight": 0.5, + "type": "string", + "pattern": "M|F" + }, + { + "weight": 0.5, + "type": "null" + } + ] } ``` @@ -32,15 +36,17 @@ definition. ```json synth { "type": "one_of", - "variants": [ { - "weight": 9.5, - "type": "string", - "faker": { - "generator": "address" - } - }, { - "weight" : 0.5, - "type": "object", + "variants": [ + { + "weight": 9.5, + "type": "string", + "faker": { + "generator": "address" + } + }, + { + "weight": 0.5, + "type": "object", "postcode": { "type": "string", "faker": { @@ -56,6 +62,7 @@ definition. "step": 2 } } - } ] + } + ] } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/same-as.md b/docs/docs/content/same-as.md index 2c230706..af185965 100644 --- a/docs/docs/content/same-as.md +++ b/docs/docs/content/same-as.md @@ -76,4 +76,4 @@ The `same_as` generator can also be simply declared by the value of the `"ref"` }, "same_zip_code": "@address.zip_code" } -``` \ No newline at end of file +``` diff --git a/docs/docs/content/series.md b/docs/docs/content/series.md index 71ba15db..65c09f82 100644 --- a/docs/docs/content/series.md +++ b/docs/docs/content/series.md @@ -1,11 +1,12 @@ Synth's `series` generator creates streams of events based on different 'processes' (a process here can be an auto-correlated process, a poisson process, a cyclical process etc.). The `series` generators are used in several different contexts: - - Creating fake events for event-driven systems - - Modelling time-independent events like 'orders' on a website or 'requests' made to a web server - - Modelling seasonal behaviour, like an increase in flight frequency for a given airline over the summer -#### Date Time +- Creating fake events for event-driven systems +- Modelling time-independent events like 'orders' on a website or 'requests' made to a web server +- Modelling seasonal behaviour, like an increase in flight frequency for a given airline over the summer + +#### Date Time All `series` are modelled on so called 'Naive Date Times' - that is 'Date Times' that do not have a timezone. This can be interpreted as Timestamps in UTC. There is future work to improve functionality to add other chrono types. @@ -22,42 +23,45 @@ The `series` generators use [`humantime`](https://docs.rs/humantime/2.1.0/humant The `incrementing` series simply increments at a fixed duration. This could be for example a stock ticker. The `incrementing` series has 2 parameters: + - `start`: The time at which the first event occurs - `increment`: The increment between two consecutive events #### Example -Below is an example stock ticker for AAPL sampled at regular intervals every minute. + +Below is an example stock ticker for AAPL sampled at regular intervals every minute. + ```json synth { - "type": "array", - "length": { - "type": "number", - "constant": 10 + "type": "array", + "length": { + "type": "number", + "constant": 10 + }, + "content": { + "type": "object", + "ticker": { + "type": "string", + "pattern": "AAPL" }, - "content": { - "type": "object", - "ticker": { - "type": "string", - "pattern": "AAPL" - }, - "timestamp": { - "type": "series", - "format" : "%Y-%m-%d %H:%M:%S", - "incrementing": { - "start" : "2021-02-01 09:00:00", - "increment" : "1m" - } - }, - "price": { - "type": "number", - "subtype" : "f64", - "range" : { - "high": 105, - "low": 100, - "step": 0.01 - } - } + "timestamp": { + "type": "series", + "format": "%Y-%m-%d %H:%M:%S", + "incrementing": { + "start": "2021-02-01 09:00:00", + "increment": "1m" + } + }, + "price": { + "type": "number", + "subtype": "f64", + "range": { + "high": 105, + "low": 100, + "step": 0.01 + } } + } } ``` @@ -68,109 +72,114 @@ The `poisson` series models independent events which occur at random, but which One example of a poisson process could be earthquakes occurring during the course of a year, or customers arriving at a store, or cars crossing a bridge etc. The `poisson` series has 2 parameters: - - `start`: The time at which the first event occurs - - `rate`: The average duration between two consecutive events + +- `start`: The time at which the first event occurs +- `rate`: The average duration between two consecutive events #### Example + The below is an example HTTP server, which was brought up on a given date and has an average of 1 request every 1 minute. + ```json synth { - "type": "array", - "length": { - "type": "number", - "constant": 10 + "type": "array", + "length": { + "type": "number", + "constant": 10 + }, + "content": { + "type": "object", + "ip": { + "type": "string", + "faker": { + "generator": "ipv4" + } }, - "content": { - "type": "object", - "ip": { - "type": "string", - "faker": { - "generator": "ipv4" - } - }, - "timestamp": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "1m" - } - }, - "request": { - "type": "string", - "categorical": { - "GET /index.html HTTP/1.0": 10, - "GET /home.html HTTP/1.0": 5, - "GET /login.html HTTP/1.0": 3 - } - }, - "response_code": { - "type": "number", - "subtype": "u64", - "categorical": { - "200": 95, - "500": 5 - } - }, - "response_size": { - "type": "number", - "range": { - "low": 500, - "high": 3000, - "step": 1 - } - } + "timestamp": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "1m" + } + }, + "request": { + "type": "string", + "categorical": { + "GET /index.html HTTP/1.0": 10, + "GET /home.html HTTP/1.0": 5, + "GET /login.html HTTP/1.0": 3 + } + }, + "response_code": { + "type": "number", + "subtype": "u64", + "categorical": { + "200": 95, + "500": 5 + } + }, + "response_size": { + "type": "number", + "range": { + "low": 500, + "high": 3000, + "step": 1 + } } + } } ``` ## cyclical -The `cyclical` series models events which have a 'cyclical' or 'periodic' frequency. +The `cyclical` series models events which have a 'cyclical' or 'periodic' frequency. For example, the frequency of orders placed in an online store peaks during the day and is at it's lowest during the night. The `cyclical` series has 4 parameters: + - `start`: The time at which the first event occurs - `max_rate`: The maximum average duration between two events. - `min_rate`: The minimum average duration between two events - `period`: The period of the cyclical series. #### Example + The below is a minimal example of orders being placed in an online store. ```json synth { - "type": "array", - "length": { - "type": "number", - "constant": 10 + "type": "array", + "length": { + "type": "number", + "constant": 10 + }, + "content": { + "type": "object", + "order_id": { + "type": "number", + "id": {} }, - "content": { - "type": "object", - "order_id": { - "type": "number", - "id": {} - }, - "item": { - "type": "string", - "categorical": { - "t-shirt": 4, - "jeans": 1, - "jacket": 1, - "belt": 2 - } - }, - "timestamp": { - "type": "series", - "cyclical": { - "start": "2021-02-01 00:00:00", - "period": "1d", - "min_rate": "10m", - "max_rate": "30s" - } - } + "item": { + "type": "string", + "categorical": { + "t-shirt": 4, + "jeans": 1, + "jacket": 1, + "belt": 2 + } + }, + "timestamp": { + "type": "series", + "cyclical": { + "start": "2021-02-01 00:00:00", + "period": "1d", + "min_rate": "10m", + "max_rate": "30s" + } } + } } ``` @@ -179,53 +188,54 @@ The below is a minimal example of orders being placed in an online store. The `zip` series combines 2 or more series together by `zipping` the output together. That is, the two series are super imposed. The `zip` series has 1 parameter: + - `series`: The child series to be zipped together ```json synth { - "type": "array", - "length": { - "type": "number", - "constant": 10 + "type": "array", + "length": { + "type": "number", + "constant": 10 + }, + "content": { + "type": "object", + "order_id": { + "type": "number", + "id": {} }, - "content": { - "type": "object", - "order_id": { - "type": "number", - "id": {} - }, - "item": { - "type": "string", - "categorical": { - "t-shirt": 4, - "jeans": 1, - "jacket": 1, - "belt": 2 + "item": { + "type": "string", + "categorical": { + "t-shirt": 4, + "jeans": 1, + "jacket": 1, + "belt": 2 + } + }, + "timestamp": { + "type": "series", + "zip": { + "series": [ + { + "cyclical": { + "start": "2021-02-01 00:00:00", + "period": "1w", + "min_rate": "1m", + "max_rate": "1s" } - }, - "timestamp": { - "type": "series", - "zip": { - "series": [ - { - "cyclical": { - "start": "2021-02-01 00:00:00", - "period": "1w", - "min_rate": "1m", - "max_rate": "1s" - } - }, - { - "cyclical": { - "start": "2021-02-01 00:00:00", - "period": "1d", - "min_rate": "10m", - "max_rate": "30s" - } - } - ] + }, + { + "cyclical": { + "start": "2021-02-01 00:00:00", + "period": "1d", + "min_rate": "10m", + "max_rate": "30s" } - } + } + ] + } } + } } ``` diff --git a/docs/docs/content/string.md b/docs/docs/content/string.md index 4878efa9..17d1dfd6 100644 --- a/docs/docs/content/string.md +++ b/docs/docs/content/string.md @@ -80,661 +80,606 @@ the `generator` key. #### first_name - ```json synth { - "type": "string", - "faker": { - "generator": "first_name" - } + "type": "string", + "faker": { + "generator": "first_name" + } } ``` #### last_name - ```json synth { - "type": "string", - "faker": { - "generator": "last_name" - } + "type": "string", + "faker": { + "generator": "last_name" + } } ``` #### title - ```json synth { - "type": "string", - "faker": { - "generator": "title" - } + "type": "string", + "faker": { + "generator": "title" + } } ``` #### suffix - ```json synth { - "type": "string", - "faker": { - "generator": "suffix" - } + "type": "string", + "faker": { + "generator": "suffix" + } } ``` #### name - ```json synth { - "type": "string", - "faker": { - "generator": "name" - } + "type": "string", + "faker": { + "generator": "name" + } } ``` #### name_with_title - ```json synth { - "type": "string", - "faker": { - "generator": "name_with_title" - } + "type": "string", + "faker": { + "generator": "name_with_title" + } } ``` #### credit_card - ```json synth { - "type": "string", - "faker": { - "generator": "credit_card" - } + "type": "string", + "faker": { + "generator": "credit_card" + } } ``` #### free_email_provider - ```json synth { - "type": "string", - "faker": { - "generator": "free_email_provider" - } + "type": "string", + "faker": { + "generator": "free_email_provider" + } } ``` #### domain_suffix - ```json synth { - "type": "string", - "faker": { - "generator": "domain_suffix" - } + "type": "string", + "faker": { + "generator": "domain_suffix" + } } ``` #### free_email - ```json synth { - "type": "string", - "faker": { - "generator": "free_email" - } + "type": "string", + "faker": { + "generator": "free_email" + } } ``` #### safe_email - ```json synth { - "type": "string", - "faker": { - "generator": "safe_email" - } + "type": "string", + "faker": { + "generator": "safe_email" + } } ``` #### username - ```json synth { - "type": "string", - "faker": { - "generator": "username" - } + "type": "string", + "faker": { + "generator": "username" + } } ``` #### ipv4 - ```json synth { - "type": "string", - "faker": { - "generator": "ipv4" - } + "type": "string", + "faker": { + "generator": "ipv4" + } } ``` #### ipv6 - ```json synth { - "type": "string", - "faker": { - "generator": "ipv6" - } + "type": "string", + "faker": { + "generator": "ipv6" + } } ``` #### ip - ```json synth { - "type": "string", - "faker": { - "generator": "ip" - } + "type": "string", + "faker": { + "generator": "ip" + } } ``` #### mac_address - ```json synth { - "type": "string", - "faker": { - "generator": "mac_address" - } + "type": "string", + "faker": { + "generator": "mac_address" + } } ``` #### color - ```json synth { - "type": "string", - "faker": { - "generator": "color" - } + "type": "string", + "faker": { + "generator": "color" + } } ``` #### user_agent - ```json synth { - "type": "string", - "faker": { - "generator": "user_agent" - } + "type": "string", + "faker": { + "generator": "user_agent" + } } ``` #### rfc_status_code - ```json synth { - "type": "string", - "faker": { - "generator": "rfc_status_code" - } + "type": "string", + "faker": { + "generator": "rfc_status_code" + } } ``` #### valid_status_code - ```json synth { - "type": "string", - "faker": { - "generator": "valid_status_code" - } + "type": "string", + "faker": { + "generator": "valid_status_code" + } } ``` #### company_suffix - ```json synth { - "type": "string", - "faker": { - "generator": "company_suffix" - } + "type": "string", + "faker": { + "generator": "company_suffix" + } } ``` #### company_name - ```json synth { - "type": "string", - "faker": { - "generator": "company_name" - } + "type": "string", + "faker": { + "generator": "company_name" + } } ``` #### buzzword - ```json synth { - "type": "string", - "faker": { - "generator": "buzzword" - } + "type": "string", + "faker": { + "generator": "buzzword" + } } ``` #### buzzword_muddle - ```json synth { - "type": "string", - "faker": { - "generator": "buzzword_muddle" - } + "type": "string", + "faker": { + "generator": "buzzword_muddle" + } } ``` #### buzzword_tail - ```json synth { - "type": "string", - "faker": { - "generator": "buzzword_tail" - } + "type": "string", + "faker": { + "generator": "buzzword_tail" + } } ``` #### catch_phrase - ```json synth { - "type": "string", - "faker": { - "generator": "catch_phrase" - } + "type": "string", + "faker": { + "generator": "catch_phrase" + } } ``` #### bs_verb - ```json synth { - "type": "string", - "faker": { - "generator": "bs_verb" - } + "type": "string", + "faker": { + "generator": "bs_verb" + } } ``` #### bs_adj - ```json synth { - "type": "string", - "faker": { - "generator": "bs_adj" - } + "type": "string", + "faker": { + "generator": "bs_adj" + } } ``` #### bs_noun - ```json synth { - "type": "string", - "faker": { - "generator": "bs_noun" - } + "type": "string", + "faker": { + "generator": "bs_noun" + } } ``` #### bs - ```json synth { - "type": "string", - "faker": { - "generator": "bs" - } + "type": "string", + "faker": { + "generator": "bs" + } } ``` #### profession - ```json synth { - "type": "string", - "faker": { - "generator": "profession" - } + "type": "string", + "faker": { + "generator": "profession" + } } ``` #### industry - ```json synth { - "type": "string", - "faker": { - "generator": "industry" - } + "type": "string", + "faker": { + "generator": "industry" + } } ``` #### city_prefix - ```json synth { - "type": "string", - "faker": { - "generator": "city_prefix" - } + "type": "string", + "faker": { + "generator": "city_prefix" + } } ``` #### city_suffix - ```json synth { - "type": "string", - "faker": { - "generator": "city_suffix" - } + "type": "string", + "faker": { + "generator": "city_suffix" + } } ``` #### city_name - ```json synth { - "type": "string", - "faker": { - "generator": "city_name" - } + "type": "string", + "faker": { + "generator": "city_name" + } } ``` #### country_name - ```json synth { - "type": "string", - "faker": { - "generator": "country_name" - } + "type": "string", + "faker": { + "generator": "country_name" + } } ``` #### country_code - ```json synth { - "type": "string", - "faker": { - "generator": "country_code" - } + "type": "string", + "faker": { + "generator": "country_code" + } } ``` #### street_suffix - ```json synth { - "type": "string", - "faker": { - "generator": "street_suffix" - } + "type": "string", + "faker": { + "generator": "street_suffix" + } } ``` #### street_name - ```json synth { - "type": "string", - "faker": { - "generator": "street_name" - } + "type": "string", + "faker": { + "generator": "street_name" + } } ``` #### time_zone - ```json synth { - "type": "string", - "faker": { - "generator": "time_zone" - } + "type": "string", + "faker": { + "generator": "time_zone" + } } ``` #### state_name - ```json synth { - "type": "string", - "faker": { - "generator": "state_name" - } + "type": "string", + "faker": { + "generator": "state_name" + } } ``` #### state_abbr - ```json synth { - "type": "string", - "faker": { - "generator": "state_abbr" - } + "type": "string", + "faker": { + "generator": "state_abbr" + } } ``` #### secondary_address_type - ```json synth { - "type": "string", - "faker": { - "generator": "secondary_address_type" - } + "type": "string", + "faker": { + "generator": "secondary_address_type" + } } ``` #### secondary_address - ```json synth { - "type": "string", - "faker": { - "generator": "secondary_address" - } + "type": "string", + "faker": { + "generator": "secondary_address" + } } ``` #### zip_code - ```json synth { - "type": "string", - "faker": { - "generator": "zip_code" - } + "type": "string", + "faker": { + "generator": "zip_code" + } } ``` #### post_code - ```json synth { - "type": "string", - "faker": { - "generator": "post_code" - } + "type": "string", + "faker": { + "generator": "post_code" + } } ``` #### building_number - ```json synth { - "type": "string", - "faker": { - "generator": "building_number" - } + "type": "string", + "faker": { + "generator": "building_number" + } } ``` #### latitude - ```json synth { - "type": "string", - "faker": { - "generator": "latitude" - } + "type": "string", + "faker": { + "generator": "latitude" + } } ``` #### longitude - ```json synth { - "type": "string", - "faker": { - "generator": "longitude" - } + "type": "string", + "faker": { + "generator": "longitude" + } } ``` #### phone_number - ```json synth { - "type": "string", - "faker": { - "generator": "phone_number" - } + "type": "string", + "faker": { + "generator": "phone_number" + } } ``` #### cell_number - ```json synth { - "type": "string", - "faker": { - "generator": "cell_number" - } + "type": "string", + "faker": { + "generator": "cell_number" + } } ``` #### file_path - ```json synth { - "type": "string", - "faker": { - "generator": "file_path" - } + "type": "string", + "faker": { + "generator": "file_path" + } } ``` #### file_name - ```json synth { - "type": "string", - "faker": { - "generator": "file_name" - } + "type": "string", + "faker": { + "generator": "file_name" + } } ``` #### file_extension - ```json synth { - "type": "string", - "faker": { - "generator": "file_extension" - } + "type": "string", + "faker": { + "generator": "file_extension" + } } ``` #### dir_path - ```json synth { - "type": "string", - "faker": { - "generator": "dir_path" - } + "type": "string", + "faker": { + "generator": "dir_path" + } } ``` @@ -743,28 +688,27 @@ the `generator` key. `serialized` is a variant of the `string` generator type which serializes the output of a child generator into a string. `serialized` has 2 fields, + - `serializer`: The serializer to be used (currently only `json`) - `content`: The content to be serialized. This can be any valid Synth generator - - #### Example ```json synth { - "type": "string", - "serialized": { - "serializer": "json", - "content": { - "type": "object", - "username": { - "type": "string", - "faker": { - "generator": "name" - } - } + "type": "string", + "serialized": { + "serializer": "json", + "content": { + "type": "object", + "username": { + "type": "string", + "faker": { + "generator": "name" } + } } + } } ``` @@ -775,6 +719,7 @@ The `truncated` generator truncates the output of it's inner generator to a fixe If the output of its inner generator is less than or equal to the length, it is left untouched. `truncated` has 2 fields, + - `length`: The number of characters to truncate to. This can be any Synth generator that yields a non-negative Number. - `content`: The content to be truncated. This can be any Synth generator that yields a String. @@ -795,11 +740,12 @@ If the output of its inner generator is less than or equal to the length, it is ## sliced -The `sliced` generator takes a character slice of a string. +The `sliced` generator takes a character slice of a string. The slice format is `[start]:[finish]` and if it fails to parse the original string will be returned. `sliced` has 2 fields, + - `slice`: A string with a optional start and optional end separated by `:` - `content`: The content to be sliced. This can be any Synth generator that yields a String. @@ -839,8 +785,6 @@ Or shorthand notation if you are declaring an object field: } ``` - - ## categorical A `categorical` is a variant of the `string` generator type that generates values from a finite set of user-defined @@ -868,4 +812,3 @@ The value of the `"categorical"` key must be an object whose: - values are non-negative integers defining the relative weight of the corresponding variant (e.g. `8`, `2`, etc.). [faker]: https://github.com/cksac/fake-rs - diff --git a/docs/docs/content/toc.md b/docs/docs/content/toc.md index 7e18c563..a2ae9408 100644 --- a/docs/docs/content/toc.md +++ b/docs/docs/content/toc.md @@ -9,46 +9,46 @@ Synth has the following types of generators: Modifiers encode additional constraints or variations on their contained generator(s) -* [optional](/modifiers#optional) makes a value nullable -* [unique](/modifiers#unique) ensures the generated values don't contain -duplicates -* [one_of](/content/one-of) allows you to choose from a set of contained -generators -* [same_as](/content/same-as) creates a reference to another field in this or -another collection +- [optional](/modifiers#optional) makes a value nullable +- [unique](/modifiers#unique) ensures the generated values don't contain + duplicates +- [one_of](/content/one-of) allows you to choose from a set of contained + generators +- [same_as](/content/same-as) creates a reference to another field in this or + another collection ## Generators -* [null](/content/null) generates as many nulls as you ever want -* [bool](/content/bool) generates `true` or `false`, either constant or -following a given percentage -* [number](/content/number) generates ranges, distributions or series of -[integer or floating-point](/content/number#subtype) numbers -* [series](/content/series) generates streams of events (e.g. for logs) - * [incrementing](/content/series#incrementing) emits evenly spaced events - * [poisson](/content/series#poisson) models a random poisson process - * [cyclical](/content/series#cyclical) models periodic events - * [zip](/content/series#zip) combines multiple series together -* [string](/content/string) can contain one of the following generators for -various classes of string: - * [pattern](/content/string#pattern) takes a regular expression and - generates matching strings - * [uuid](/content/string#uuid) generates hyphenated UUIDs - optionally with time zone - * [faker](/content/string#faker) has a large number of generators for names, - contact information, credit card numbers, sentences, and much more - * [format](/content/string#format) combines multiple generators to one - formatted string - * [serialized](/content/string#serialized) JSONifies the value of the - contained generator - * [truncated](/content/string#truncated) ensures all generated strings stay - within length limits - * [categorical](/content/string#categorical) is like a - [one_of](/content/one-of) specialized for strings -* [date_time](/content/date-time) generates dates and times, -* [object](/content/object) creates an object with string keys containing -generators for the values -* [array](/content/array) fills an array of the given length with elements of -the contained generator -* [datasource](datasource) pulls data from an external source -like a file +- [null](/content/null) generates as many nulls as you ever want +- [bool](/content/bool) generates `true` or `false`, either constant or + following a given percentage +- [number](/content/number) generates ranges, distributions or series of + [integer or floating-point](/content/number#subtype) numbers +- [series](/content/series) generates streams of events (e.g. for logs) + - [incrementing](/content/series#incrementing) emits evenly spaced events + - [poisson](/content/series#poisson) models a random poisson process + - [cyclical](/content/series#cyclical) models periodic events + - [zip](/content/series#zip) combines multiple series together +- [string](/content/string) can contain one of the following generators for + various classes of string: + - [pattern](/content/string#pattern) takes a regular expression and + generates matching strings + - [uuid](/content/string#uuid) generates hyphenated UUIDs + optionally with time zone + - [faker](/content/string#faker) has a large number of generators for names, + contact information, credit card numbers, sentences, and much more + - [format](/content/string#format) combines multiple generators to one + formatted string + - [serialized](/content/string#serialized) JSONifies the value of the + contained generator + - [truncated](/content/string#truncated) ensures all generated strings stay + within length limits + - [categorical](/content/string#categorical) is like a + [one_of](/content/one-of) specialized for strings +- [date_time](/content/date-time) generates dates and times, +- [object](/content/object) creates an object with string keys containing + generators for the values +- [array](/content/array) fills an array of the given length with elements of + the contained generator +- [datasource](datasource) pulls data from an external source + like a file diff --git a/docs/docs/content/unique.md b/docs/docs/content/unique.md index 7598ce04..96a24787 100644 --- a/docs/docs/content/unique.md +++ b/docs/docs/content/unique.md @@ -8,25 +8,25 @@ Synth's `unique` generator type generates values which are guaranteed to be uniq ```json synth { - "type": "array", - "length": { + "type": "array", + "length": { + "type": "number", + "constant": 10 + }, + "content": { + "type": "object", + "ticker": { + "type": "unique", + "content": { "type": "number", - "constant": 10 - }, - "content": { - "type": "object", - "ticker": { - "type": "unique", - "content": { - "type": "number", - "range": { - "low": 0, - "high": 10, - "step": 1 - } - } + "range": { + "low": 0, + "high": 10, + "step": 1 } + } } + } } ``` @@ -38,24 +38,24 @@ Below is an example of a generator which will fail since the inner generator can ```json synth { - "type": "array", - "length": { + "type": "array", + "length": { + "type": "number", + "constant": 20 + }, + "content": { + "type": "object", + "ticker": { + "type": "unique", + "content": { "type": "number", - "constant": 20 - }, - "content": { - "type": "object", - "ticker": { - "type": "unique", - "content": { - "type": "number", - "range": { - "low": 0, - "high": 10, - "step": 1 - } - } + "range": { + "low": 0, + "high": 10, + "step": 1 } + } } + } } -``` \ No newline at end of file +``` diff --git a/docs/docs/examples/bank.md b/docs/docs/examples/bank.md index 949a52dc..6eedc17e 100644 --- a/docs/docs/examples/bank.md +++ b/docs/docs/examples/bank.md @@ -34,7 +34,7 @@ application testing. ```json { "id": 1, - "amount": 5001.70, + "amount": 5001.7, "currency": "GIP", "timestamp": "2020-05-13T20:48:01+0000", "description": "Walmart Credit Purchase", @@ -290,10 +290,10 @@ There is quite a bit going on here, so let's break it down. This file represents a [`collection`](../getting_started/core-concepts). Collections are [array](../content/array.md)s under the hood and so they have 2 fields. -1) The `content` of an Array. This can be any valid JSON, but since `bank_db` originates from a SQL database with column +1. The `content` of an Array. This can be any valid JSON, but since `bank_db` originates from a SQL database with column names and so on, it is a JSON object. -2) The `length` of an Array. The length of an Array is actually also a Content node. This gives you flexibility - for +2. The `length` of an Array. The length of an Array is actually also a Content node. This gives you flexibility - for example you can make the length of an array be a `number::range` For more information on how to compose schemas, see the [Schema](../getting_started/schema.md) page. @@ -354,7 +354,7 @@ the [same_as](../content/same-as) content type to express this foreign key relat The `currency` field should reflect the real currencies that the bank supports. We could use the [string::faker](../content/string) support `currency_code` generator to do this, but the bank only supports `USD` -, `GBP` and `EUR`. So she uses a [string::categorical](../content/string) instead. Roughly 80% of transactions are +, `GBP` and `EUR`. So she uses a [string::categorical](../content/string) instead. Roughly 80% of transactions are in `USD` so let's assign a higher probability to that variant. ```json synth @@ -385,6 +385,7 @@ a `descriptions.json` file in the workspace. ``` Next, edit the `description` field as follow: + ```json synth { "type": "datasource", diff --git a/docs/docs/getting_started/core-concepts.md b/docs/docs/getting_started/core-concepts.md index d7396831..e375dc7e 100644 --- a/docs/docs/getting_started/core-concepts.md +++ b/docs/docs/getting_started/core-concepts.md @@ -18,8 +18,8 @@ structure: ``` └── blog/ ├── users.json - └── posts.json -``` + └── posts.json +``` Any file whose extension is `.json` in a namespace directory will be opened by the [`synth generate`][synth-generate] subcommand and considered part of the @@ -32,7 +32,7 @@ their name and correspond to [tables][sql-tables] in the world of relational databases. Strictly speaking, collections are a super-set of tables as they are in fact arbitrarily deep JSON document trees. -Collections are represented in a namespace directory as JSON files. The *name* +Collections are represented in a namespace directory as JSON files. The _name_ of a collection (the way it is referred to by [`synth`][synth]) is its filename without the extension. For example the file `bank/transactions.json` defines a collection named `transactions` in a namespace `bank`. @@ -44,7 +44,7 @@ structure then looks like this: ``` └── bank/ ├── transactions.json - └── users.json + └── users.json ``` Collections must be valid instances of the [`synth` schema][schema] that @@ -145,21 +145,12 @@ This behavior can be tuned (and the seed be changed, or randomized) using the `--seed` or `--random` flag. [synth]: cli.md - [sql-schemas]: https://www.postgresql.org/docs/9.1/ddl-schemas.html - [sql-tables]: https://www.postgresql.org/docs/9.1/sql-createtable.html - [same-as]: ../content/same-as - [schema]: schema.md - [array-generators]: ../content/array - [same-as]: ../content/same-as - [synth-import]: cli.md#command-import - [synth-generate]: cli.md#command-generate - -[seedable-rng]: https://docs.rs/rand/0.8.4/rand/trait.SeedableRng.html#method.seed_from_u64 \ No newline at end of file +[seedable-rng]: https://docs.rs/rand/0.8.4/rand/trait.SeedableRng.html#method.seed_from_u64 diff --git a/docs/docs/getting_started/hello-world.md b/docs/docs/getting_started/hello-world.md index 5cd4d1ff..0430fced 100644 --- a/docs/docs/getting_started/hello-world.md +++ b/docs/docs/getting_started/hello-world.md @@ -2,7 +2,7 @@ title: Hello world --- -After installing [`synth`][synth], the next step is to create a **namespace**. +After installing [`synth`][synth], the next step is to create a **namespace**. Namespaces are directories in your filesystem that [`synth`][synth] uses to read your schemas from. Currently [`synth`][synth] reads schemas written in a specialized JSON data model. You can find out everything there is to know about [`synth`][synth] schemas in the [Generators][generators] section or in the [Schema][schema] section. In this section we will show you how to set up a simple "hello world" data generator. @@ -22,16 +22,16 @@ at `hello_synth/say_hello.json`: ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u64", - "constant": 1 - }, - "content": { - "type": "string", - "pattern": "Hello world!" - } + "type": "array", + "length": { + "type": "number", + "subtype": "u64", + "constant": 1 + }, + "content": { + "type": "string", + "pattern": "Hello world!" + } } ``` @@ -50,9 +50,10 @@ synth generate hello_synth and you should see an output very close to the output of the snippet. ## Where to go from here -* Take a look at the exhaustive [generators reference][generators]. -* Go deeper into how [`synth`][synth] works by looking at the [core concepts][core-concepts] and the specifications of the [schema][schema]. -* For more complex real life examples, see the [examples][examples] section. + +- Take a look at the exhaustive [generators reference][generators]. +- Go deeper into how [`synth`][synth] works by looking at the [core concepts][core-concepts] and the specifications of the [schema][schema]. +- For more complex real life examples, see the [examples][examples] section. [synth]: cli.md [schema]: schema.md diff --git a/docs/docs/getting_started/how-it-works.md b/docs/docs/getting_started/how-it-works.md index 116e7b8e..adbcd945 100644 --- a/docs/docs/getting_started/how-it-works.md +++ b/docs/docs/getting_started/how-it-works.md @@ -8,7 +8,6 @@ Synth's funcionality can be broken into 3 main parts: 2. Schema (IR): The [Schema](schema.md) intermediate representation is a compact state representing the range of data generation 3. Generator Network: Schemas are transpiled into a network of generators which actually generate the required data. -Below is a high-level diagram illustrating the process: +Below is a high-level diagram illustrating the process: - ![How it works](img/how_it_works.png) diff --git a/docs/docs/getting_started/installation.md b/docs/docs/getting_started/installation.md index 5a21dff3..6c7b8362 100644 --- a/docs/docs/getting_started/installation.md +++ b/docs/docs/getting_started/installation.md @@ -79,6 +79,7 @@ If compilation fails, it may be because some required dependencies are not insta ``` sudo apt-get install libssl-dev libsqlite3-dev ``` + ::: diff --git a/docs/docs/getting_started/schema.md b/docs/docs/getting_started/schema.md index 72cea2bc..4c84cbdb 100644 --- a/docs/docs/getting_started/schema.md +++ b/docs/docs/getting_started/schema.md @@ -161,32 +161,32 @@ And the second, the `users` collection: ```json synth { - "type": "array", - "length": { - "type": "number", - "subtype": "u64", - "range": { - "low": 1, - "high": 6, - "step": 1 - } + "type": "array", + "length": { + "type": "number", + "subtype": "u64", + "range": { + "low": 1, + "high": 6, + "step": 1 + } + }, + "content": { + "type": "object", + "user_id": { + "type": "number", + "subtype": "u64", + "id": { + "start_at": 0 + } }, - "content": { - "type": "object", - "user_id": { - "type": "number", - "subtype": "u64", - "id": { - "start_at": 0 - } - }, - "user_email": { - "type": "string", - "faker": { - "generator": "email" - } - } + "user_email": { + "type": "string", + "faker": { + "generator": "email" + } } + } } ``` diff --git a/docs/docs/getting_started/synth.md b/docs/docs/getting_started/synth.md index d471a704..f48f77dd 100644 --- a/docs/docs/getting_started/synth.md +++ b/docs/docs/getting_started/synth.md @@ -6,7 +6,7 @@ Synth is a tool for generating realistic data using a declarative data model. Sy ## Why Synth -Synth answers a simple question. There are so many ways to consume data, why are there no frameworks for *generating* data? +Synth answers a simple question. There are so many ways to consume data, why are there no frameworks for _generating_ data? Synth provides a robust, declarative framework for specifying constraint based data generation, solving the following problems developers face on the regular: @@ -28,4 +28,4 @@ The key features of Synth are: - **Database Agnostic**: Synth supports semi-structured data and is database agnostic - playing nicely with SQL and NoSQL databases. -- **Semantic Data Types**: Synth has a library of semantic 'faker' types to cover PII like names, addresses, credit card numbers etc. \ No newline at end of file +- **Semantic Data Types**: Synth has a library of semantic 'faker' types to cover PII like names, addresses, credit card numbers etc. diff --git a/docs/docs/integrations/postgres.md b/docs/docs/integrations/postgres.md index 93db1a54..c653cf3b 100644 --- a/docs/docs/integrations/postgres.md +++ b/docs/docs/integrations/postgres.md @@ -44,7 +44,7 @@ more realistic data model by automatically inferring bounds on types. `synth` has its own internal data model, and so does Postgres, therefore a conversion occurs between `synth` types and Postgres types. The inferred type -can be seen below. The synth types link to default generator *variant* +can be seen below. The synth types link to default generator _variant_ generated during the `import` process for that PostgreSQL type. Note, not all PostgreSQL types have been covered yet. If there is a type you @@ -70,9 +70,9 @@ table formatter: https://codebeautify.org/markdown-formatter | float4 | [f32](../content/number#range) | | float8 | [f64](../content/number#range) | | numeric | [f64](../content/number#range) | -| timestamptz | [date_time](../content/date-time) | -| timestamp | [naive_date_time](../content/date-time) | -| date | [naive_date](../content/date-time) | +| timestamptz | [date_time](../content/date-time) | +| timestamp | [naive_date_time](../content/date-time) | +| date | [naive_date](../content/date-time) | | uuid | [string](../content/string#uuid) | ### Example Import @@ -80,6 +80,7 @@ table formatter: https://codebeautify.org/markdown-formatter Below is an example import for a single table. Postgres table definition: + ```sql create table doctors ( @@ -94,6 +95,7 @@ create table doctors ``` And the corresponding `synth` collection: + ```json { "type": "array", @@ -141,6 +143,7 @@ And the corresponding `synth` collection: } ``` + ### Example Import Command ```bash diff --git a/docs/docs/other/telemetry.md b/docs/docs/other/telemetry.md index adbfc30a..e2dd878d 100644 --- a/docs/docs/other/telemetry.md +++ b/docs/docs/other/telemetry.md @@ -26,8 +26,8 @@ public releases. Below are a set of principles that guide the telemetry decisions made in Synth: -1. It is made *completely transparent* that telemetry is going to be installed -2. It is made *completely transparent* as to what data we collect +1. It is made _completely transparent_ that telemetry is going to be installed +2. It is made _completely transparent_ as to what data we collect 3. No personally identifiable information is collected. (IP addresses are discarded at the sink) 4. Nothing is collected unless it is explicitly and clearly documented here. @@ -84,29 +84,29 @@ activity: { "type": "object", "distinct_id": { - "type": "string", - "uuid": {} + "type": "string", + "uuid": {} }, "command": { - "type": "string", - "categorical": { - "import": 1, - "generate": 10, - "telemetry::enabled": 10, - "telemetry::disabled": 1 - } + "type": "string", + "categorical": { + "import": 1, + "generate": 10, + "telemetry::enabled": 10, + "telemetry::disabled": 1 + } }, "version": { - "type": "string", - "pattern": "v0\\.4\\.3" + "type": "string", + "pattern": "v0\\.4\\.3" }, "os": { - "type": "string", - "categorical": { - "linux": 10, - "macos": 10, - "windows": 10 - } + "type": "string", + "categorical": { + "linux": 10, + "macos": 10, + "windows": 10 + } }, "timestamp": { "type": "date_time", diff --git a/docs/docs/tutorials/creating-logs-with-synth.md b/docs/docs/tutorials/creating-logs-with-synth.md index bdbb9fea..85c19a8d 100644 --- a/docs/docs/tutorials/creating-logs-with-synth.md +++ b/docs/docs/tutorials/creating-logs-with-synth.md @@ -16,29 +16,34 @@ many logs as you need. We want to generate HTTP logs - but what is an HTTP log actually? Different HTTP servers will have different logging conventions - but we'll stick to the -[Common Log Format](https://en.wikipedia.org/wiki/Common_Log_Format) (CLF) +[Common Log Format](https://en.wikipedia.org/wiki/Common_Log_Format) (CLF) used as a default by web servers like the [Apache Web Server](https://en.wikipedia.org/wiki/Apache_HTTP_Server). CLF has the following syntax: + ``` host ident authuser date request status bytes ``` + concretely: + ``` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ``` + or as json: + ```json { - "host": "127.0.0.1", // ip address of the client - "ident": "user-identifier", // RFC 1413 identity of the cient - "authuser": "frank", // userid - "date": "10/Oct/2000:13:55:36 -0700", // %d/%b/%Y:%H:%M:%S %z - "request": "GET /apache_pb.gif HTTP/1.0", // HTTP request from client - "status": 200, // HTTP status code - "bytes": 2326 // size of object returned in bytes + "host": "127.0.0.1", // ip address of the client + "ident": "user-identifier", // RFC 1413 identity of the cient + "authuser": "frank", // userid + "date": "10/Oct/2000:13:55:36 -0700", // %d/%b/%Y:%H:%M:%S %z + "request": "GET /apache_pb.gif HTTP/1.0", // HTTP request from client + "status": 200, // HTTP status code + "bytes": 2326 // size of object returned in bytes } ``` @@ -50,39 +55,44 @@ To install `synth` head over to the [download page](https://getsynth.com/downloa So let's get started! -To get started with synth let's create a new [namespace](/docs/getting_started/core-concepts) by simply +To get started with synth let's create a new [namespace](/docs/getting_started/core-concepts) by simply creating a new directory - we'll call it `clf-logs`: + ```commandline -$ mkdir clf-logs +$ mkdir clf-logs ``` -Next let's create a [collection](/docs/getting_started/core-concepts#collections) called logs which will define the + +Next let's create a [collection](/docs/getting_started/core-concepts#collections) called logs which will define the schema of our log data: + ```commandline $ cd clf-logs && touch logs.json ``` ### Scaffolding -We're going to base the meat of our CLF log schema on the `date` field. We -want our logs to look and behave realistically. We can model requests -arriving at our web server using a [poisson process](https://en.wikipedia.org/wiki/Poisson_point_process) - a model which is often used to model -independent random events with a mean interval between events (like +We're going to base the meat of our CLF log schema on the `date` field. We +want our logs to look and behave realistically. We can model requests +arriving at our web server using a [poisson process](https://en.wikipedia.org/wiki/Poisson_point_process) - a model which is often used to model +independent random events with a mean interval between events (like customers arriving a store.) `synth` has a [poisson generator](/docs/content/series#poisson) which we can use to do this. -First let's open up `logs.json` in our favourite IDE and define an array of +First let's open up `logs.json` in our favourite IDE and define an array of objects which have a field `date`: + ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": 0 - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": 0 + } } ``` Now lets sanity check our schema by generating some data: + ```commandline $ synth generate . --collection logs | jq [ @@ -94,94 +104,94 @@ $ synth generate . --collection logs | jq ### Poisson Generator -Cool! We generated a `0`. So far so good. Now let's swap out the `0` for the +Cool! We generated a `0`. So far so good. Now let's swap out the `0` for the poisson generator in `logs.json`. ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } } + } } ``` Our poisson series generator has 2 parameters: + - `start`: the earliest time an event can occur - `rate`: the average arrival rate for events ### Host -Next let's add the `host` field. Here we can simply use one of our [faker +Next let's add the `host` field. Here we can simply use one of our [faker generators](/docs/content/string#faker) for generating `ipv4` addresses: + ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - }, - "host": { - "type": "string", - "faker": { - "generator": "ipv4" - } - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } + }, + "host": { + "type": "string", + "faker": { + "generator": "ipv4" + } } + } } ``` Easy! - ### Ident -Ident corresponds to the RFC 1413 Identification Protocol. We can leave this -as `"-"` using the [pattern generator](/docs/content/string#pattern) for now, but you can get +Ident corresponds to the RFC 1413 Identification Protocol. We can leave this +as `"-"` using the [pattern generator](/docs/content/string#pattern) for now, but you can get creative if you need something more elaborate for your use case: - ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - }, - "host": { - "type": "string", - "faker": { - "generator": "ipv4" - } - }, - "ident": { - "type": "string", - "pattern": "-" - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } + }, + "host": { + "type": "string", + "faker": { + "generator": "ipv4" + } + }, + "ident": { + "type": "string", + "pattern": "-" } + } } ``` @@ -189,262 +199,265 @@ creative if you need something more elaborate for your use case: `authuser` is the userid of the person requesting the document. Usually "-" unless .htaccess has requested authentication. -Here we'll just use the `faker` [`first_name`](/docs/content/string#first_name) generator get a bunch of first +Here we'll just use the `faker` [`first_name`](/docs/content/string#first_name) generator get a bunch of first names: ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - }, - "host": { - "type": "string", - "faker": { - "generator": "ipv4" - } - }, - "ident": { - "type": "string", - "pattern": "-" - }, - "authuser": { - "type": "string", - "faker": { - "generator": "first_name" - } - } + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } + }, + "host": { + "type": "string", + "faker": { + "generator": "ipv4" + } + }, + "ident": { + "type": "string", + "pattern": "-" + }, + "authuser": { + "type": "string", + "faker": { + "generator": "first_name" + } } + } } ``` ### Request -Request is a little involved - we're going to need to use the [format](/docs/content/string#format) -generator to string together multiple generators. `format` takes arbitrarily +Request is a little involved - we're going to need to use the [format](/docs/content/string#format) +generator to string together multiple generators. `format` takes arbitrarily many arguments and a format string. Our format string is of the form: ``` {http_method} {endpoint} HTTP/1.0 ``` -We can build this compositionally with 2 child generators and a `format` +We can build this compositionally with 2 child generators and a `format` generator: ```json synth { - "type": "string", - "format": { - "format": "{http_method} /{endpoint} HTTP/1.0", - "arguments": { - "http_method": { - "type": "string", - "categorical": { - "GET": 1, - "PUT": 1, - "POST": 1, - "PATCH": 1 - } - }, - "endpoint": { - "type": "string", - "faker": { - "generator": "file_name" - } - } + "type": "string", + "format": { + "format": "{http_method} /{endpoint} HTTP/1.0", + "arguments": { + "http_method": { + "type": "string", + "categorical": { + "GET": 1, + "PUT": 1, + "POST": 1, + "PATCH": 1 + } + }, + "endpoint": { + "type": "string", + "faker": { + "generator": "file_name" } + } } + } } ``` -Here `http_method` is a categorical generator, with equal probability to -yield any of the 4 HTTP methods defined, and `endpoint` is a `faker` + +Here `http_method` is a categorical generator, with equal probability to +yield any of the 4 HTTP methods defined, and `endpoint` is a `faker` generator which generates file names. ### Status For `status` we'll be using the [`categorical`](/docs/content/string#categorical) generator as well - easy: + ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - }, - "host": { + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } + }, + "host": { + "type": "string", + "faker": { + "generator": "ipv4" + } + }, + "ident": { + "type": "string", + "pattern": "-" + }, + "authuser": { + "type": "string", + "faker": { + "generator": "first_name" + } + }, + "request": { + "type": "string", + "format": { + "format": "{http_method} /{endpoint} HTTP/1.0", + "arguments": { + "http_method": { "type": "string", - "faker": { - "generator": "ipv4" + "categorical": { + "GET": 1, + "PUT": 1, + "POST": 1, + "PATCH": 1 } - }, - "ident": { - "type": "string", - "pattern": "-" - }, - "authuser": { + }, + "endpoint": { "type": "string", "faker": { - "generator": "first_name" - } - }, - "request": { - "type": "string", - "format": { - "format": "{http_method} /{endpoint} HTTP/1.0", - "arguments": { - "http_method": { - "type": "string", - "categorical": { - "GET": 1, - "PUT": 1, - "POST": 1, - "PATCH": 1 - } - }, - "endpoint": { - "type": "string", - "faker": { - "generator": "file_name" - } - } - } - } - }, - "status": { - "type": "number", - "categorical": { - "200": 8, - "404": 1, - "500": 1 + "generator": "file_name" } + } } + } + }, + "status": { + "type": "number", + "categorical": { + "200": 8, + "404": 1, + "500": 1 + } } + } } ``` -In this case the we're assigning weights to the variants of the categorical. -80% of the time we'll be getting `200`, 10% of the time we'll get `404` and +In this case the we're assigning weights to the variants of the categorical. +80% of the time we'll be getting `200`, 10% of the time we'll get `404` and 10% of the time we'll get `500`. ### Bytes -And finally `bytes`. For `bytes` we'll use a [number range](/docs/content/number#range) generator +And finally `bytes`. For `bytes` we'll use a [number range](/docs/content/number#range) generator from 1 b to 1 MiB: ```json synth { - "type": "array", - "length": 1, - "content": { - "type": "object", - "date": { - "type": "series", - "format": "%d/%b/%Y:%H:%M:%S", - "poisson": { - "start": "10/Oct/2000:13:55:36", - "rate": "10m" - } - }, - "host": { + "type": "array", + "length": 1, + "content": { + "type": "object", + "date": { + "type": "series", + "format": "%d/%b/%Y:%H:%M:%S", + "poisson": { + "start": "10/Oct/2000:13:55:36", + "rate": "10m" + } + }, + "host": { + "type": "string", + "faker": { + "generator": "ipv4" + } + }, + "ident": { + "type": "string", + "pattern": "-" + }, + "authuser": { + "type": "string", + "faker": { + "generator": "first_name" + } + }, + "request": { + "type": "string", + "format": { + "format": "{http_method} /{endpoint} HTTP/1.0", + "arguments": { + "http_method": { "type": "string", - "faker": { - "generator": "ipv4" + "categorical": { + "GET": 1, + "PUT": 1, + "POST": 1, + "PATCH": 1 } - }, - "ident": { - "type": "string", - "pattern": "-" - }, - "authuser": { + }, + "endpoint": { "type": "string", "faker": { - "generator": "first_name" - } - }, - "request": { - "type": "string", - "format": { - "format": "{http_method} /{endpoint} HTTP/1.0", - "arguments": { - "http_method": { - "type": "string", - "categorical": { - "GET": 1, - "PUT": 1, - "POST": 1, - "PATCH": 1 - } - }, - "endpoint": { - "type": "string", - "faker": { - "generator": "file_name" - } - } - } - } - }, - "status": { - "type": "number", - "categorical": { - "200": 8, - "404": 1, - "500": 1 - } - }, - "bytes": { - "type": "number", - "range": { - "low": 1, - "high": 1048576 + "generator": "file_name" } + } } + } + }, + "status": { + "type": "number", + "categorical": { + "200": 8, + "404": 1, + "500": 1 + } + }, + "bytes": { + "type": "number", + "range": { + "low": 1, + "high": 1048576 + } } + } } ``` + And we're done! ## Tying it all together -Now we have our raw data structure - we need to compose it into the proper -log string. To do this, we'll create a separate collection `formatted` by -creating another file in our namespace called `formatted.json`. We'll then -use a combination of the `format` and [`same_as`](/docs/content/same-as) generators to compose +Now we have our raw data structure - we need to compose it into the proper +log string. To do this, we'll create a separate collection `formatted` by +creating another file in our namespace called `formatted.json`. We'll then +use a combination of the `format` and [`same_as`](/docs/content/same-as) generators to compose together fields from our original collection `logs.json`. ```json { - "type": "array", - "length": 1, - "content": { - "type": "string", - "format": { - "format": "{host} {ident} {authuser} [{date}] {request} {status} {bytes}", - "arguments": { - "host": "@logs.content.host", - "ident": "@logs.content.ident", - "authuser": "@logs.content.authuser", - "date": "@logs.content.date", - "request": "@logs.content.request", - "status": "@logs.content.status", - "bytes": "@logs.content.bytes" - } - } + "type": "array", + "length": 1, + "content": { + "type": "string", + "format": { + "format": "{host} {ident} {authuser} [{date}] {request} {status} {bytes}", + "arguments": { + "host": "@logs.content.host", + "ident": "@logs.content.ident", + "authuser": "@logs.content.authuser", + "date": "@logs.content.date", + "request": "@logs.content.request", + "status": "@logs.content.status", + "bytes": "@logs.content.bytes" + } } + } } ``` @@ -460,4 +473,3 @@ $ synth generate . --collection formatted --size 5 | jq "253.115.73.9 - Paolo [10/Oct/2000:15:14:53] PATCH /next.png HTTP/1.0 200 884672" ] ``` - diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 15fef96c..bed2f6b6 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,192 +1,191 @@ const isTargetVercel = () => { - return process.env["VERCEL"] === '1' -} + return process.env['VERCEL'] === '1'; +}; module.exports = { - title: 'Synth', - tagline: 'Open-source data generation', - url: "https://getsynth.com", - baseUrl: '/docs/', - onBrokenLinks: 'warn', - onBrokenMarkdownLinks: 'warn', - favicon: '/favicon.ico', - organizationName: 'getsynth', // Usually your GitHub org/user name. - projectName: 'synth', // Usually your repo name. - customFields: { - blogTitle: "Synth - Blog" - }, - plugins: [ - require('./src/lib/fathom.js'), - [ - "@papercups-io/docusaurus-plugin", - { - accountId: '41ff5b3d-e2c2-42ed-bed3-ef7a6c0dde62', - title: 'Welcome to Synth', - subtitle: 'Ask us anything in the chat window below 😊', - newMessagePlaceholder: 'Start typing...', - primaryColor: '#00dab8', - greeting: '', - requireEmailUpfront: false, - showAgentAvailability: false, - }, - ] + title: 'Synth', + tagline: 'Open-source data generation', + url: 'https://getsynth.com', + baseUrl: '/docs/', + onBrokenLinks: 'warn', + onBrokenMarkdownLinks: 'warn', + favicon: '/favicon.ico', + organizationName: 'getsynth', // Usually your GitHub org/user name. + projectName: 'synth', // Usually your repo name. + customFields: { + blogTitle: 'Synth - Blog', + }, + plugins: [ + require('./src/lib/fathom.js'), + [ + '@papercups-io/docusaurus-plugin', + { + accountId: '41ff5b3d-e2c2-42ed-bed3-ef7a6c0dde62', + title: 'Welcome to Synth', + subtitle: 'Ask us anything in the chat window below 😊', + newMessagePlaceholder: 'Start typing...', + primaryColor: '#00dab8', + greeting: '', + requireEmailUpfront: false, + showAgentAvailability: false, + }, ], - themeConfig: { - image: '/img/getsynth_favicon.png', - fathomAnalytics: { - siteId: isTargetVercel() ? 'QRVYRJEG' : 'HSFEOKWQ', + ], + themeConfig: { + image: '/img/getsynth_favicon.png', + fathomAnalytics: { + siteId: isTargetVercel() ? 'QRVYRJEG' : 'HSFEOKWQ', + }, + algolia: { + apiKey: 'b0583a1f7732cee4e8c80f4a86adf57c', + indexName: 'synth', + }, + hideableSidebar: true, + colorMode: { + defaultMode: 'dark', + disableSwitch: false, + respectPrefersColorScheme: false, + }, + navbar: { + hideOnScroll: true, + logo: { + alt: 'Synth', + src: '/img/synth_logo_large.png', + href: 'https://getsynth.com', + target: '_self', + }, + items: [ + { + to: '/docs/getting_started/synth', + activeBasePath: '/docs/getting_started', + label: 'Getting Started', + position: 'left', + }, + { + to: '/docs/examples/bank', + activeBasePath: '/docs/examples', + label: 'Examples', + position: 'left', + }, + { + to: '/docs/integrations/postgres', + activeBasePath: '/docs/integrations', + label: 'Integrations', + position: 'left', }, - algolia: { - apiKey: 'b0583a1f7732cee4e8c80f4a86adf57c', - indexName: 'synth', + { + to: '/docs/content/index', + activeBasePath: '/docs/content', + label: 'Generators', + position: 'left', }, - hideableSidebar: true, - colorMode: { - defaultMode: 'dark', - disableSwitch: false, - respectPrefersColorScheme: false, + { + to: 'blog', + label: 'Blog', + activeBasePath: '/blog', + position: 'right', + }, + { + href: 'https://discord.gg/H33rRDTm3p', + label: 'Discord', + position: 'right', + }, + { + href: 'https://github.com/getsynth/synth', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Learn', + items: [ + { + href: '/docs/getting_started/synth', + label: 'What is Synth?', + }, + { + to: '/docs/getting_started/hello-world', + label: 'Getting Started', + }, + { + to: '/docs/examples/bank', + label: 'Examples', + }, + ], }, - navbar: { - hideOnScroll: true, - logo: { - alt: 'Synth', - src: '/img/synth_logo_large.png', - href: 'https://getsynth.com', - target: '_self' + { + title: 'More', + items: [ + { + to: '/docs/content/index', + label: 'Generators', + }, + { + to: '/docs/integrations/postgres', + label: 'Integrations', }, - items: [ - { - to: '/docs/getting_started/synth', - activeBasePath: '/docs/getting_started', - label: 'Getting Started', - position: 'left', - }, - { - to: '/docs/examples/bank', - activeBasePath: '/docs/examples', - label: 'Examples', - position: 'left', - }, - { - to: '/docs/integrations/postgres', - activeBasePath: '/docs/integrations', - label: 'Integrations', - position: 'left', - }, - { - to: '/docs/content/index', - activeBasePath: '/docs/content', - label: 'Generators', - position: 'left', - }, - { - to: 'blog', - label: 'Blog', - activeBasePath: '/blog', - position: 'right' - }, - { - href: 'https://discord.gg/H33rRDTm3p', - label: 'Discord', - position: 'right' - }, - { - href: 'https://github.com/getsynth/synth', - label: 'GitHub', - position: 'right', - }, - ], + ], }, - footer: { - style: 'dark', - links: [ - { - title: 'Learn', - items: [ - { - href: '/docs/getting_started/synth', - label: 'What is Synth?', - }, - { - to: '/docs/getting_started/hello-world', - label: 'Getting Started', - }, - { - to: '/docs/examples/bank', - label: 'Examples', - }, - ], - }, - { - title: 'More', - items: [ - { - to: '/docs/content/index', - label: 'Generators', - }, - { - to: '/docs/integrations/postgres', - label: 'Integrations' - } - ], - }, - { - title: 'Community', - items: [ - { - to: '/blog', - label: 'Blog', - }, - { - href: 'https://github.com/getsynth/synth', - label: 'GitHub', - }, - { - href: 'https://discord.gg/H33rRDTm3p', - label: 'Discord', - } - ], - } - ], - logo: { - alt: 'Built with <3 by OpenQuery in London', - src: 'img/synth_logo_large.png', - href: 'https://getsynth.com', + { + title: 'Community', + items: [ + { + to: '/blog', + label: 'Blog', + }, + { + href: 'https://github.com/getsynth/synth', + label: 'GitHub', }, - copyright: `Copyright © ${new Date().getFullYear()} OpenQuery.`, + { + href: 'https://discord.gg/H33rRDTm3p', + label: 'Discord', + }, + ], }, - announcementBar: { - id: 'announcementBar', // Increment on change - content: `⭐️ If you like Synth, give it a star on GitHub!`, - isCloseable: true + isCloseable: true, + }, + prism: { + additionalLanguages: ['rust', 'graphql'], + }, + }, + presets: [ + [ + '@docusaurus/preset-classic', + { + docs: { + routeBasePath: '/', + sidebarPath: require.resolve('./sidebars.js'), + // Please change this to your repo. + editUrl: 'https://github.com/getsynth/synth/edit/master/docs/', }, - prism: { - additionalLanguages: ['rust', 'graphql'], + blog: { + blogSidebarTitle: 'All posts', + blogSidebarCount: 'ALL', }, - }, - presets: [ - [ - '@docusaurus/preset-classic', - { - docs: { - routeBasePath: '/', - sidebarPath: require.resolve('./sidebars.js'), - // Please change this to your repo. - editUrl: - 'https://github.com/getsynth/synth/edit/master/docs/', - }, - blog: { - blogSidebarTitle: 'All posts', - blogSidebarCount: 'ALL', - }, - theme: { - customCss: require.resolve('./src/css/custom.css') - }, - }, - ], + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + }, ], + ], }; diff --git a/docs/package-lock.json b/docs/package-lock.json index fd809e03..ef3c11cf 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -26,7 +26,14 @@ "@types/react": "^17.0.2", "@types/react-helmet": "^6.1.0", "@types/react-router-dom": "^5.1.7", + "@typescript-eslint/eslint-plugin": "^5.11.0", + "@typescript-eslint/parser": "^5.11.0", + "eslint": "^8.8.0", + "eslint-config-google": "^0.14.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-react": "^7.28.0", "http-proxy-middleware": "^1.0.6", + "prettier": "2.5.1", "typescript": "^4.1.5" } }, @@ -2966,6 +2973,80 @@ "node": ">=12.13.0" } }, + "node_modules/@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -2979,6 +3060,26 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@mdx-js/mdx": { "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", @@ -3557,6 +3658,190 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz", + "integrity": "sha512-HJh33bgzXe6jGRocOj4FmefD7hRY4itgjzOrSs3JPrTNXsX7j5+nQPciAUj/1nZtwo2kAc3C75jZO+T23gzSGw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/type-utils": "5.11.0", + "@typescript-eslint/utils": "5.11.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.11.0.tgz", + "integrity": "sha512-x0DCjetHZYBRovJdr3U0zG9OOdNXUaFLJ82ehr1AlkArljJuwEsgnud+Q7umlGDFLFrs8tU8ybQDFocp/eX8mQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/typescript-estree": "5.11.0", + "debug": "^4.3.2" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.11.0.tgz", + "integrity": "sha512-z+K4LlahDFVMww20t/0zcA7gq/NgOawaLuxgqGRVKS0PiZlCTIUtX0EJbC0BK1JtR4CelmkPK67zuCgpdlF4EA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.11.0.tgz", + "integrity": "sha512-wDqdsYO6ofLaD4DsGZ0jGwxp4HrzD2YKulpEZXmgN3xo4BHJwf7kq49JTRpV0Gx6bxkSUmc9s0EIK1xPbFFpIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.11.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.11.0.tgz", + "integrity": "sha512-cxgBFGSRCoBEhvSVLkKw39+kMzUKHlJGVwwMbPcTZX3qEhuXhrjwaZXWMxVfxDgyMm+b5Q5b29Llo2yow8Y7xQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.11.0.tgz", + "integrity": "sha512-yVH9hKIv3ZN3lw8m/Jy5I4oXO4ZBMqijcXCdA4mY8ull6TPTAoQnKKrcZ0HDXg7Bsl0Unwwx7jcXMuNZc0m4lg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.11.0.tgz", + "integrity": "sha512-g2I480tFE1iYRDyMhxPAtLQ9HAn0jjBtipgTCZmd9I9s11OV8CTsG+YfFciuNDcHqm4csbAgC2aVZCHzLxMSUw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/typescript-estree": "5.11.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.11.0.tgz", + "integrity": "sha512-E8w/vJReMGuloGxJDkpPlGwhxocxOpSVgSvjiLO5IxZPmxZF30weOeJYyPSEACwM+X4NziYS9q+WkN/2DHYQwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.11.0", + "eslint-visitor-keys": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -3711,9 +3996,9 @@ } }, "node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", "bin": { "acorn": "bin/acorn" }, @@ -3729,6 +4014,15 @@ "acorn": "^8" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -3961,6 +4255,25 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3985,6 +4298,23 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.flatmap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", + "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -5738,6 +6068,12 @@ "node": ">=4.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", @@ -5928,6 +6264,18 @@ "buffer-indexof": "^1.0.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -6175,6 +6523,153 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.2.0", + "espree": "^9.3.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz", + "integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flatmap": "^1.2.5", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.0", + "object.values": "^1.1.5", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -6184,7 +6679,145 @@ "estraverse": "^4.1.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esprima": { @@ -6199,6 +6832,27 @@ "node": ">=4" } }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -6600,6 +7254,12 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "node_modules/fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", @@ -6687,6 +7347,18 @@ "node": ">=0.8.0" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", @@ -6806,6 +7478,25 @@ "node": ">=8" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, "node_modules/follow-redirects": { "version": "1.14.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", @@ -7125,6 +7816,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8717,6 +9414,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "node_modules/json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -8747,6 +9450,19 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsx-ast-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.3", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -8803,6 +9519,19 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", @@ -9418,6 +10147,12 @@ "node": ">=0.10.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -9683,6 +10418,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object.getownpropertydescriptors": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", @@ -9699,6 +10465,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -9814,6 +10593,23 @@ "node": ">=4" } }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -10939,6 +11735,15 @@ "postcss": "^8.2.15" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", @@ -10947,6 +11752,18 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/pretty-error": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-3.0.4.tgz", @@ -11530,6 +12347,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, "node_modules/regexpu-core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", @@ -13184,6 +14013,25 @@ "node": ">=8" } }, + "node_modules/string.prototype.matchall": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", + "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -13715,6 +14563,39 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -14261,6 +15142,12 @@ "uuid": "bin/uuid" } }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -15107,6 +15994,15 @@ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/worker-rpc": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", @@ -17518,6 +18414,61 @@ "tslib": "^2.1.0" } }, + "@eslint/eslintrc": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.0.5.tgz", + "integrity": "sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.2.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, "@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -17531,6 +18482,23 @@ "@hapi/hoek": "^9.0.0" } }, + "@humanwhocodes/config-array": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.3.tgz", + "integrity": "sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@mdx-js/mdx": { "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", @@ -17935,39 +18903,134 @@ "integrity": "sha512-RNSXOyb3VyRs/EOGmjBhhGKTbnN6fHWvy5FNLzWfOWOGjgVUKqJZXfpKzLmgoU8h6Hj8mpALj/mbXQASOb92wQ==", "dev": true, "requires": { - "@types/history": "*", - "@types/react": "*" + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.1.tgz", + "integrity": "sha512-UvyRy73318QI83haXlaMwmklHHzV9hjl3u71MmM6wYNu0hOVk9NLTa0vGukf8zXUqnwz4O06ig876YSPpeK28A==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/sax": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.3.tgz", + "integrity": "sha512-+QSw6Tqvs/KQpZX8DvIl3hZSjNFLW/OqE5nlyHXtTwODaJvioN2rOWpBNEWZp2HZUFhOh+VohmJku/WxEXU2XA==", + "requires": { + "@types/node": "*" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.11.0.tgz", + "integrity": "sha512-HJh33bgzXe6jGRocOj4FmefD7hRY4itgjzOrSs3JPrTNXsX7j5+nQPciAUj/1nZtwo2kAc3C75jZO+T23gzSGw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/type-utils": "5.11.0", + "@typescript-eslint/utils": "5.11.0", + "debug": "^4.3.2", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.2.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.11.0.tgz", + "integrity": "sha512-x0DCjetHZYBRovJdr3U0zG9OOdNXUaFLJ82ehr1AlkArljJuwEsgnud+Q7umlGDFLFrs8tU8ybQDFocp/eX8mQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/typescript-estree": "5.11.0", + "debug": "^4.3.2" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.11.0.tgz", + "integrity": "sha512-z+K4LlahDFVMww20t/0zcA7gq/NgOawaLuxgqGRVKS0PiZlCTIUtX0EJbC0BK1JtR4CelmkPK67zuCgpdlF4EA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.11.0.tgz", + "integrity": "sha512-wDqdsYO6ofLaD4DsGZ0jGwxp4HrzD2YKulpEZXmgN3xo4BHJwf7kq49JTRpV0Gx6bxkSUmc9s0EIK1xPbFFpIA==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.11.0", + "debug": "^4.3.2", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.11.0.tgz", + "integrity": "sha512-cxgBFGSRCoBEhvSVLkKw39+kMzUKHlJGVwwMbPcTZX3qEhuXhrjwaZXWMxVfxDgyMm+b5Q5b29Llo2yow8Y7xQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.11.0.tgz", + "integrity": "sha512-yVH9hKIv3ZN3lw8m/Jy5I4oXO4ZBMqijcXCdA4mY8ull6TPTAoQnKKrcZ0HDXg7Bsl0Unwwx7jcXMuNZc0m4lg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/visitor-keys": "5.11.0", + "debug": "^4.3.2", + "globby": "^11.0.4", + "is-glob": "^4.0.3", + "semver": "^7.3.5", + "tsutils": "^3.21.0" } }, - "@types/react-router-dom": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.1.tgz", - "integrity": "sha512-UvyRy73318QI83haXlaMwmklHHzV9hjl3u71MmM6wYNu0hOVk9NLTa0vGukf8zXUqnwz4O06ig876YSPpeK28A==", + "@typescript-eslint/utils": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.11.0.tgz", + "integrity": "sha512-g2I480tFE1iYRDyMhxPAtLQ9HAn0jjBtipgTCZmd9I9s11OV8CTsG+YfFciuNDcHqm4csbAgC2aVZCHzLxMSUw==", "dev": true, "requires": { - "@types/history": "*", - "@types/react": "*", - "@types/react-router": "*" + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.11.0", + "@typescript-eslint/types": "5.11.0", + "@typescript-eslint/typescript-estree": "5.11.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" } }, - "@types/sax": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.3.tgz", - "integrity": "sha512-+QSw6Tqvs/KQpZX8DvIl3hZSjNFLW/OqE5nlyHXtTwODaJvioN2rOWpBNEWZp2HZUFhOh+VohmJku/WxEXU2XA==", + "@typescript-eslint/visitor-keys": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.11.0.tgz", + "integrity": "sha512-E8w/vJReMGuloGxJDkpPlGwhxocxOpSVgSvjiLO5IxZPmxZF30weOeJYyPSEACwM+X4NziYS9q+WkN/2DHYQwA==", + "dev": true, "requires": { - "@types/node": "*" + "@typescript-eslint/types": "5.11.0", + "eslint-visitor-keys": "^3.0.0" } }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "@types/unist": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" - }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -18119,9 +19182,9 @@ } }, "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" }, "acorn-import-assertions": { "version": "1.8.0", @@ -18129,6 +19192,13 @@ "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", "requires": {} }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -18300,6 +19370,19 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -18315,6 +19398,17 @@ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, + "array.prototype.flatmap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", + "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + } + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -19627,6 +20721,12 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", @@ -19779,6 +20879,15 @@ "buffer-indexof": "^1.0.0" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -19982,6 +21091,181 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, + "eslint": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.8.0.tgz", + "integrity": "sha512-H3KXAzQGBH1plhYS3okDix2ZthuYJlQQEGE5k0IKuEqUSiyu4AmxxlJ2MtTYeJ3xB4jDhcYCwGOg2TXYdnDXlQ==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.0.5", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.0", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.2.0", + "espree": "^9.3.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.0.tgz", + "integrity": "sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.12.1", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.12.1.tgz", + "integrity": "sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "eslint-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "requires": {} + }, + "eslint-config-prettier": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", + "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", + "dev": true, + "requires": {} + }, + "eslint-plugin-react": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz", + "integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flatmap": "^1.2.5", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.0", + "object.values": "^1.1.5", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.6" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -19991,11 +21275,62 @@ "estraverse": "^4.1.1" } }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz", + "integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==", + "dev": true + }, + "espree": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.0.tgz", + "integrity": "sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.1.0" + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, "esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -20321,6 +21656,12 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", @@ -20395,6 +21736,15 @@ } } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "file-loader": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", @@ -20483,6 +21833,22 @@ "path-exists": "^4.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, "follow-redirects": { "version": "1.14.4", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", @@ -20716,6 +22082,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -21858,6 +23230,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json3": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", @@ -21880,6 +23258,16 @@ "universalify": "^2.0.0" } }, + "jsx-ast-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "object.assign": "^4.1.2" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -21921,6 +23309,16 @@ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "lilconfig": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", @@ -22406,6 +23804,12 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -22605,6 +24009,28 @@ "object-keys": "^1.1.1" } }, + "object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "object.getownpropertydescriptors": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", @@ -22615,6 +24041,16 @@ "es-abstract": "^1.19.1" } }, + "object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -22696,6 +24132,20 @@ } } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -23441,11 +24891,23 @@ "integrity": "sha512-nwgtJJys+XmmSGoYCcgkf/VczP8Mp/0OfSv3v0+fw0uABY4yxw+eFs0Xp9nAZHIKnS5j+e9ywQ+RD+ONyvl5pA==", "requires": {} }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, "pretty-error": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-3.0.4.tgz", @@ -23904,6 +25366,12 @@ "define-properties": "^1.1.3" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, "regexpu-core": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", @@ -24360,6 +25828,7 @@ "chalk": "^4.1.0", "find-up": "^5.0.0", "mkdirp": "^1.0.4", + "postcss": "^8.2.4", "strip-json-comments": "^3.1.1" }, "dependencies": { @@ -25201,6 +26670,22 @@ "strip-ansi": "^6.0.1" } }, + "string.prototype.matchall": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", + "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + } + }, "string.prototype.trimend": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", @@ -25593,6 +27078,32 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -25980,6 +27491,12 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -26626,6 +28143,12 @@ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "worker-rpc": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", diff --git a/docs/package.json b/docs/package.json index 591a7d59..9fa235df 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,7 +9,8 @@ "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "serve": "docusaurus serve", - "clear": "docusaurus clear" + "clear": "docusaurus clear", + "format": "prettier --write ." }, "dependencies": { "@docusaurus/core": "^2.0.0-beta.6", @@ -42,7 +43,14 @@ "@types/react": "^17.0.2", "@types/react-helmet": "^6.1.0", "@types/react-router-dom": "^5.1.7", + "@typescript-eslint/eslint-plugin": "^5.11.0", + "@typescript-eslint/parser": "^5.11.0", + "eslint": "^8.8.0", + "eslint-config-google": "^0.14.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-react": "^7.28.0", "http-proxy-middleware": "^1.0.6", + "prettier": "2.5.1", "typescript": "^4.1.5" } } diff --git a/docs/sidebars.js b/docs/sidebars.js index 9d1d32cc..f8173bfb 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -1,10 +1,33 @@ module.exports = { - docsSidebar: { - "Getting Started": ['getting_started/synth', 'getting_started/installation', 'getting_started/hello-world', 'getting_started/core-concepts', 'getting_started/schema', 'getting_started/command-line', 'getting_started/how-it-works'], - "Examples": ['examples/bank'], - "Tutorials": ['tutorials/creating-logs-with-synth'], - "Integrations": ['integrations/postgres'], - "Generators": ['content/index', 'content/modifiers', 'content/null', 'content/bool', 'content/number', 'content/string', 'content/date-time', 'content/object', 'content/array', 'content/one-of', 'content/same-as', 'content/unique', 'content/series', 'content/datasource'], - "Other": ['other/telemetry'] - }, + docsSidebar: { + 'Getting Started': [ + 'getting_started/synth', + 'getting_started/installation', + 'getting_started/hello-world', + 'getting_started/core-concepts', + 'getting_started/schema', + 'getting_started/command-line', + 'getting_started/how-it-works', + ], + Examples: ['examples/bank'], + Tutorials: ['tutorials/creating-logs-with-synth'], + Integrations: ['integrations/postgres'], + Generators: [ + 'content/index', + 'content/modifiers', + 'content/null', + 'content/bool', + 'content/number', + 'content/string', + 'content/date-time', + 'content/object', + 'content/array', + 'content/one-of', + 'content/same-as', + 'content/unique', + 'content/series', + 'content/datasource', + ], + Other: ['other/telemetry'], + }, }; diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 0c570776..69f7924b 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -65,14 +65,14 @@ div.footer { div[class^='announcementBar_'] { --site-announcement-bar-stripe-color1: hsl( - var(--site-primary-hue-saturation), - 85% + var(--site-primary-hue-saturation), + 85% ); --site-announcement-bar-stripe-color2: hsl( - var(--site-primary-hue-saturation), - 95% + var(--site-primary-hue-saturation), + 95% ); - background-color: #20BEB6; + background-color: #20beb6; color: rgb(253, 253, 253); font-weight: bold; } @@ -87,5 +87,4 @@ a[class^='footerLogoLink'] { } .navbar { - -} \ No newline at end of file +} diff --git a/docs/src/lib/fathom.js b/docs/src/lib/fathom.js index 5cfdf4a8..8e809b53 100644 --- a/docs/src/lib/fathom.js +++ b/docs/src/lib/fathom.js @@ -1,45 +1,45 @@ module.exports = function (context) { - const { siteConfig } = context; - const { themeConfig } = siteConfig; - const { fathomAnalytics } = themeConfig || {}; + const { siteConfig } = context; + const { themeConfig } = siteConfig; + const { fathomAnalytics } = themeConfig || {}; - if (!fathomAnalytics) { - throw new Error( - `You need to specify 'fathomAnalytics' object in 'themeConfig' with 'siteId' field in it to use docusaurus-plugin-fathom` - ); - } + if (!fathomAnalytics) { + throw new Error( + `You need to specify 'fathomAnalytics' object in 'themeConfig' with 'siteId' field in it to use docusaurus-plugin-fathom` + ); + } - let { siteId, customDomain = 'https://cdn.usefathom.com' } = fathomAnalytics; + let { siteId, customDomain = 'https://cdn.usefathom.com' } = fathomAnalytics; - if (!siteId) { - throw new Error( - `You specified the 'fathomAnalytics' object in 'themeConfig' but the 'siteId' field was missing. Please ensure this is not a mistake.` - ); - } + if (!siteId) { + throw new Error( + `You specified the 'fathomAnalytics' object in 'themeConfig' but the 'siteId' field was missing. Please ensure this is not a mistake.` + ); + } - const isProd = process.env.NODE_ENV === 'production'; + const isProd = process.env.NODE_ENV === 'production'; - return { - name: 'docusaurus-plugin-fathom', + return { + name: 'docusaurus-plugin-fathom', - injectHtmlTags() { - if (!isProd) { - return {}; - } + injectHtmlTags() { + if (!isProd) { + return {}; + } - return { - headTags: [ - { - tagName: 'script', - attributes: { - defer: true, - src: `${customDomain}/script.js`, - spa: 'auto', - site: siteId, - }, - }, - ], - }; - }, - }; -}; \ No newline at end of file + return { + headTags: [ + { + tagName: 'script', + attributes: { + defer: true, + src: `${customDomain}/script.js`, + spa: 'auto', + site: siteId, + }, + }, + ], + }; + }, + }; +}; diff --git a/docs/src/lib/playground.ts b/docs/src/lib/playground.ts index 4da51e33..be442d26 100644 --- a/docs/src/lib/playground.ts +++ b/docs/src/lib/playground.ts @@ -1,60 +1,66 @@ type ErrorResponse = { - status?: number, - kind?: string, - text?: string -} + status?: number; + kind?: string; + text?: string; +}; class PlaygroundError extends Error { - response?: ErrorResponse; + response?: ErrorResponse; - constructor(response?: ErrorResponse) { - if (response === undefined) { - super(`undefined playground error`) - } else { - super(response.text); - this.response = response; - } + constructor(response?: ErrorResponse) { + if (response === undefined) { + super(`undefined playground error`); + } else { + super(response.text); + this.response = response; } + } } const pgGenerate = async function ( - req: any, - size: number | null = null, - baseUrl: string = "https://dev.getsynth.com" + req: any, + size: number | null = null, + baseUrl: string = 'https://dev.getsynth.com' ): Promise { - const params = { - method: "PUT", - body: req, - headers: { - "Content-Type": "application/json" + const params = { + method: 'PUT', + body: req, + headers: { + 'Content-Type': 'application/json', + }, + }; + const query = size === null ? '' : `?size=${size}`; + const url = `${baseUrl}/playground${query}`; + return fetch(url, params) + .then((response) => { + if (response.status != 200) { + if (response.headers.get('Content-Type') == 'application/json') { + return response.json().then((err) => + Promise.reject( + new PlaygroundError({ + status: response.status, + kind: err['kind'], + text: err['text'], + }) + ) + ); + } else { + return Promise.reject( + new PlaygroundError({ status: response.status }) + ); } - }; - const query = size === null ? "" : `?size=${size}`; - const url = `${baseUrl}/playground${query}`; - return fetch(url, params) - .then((response) => { - if (response.status != 200) { - if (response.headers.get("Content-Type") == "application/json") { - return response - .json() - .then((err) => Promise.reject(new PlaygroundError({ - status: response.status, - kind: err["kind"], - text: err["text"] - }))) - } else { - return Promise.reject(new PlaygroundError({status: response.status})) - } - } else { - return response.json(); - } - }) - .catch((err) => { - return Promise.reject(new PlaygroundError({ - kind: "Network", - text: err.toString() - })) + } else { + return response.json(); + } + }) + .catch((err) => { + return Promise.reject( + new PlaygroundError({ + kind: 'Network', + text: err.toString(), }) -} + ); + }); +}; -export {PlaygroundError, pgGenerate}; \ No newline at end of file +export { PlaygroundError, pgGenerate }; diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 724fd6df..376d6a16 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import Head from '@docusaurus/Head'; import { Redirect } from '@docusaurus/router'; -import useBaseUrl from "@docusaurus/useBaseUrl"; +import useBaseUrl from '@docusaurus/useBaseUrl'; const Home = () => { - return ; + return ; }; export default Home; diff --git a/docs/src/theme/CodeBlock/index.tsx b/docs/src/theme/CodeBlock/index.tsx index 60b3b1c8..fb46e039 100644 --- a/docs/src/theme/CodeBlock/index.tsx +++ b/docs/src/theme/CodeBlock/index.tsx @@ -5,19 +5,19 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useEffect, useState, useRef} from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import clsx from 'clsx'; -import Highlight, {defaultProps, Language} from 'prism-react-renderer'; +import Highlight, { defaultProps, Language } from 'prism-react-renderer'; import copy from 'copy-text-to-clipboard'; import rangeParser from 'parse-numeric-range'; import usePrismTheme from '@theme/hooks/usePrismTheme'; -import type {Props} from '@theme/CodeBlock'; -import Translate, {translate} from '@docusaurus/Translate'; -import PlaygroundBlock from "../PlaygroundBlock"; +import type { Props } from '@theme/CodeBlock'; +import Translate, { translate } from '@docusaurus/Translate'; +import PlaygroundBlock from '../PlaygroundBlock'; import styles from './styles.module.css'; -import {useThemeConfig, parseCodeBlockTitle} from '@docusaurus/theme-common'; +import { useThemeConfig, parseCodeBlockTitle } from '@docusaurus/theme-common'; const HighlightLinesRangeRegex = /{([\d,-]+)}/; @@ -25,291 +25,314 @@ const HighlightLanguages = ['js', 'jsBlock', 'jsx', 'python', 'html'] as const; type HighlightLanguage = typeof HighlightLanguages[number]; type HighlightLanguageConfig = { - start: string; - end: string; + start: string; + end: string; }; // Supported types of highlight comments const HighlightComments: Record = { - js: { - start: '\\/\\/', - end: '', - }, - jsBlock: { - start: '\\/\\*', - end: '\\*\\/', - }, - jsx: { - start: '\\{\\s*\\/\\*', - end: '\\*\\/\\s*\\}', - }, - python: { - start: '#', - end: '', - }, - html: { - start: '', - }, + js: { + start: '\\/\\/', + end: '', + }, + jsBlock: { + start: '\\/\\*', + end: '\\*\\/', + }, + jsx: { + start: '\\{\\s*\\/\\*', + end: '\\*\\/\\s*\\}', + }, + python: { + start: '#', + end: '', + }, + html: { + start: '', + }, }; // Supported highlight directives const HighlightDirectives = [ - 'highlight-next-line', - 'highlight-start', - 'highlight-end', + 'highlight-next-line', + 'highlight-start', + 'highlight-end', ]; const getHighlightDirectiveRegex = ( - languages: readonly HighlightLanguage[] = HighlightLanguages, + languages: readonly HighlightLanguage[] = HighlightLanguages ) => { - // to be more reliable, the opening and closing comment must match - const commentPattern = languages - .map((lang) => { - const {start, end} = HighlightComments[lang]; - return `(?:${start}\\s*(${HighlightDirectives.join('|')})\\s*${end})`; - }) - .join('|'); - // white space is allowed, but otherwise it should be on it's own line - return new RegExp(`^\\s*(?:${commentPattern})\\s*$`); + // to be more reliable, the opening and closing comment must match + const commentPattern = languages + .map((lang) => { + const { start, end } = HighlightComments[lang]; + return `(?:${start}\\s*(${HighlightDirectives.join('|')})\\s*${end})`; + }) + .join('|'); + // white space is allowed, but otherwise it should be on it's own line + return new RegExp(`^\\s*(?:${commentPattern})\\s*$`); }; // select comment styles based on language const highlightDirectiveRegex = (lang: string) => { - switch (lang) { - case 'js': - case 'javascript': - case 'ts': - case 'typescript': - return getHighlightDirectiveRegex(['js', 'jsBlock']); - - case 'jsx': - case 'tsx': - return getHighlightDirectiveRegex(['js', 'jsBlock', 'jsx']); - - case 'html': - return getHighlightDirectiveRegex(['js', 'jsBlock', 'html']); - - case 'python': - case 'py': - return getHighlightDirectiveRegex(['python']); - - default: - // all comment types - return getHighlightDirectiveRegex(); - } + switch (lang) { + case 'js': + case 'javascript': + case 'ts': + case 'typescript': + return getHighlightDirectiveRegex(['js', 'jsBlock']); + + case 'jsx': + case 'tsx': + return getHighlightDirectiveRegex(['js', 'jsBlock', 'jsx']); + + case 'html': + return getHighlightDirectiveRegex(['js', 'jsBlock', 'html']); + + case 'python': + case 'py': + return getHighlightDirectiveRegex(['python']); + + default: + // all comment types + return getHighlightDirectiveRegex(); + } }; export default function CodeBlock({ - children, - className: languageClassName, - metastring, - title, - isResult = false - }: Props): JSX.Element { - const {prism} = useThemeConfig(); - - const [showCopied, setShowCopied] = useState(false); - const [mounted, setMounted] = useState(false); - const [showResult, setShowResult] = useState(0); - // The Prism theme on SSR is always the default theme but the site theme - // can be in a different mode. React hydration doesn't update DOM styles - // that come from SSR. Hence force a re-render after mounting to apply the - // current relevant styles. There will be a flash seen of the original - // styles seen using this current approach but that's probably ok. Fixing - // the flash will require changing the theming approach and is not worth it - // at this point. - useEffect(() => { - setMounted(true); - }, []); - - // TODO: the title is provided by MDX as props automatically - // so we probably don't need to parse the metastring - // (note: title="xyz" => title prop still has the quotes) - const codeBlockTitle = parseCodeBlockTitle(metastring) || title; - - const button = useRef(null); - let highlightLines: number[] = []; - - const runButton = useRef(null); - - const prismTheme = usePrismTheme(); - - // In case interleaved Markdown (e.g. when using CodeBlock as standalone component). - const content = Array.isArray(children) - ? children.join('') - : (children as string); - - if (metastring && HighlightLinesRangeRegex.test(metastring)) { - // Tested above - const highlightLinesRange = metastring.match(HighlightLinesRangeRegex)![1]; - highlightLines = rangeParser(highlightLinesRange).filter((n) => n > 0); - } - - let language = languageClassName?.replace(/language-/, '') as Language; - - if (!language && prism.defaultLanguage) { - language = prism.defaultLanguage as Language; - } - - // only declaration OR directive highlight can be used for a block - let code = content.replace(/\n$/, ''); - if (highlightLines.length === 0 && language !== undefined) { - let range = ''; - const directiveRegex = highlightDirectiveRegex(language); - // go through line by line - const lines = content.replace(/\n$/, '').split('\n'); - let blockStart: number; - // loop through lines - for (let index = 0; index < lines.length;) { - const line = lines[index]; - // adjust for 0-index - const lineNumber = index + 1; - const match = line.match(directiveRegex); - if (match !== null) { - const directive = match - .slice(1) - .reduce( - (final: string | undefined, item) => final || item, - undefined, - ); - switch (directive) { - case 'highlight-next-line': - range += `${lineNumber},`; - break; - - case 'highlight-start': - blockStart = lineNumber; - break; - - case 'highlight-end': - range += `${blockStart!}-${lineNumber - 1},`; - break; - - default: - break; - } - lines.splice(index, 1); - } else { - // lines without directives are unchanged - index += 1; - } + children, + className: languageClassName, + metastring, + title, + isResult = false, +}: Props): JSX.Element { + const { prism } = useThemeConfig(); + + const [showCopied, setShowCopied] = useState(false); + const [mounted, setMounted] = useState(false); + const [showResult, setShowResult] = useState(0); + // The Prism theme on SSR is always the default theme but the site theme + // can be in a different mode. React hydration doesn't update DOM styles + // that come from SSR. Hence force a re-render after mounting to apply the + // current relevant styles. There will be a flash seen of the original + // styles seen using this current approach but that's probably ok. Fixing + // the flash will require changing the theming approach and is not worth it + // at this point. + useEffect(() => { + setMounted(true); + }, []); + + // TODO: the title is provided by MDX as props automatically + // so we probably don't need to parse the metastring + // (note: title="xyz" => title prop still has the quotes) + const codeBlockTitle = parseCodeBlockTitle(metastring) || title; + + const button = useRef(null); + let highlightLines: number[] = []; + + const runButton = useRef(null); + + const prismTheme = usePrismTheme(); + + // In case interleaved Markdown (e.g. when using CodeBlock as standalone component). + const content = Array.isArray(children) + ? children.join('') + : (children as string); + + if (metastring && HighlightLinesRangeRegex.test(metastring)) { + // Tested above + const highlightLinesRange = metastring.match(HighlightLinesRangeRegex)![1]; + highlightLines = rangeParser(highlightLinesRange).filter((n) => n > 0); + } + + let language = languageClassName?.replace(/language-/, '') as Language; + + if (!language && prism.defaultLanguage) { + language = prism.defaultLanguage as Language; + } + + // only declaration OR directive highlight can be used for a block + let code = content.replace(/\n$/, ''); + if (highlightLines.length === 0 && language !== undefined) { + let range = ''; + const directiveRegex = highlightDirectiveRegex(language); + // go through line by line + const lines = content.replace(/\n$/, '').split('\n'); + let blockStart: number; + // loop through lines + for (let index = 0; index < lines.length; ) { + const line = lines[index]; + // adjust for 0-index + const lineNumber = index + 1; + const match = line.match(directiveRegex); + if (match !== null) { + const directive = match + .slice(1) + .reduce( + (final: string | undefined, item) => final || item, + undefined + ); + switch (directive) { + case 'highlight-next-line': + range += `${lineNumber},`; + break; + + case 'highlight-start': + blockStart = lineNumber; + break; + + case 'highlight-end': + range += `${blockStart!}-${lineNumber - 1},`; + break; + + default: + break; } - highlightLines = rangeParser(range); - code = lines.join('\n'); + lines.splice(index, 1); + } else { + // lines without directives are unchanged + index += 1; + } } - - const codeWithoutComments = code.replace(/\/\/.*/g, ''); - - const handleCopyCode = () => { - copy(code); - setShowCopied(true); - - setTimeout(() => setShowCopied(false), 2000); - }; - - const handleShowResult = () => { - setShowResult(showResult + 1); - }; - - return ( - <> - - {({className, style, tokens, getLineProps, getTokenProps}) => ( -
- {codeBlockTitle && ( -
- {codeBlockTitle} -
- )} -
-
-                              
-                                {tokens.map((line, i) => {
-                                    if (line.length === 1 && line[0].content === '') {
-                                        line[0].content = '\n'; // eslint-disable-line no-param-reassign
-                                    }
-
-                                    const lineProps = getLineProps({line, key: i});
-
-                                    if (highlightLines.includes(i + 1)) {
-                                        lineProps.className += ' docusaurus-highlight-code-line';
-                                    }
-
-                                    return (
-                                        
-                                      {line.map((token, key) => (
-                                          
-                                      ))}
-                                    
-                                    );
-                                })}
-                              
-                            
-
- { - !isResult && - } - { - (!isResult && metastring == "synth") && - - } - { - isResult && - - Output - - } -
-
- { - (showResult != 0) && - - } -
+ highlightLines = rangeParser(range); + code = lines.join('\n'); + } + + const codeWithoutComments = code.replace(/\/\/.*/g, ''); + + const handleCopyCode = () => { + copy(code); + setShowCopied(true); + + setTimeout(() => setShowCopied(false), 2000); + }; + + const handleShowResult = () => { + setShowResult(showResult + 1); + }; + + return ( + <> + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+ {codeBlockTitle && ( +
+ {codeBlockTitle} +
+ )} +
+
+                
+                  {tokens.map((line, i) => {
+                    if (line.length === 1 && line[0].content === '') {
+                      line[0].content = '\n'; // eslint-disable-line no-param-reassign
+                    }
+
+                    const lineProps = getLineProps({ line, key: i });
+
+                    if (highlightLines.includes(i + 1)) {
+                      lineProps.className += ' docusaurus-highlight-code-line';
+                    }
+
+                    return (
+                      
+                        {line.map((token, key) => (
+                          
+                        ))}
+                      
+                    );
+                  })}
+                
+              
+
+ {!isResult && ( + + )} + {!isResult && metastring == 'synth' && ( + + )} + {isResult && ( + + Output + )} - - - ); +
+
+ {showResult != 0 && ( + + )} +
+ )} +
+ + ); } diff --git a/docs/src/theme/PlaygroundBlock/index.tsx b/docs/src/theme/PlaygroundBlock/index.tsx index 3cbc0890..0e4b7112 100644 --- a/docs/src/theme/PlaygroundBlock/index.tsx +++ b/docs/src/theme/PlaygroundBlock/index.tsx @@ -1,85 +1,78 @@ -import React, {useState, useEffect} from 'react'; +import React, { useState, useEffect } from 'react'; -import CodeBlock from '@theme/CodeBlock' +import CodeBlock from '@theme/CodeBlock'; -import {PlaygroundError, pgGenerate} from '../../lib/playground'; +import { PlaygroundError, pgGenerate } from '../../lib/playground'; type Querying = { - step: "querying" + step: 'querying'; }; -const Querying: Querying = {step: "querying"}; +const Querying: Querying = { step: 'querying' }; type Failed = { - step: "failed", - error: PlaygroundError + step: 'failed'; + error: PlaygroundError; }; const Failed = (error: PlaygroundError): Failed => { - return { - step: "failed", - error - } -} + return { + step: 'failed', + error, + }; +}; type Ok = { - step: "ok", - generated: any + step: 'ok'; + generated: any; }; const Ok = (generated: any): Ok => { - return { - step: "ok", - generated - } -} + return { + step: 'ok', + generated, + }; +}; type PlaygroundState = Querying | Failed | Ok; type PlaygroundProps = { - schema: any, - size?: number, - seed: number -} + schema: any; + size?: number; + seed: number; +}; -const PlaygroundBlock = ({schema, size, seed}: PlaygroundProps) => { - let [state, setState] = useState(Querying); - let [seedState, setSeedState] = useState(null); +const PlaygroundBlock = ({ schema, size, seed }: PlaygroundProps) => { + let [state, setState] = useState(Querying); + let [seedState, setSeedState] = useState(null); - useEffect(() => { - if (seedState != seed) { - setSeedState(seed); - setState(Querying); - } - if (state.step === "querying") { - const baseUrl = process.env.NODE_ENV === "development" - ? "http://localhost:8182" - : "https://dev.getsynth.com"; - pgGenerate(schema, size, baseUrl) - .then((generated) => { - setState(Ok(generated)); - }) - .catch((err: PlaygroundError) => { - setState(Failed(err)); - }) - } - }); + useEffect(() => { + if (seedState != seed) { + setSeedState(seed); + setState(Querying); + } + if (state.step === 'querying') { + const baseUrl = + process.env.NODE_ENV === 'development' + ? 'http://localhost:8182' + : 'https://dev.getsynth.com'; + pgGenerate(schema, size, baseUrl) + .then((generated) => { + setState(Ok(generated)); + }) + .catch((err: PlaygroundError) => { + setState(Failed(err)); + }); + } + }); - return ( - - { - state.step == "querying" - && 'Generating...' - || ( - state.step == "ok" - && JSON.stringify(state.generated, null, 2) - ) || ( - state.step == "failed" - && `${state.error}` - ) - } - - ); -} + return ( + + {(state.step == 'querying' && 'Generating...') || + (state.step == 'ok' && JSON.stringify(state.generated, null, 2)) || + (state.step == 'failed' && `${state.error}`)} + + ); +}; -export default PlaygroundBlock; \ No newline at end of file +export default PlaygroundBlock; diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 77c5f752..4360f0da 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "@tsconfig/docusaurus/tsconfig.json", "include": ["src/"] -} \ No newline at end of file +} diff --git a/docs/vercel.json b/docs/vercel.json index 0b587ac3..7dd8431b 100644 --- a/docs/vercel.json +++ b/docs/vercel.json @@ -5,4 +5,4 @@ "destination": "/:match*" } ] -} \ No newline at end of file +} diff --git a/www/.editorconfig b/www/.editorconfig new file mode 100644 index 00000000..ebe51d3b --- /dev/null +++ b/www/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/www/.eslintrc.js b/www/.eslintrc.js new file mode 100644 index 00000000..0b7ac358 --- /dev/null +++ b/www/.eslintrc.js @@ -0,0 +1,20 @@ +module.exports = { + env: { + browser: true, + es2021: true, + }, + extends: ['plugin:react/recommended', 'google', 'prettier'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + sourceType: 'module', + }, + plugins: ['react', '@typescript-eslint'], + rules: {}, + react: { + version: 'latest', + }, +} diff --git a/www/.gitignore b/www/.gitignore index e985853e..4e0e1925 100644 --- a/www/.gitignore +++ b/www/.gitignore @@ -1 +1,3 @@ .vercel +.next +node_modules \ No newline at end of file diff --git a/www/.prettierrc b/www/.prettierrc new file mode 100644 index 00000000..65b5c708 --- /dev/null +++ b/www/.prettierrc @@ -0,0 +1,7 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "trailingComma": "es5", + "singleQuote": true, + "semi": false +} diff --git a/www/README.md b/www/README.md index 8a4279e4..227d5ab8 100644 --- a/www/README.md +++ b/www/README.md @@ -43,4 +43,4 @@ in the root `www` folder will deploy to a test deployment, and: $ vercel --prod ``` -will deploy to production and override the current `getsynth.com` page. \ No newline at end of file +will deploy to production and override the current `getsynth.com` page. diff --git a/www/components/AccentButton.tsx b/www/components/AccentButton.tsx index 35d6f0ee..9a10e19f 100644 --- a/www/components/AccentButton.tsx +++ b/www/components/AccentButton.tsx @@ -1,36 +1,47 @@ -import React, {ReactNode} from 'react'; +import React, { ReactNode } from 'react' -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' -import {IconProp} from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { IconProp } from '@fortawesome/fontawesome-svg-core' import Link from 'next/link' type AccentButtonProps = { - link?: string, - compact?: boolean, - className?: string, - children: ReactNode -}; - -const AccentButton = ({link, className, compact, children}: AccentButtonProps) => { - const paddingy = compact ? "pt-1 pb-1" : "pt-2 pb-2"; - - const classNameEval = className === undefined ? `text-white bg-brand-600 hover:bg-brand-400 font-bold ${paddingy} pr-3 pl-3 text-sm` : className; - - const button = ; - - if (link === undefined) { - return button; - } else { - return - - {button} - - - } -}; - -export default AccentButton; \ No newline at end of file + link?: string + compact?: boolean + className?: string + children: ReactNode +} + +const AccentButton = ({ + link, + className, + compact, + children, +}: AccentButtonProps) => { + const paddingy = compact ? 'pt-1 pb-1' : 'pt-2 pb-2' + + const classNameEval = + className === undefined + ? `text-white bg-brand-600 hover:bg-brand-400 font-bold ${paddingy} pr-3 pl-3 text-sm` + : className + + const button = ( + + ) + + if (link === undefined) { + return button + } else { + return ( + + {button} + + ) + } +} + +export default AccentButton diff --git a/www/components/AnnouncementBar/index.tsx b/www/components/AnnouncementBar/index.tsx index e85534f0..33fa0d19 100644 --- a/www/components/AnnouncementBar/index.tsx +++ b/www/components/AnnouncementBar/index.tsx @@ -1,39 +1,39 @@ -import React, {useState} from "react" +import React, { useState } from 'react' -import styles from "./styles.module.css" +import styles from './styles.module.css' const AnnouncementBar = () => { - const [isClosed, setClosed] = useState(false); + const [isClosed, setClosed] = useState(false) - if (isClosed) { - return null - } + if (isClosed) { + return null + } - return ( -
-

- ⭐️ If you like Synth,  - - give it a star on GitHub - - ! -

+ return ( +
+

+ ⭐️ If you like Synth,  + + give it a star on GitHub + + ! +

- -
- ) + +
+ ) } -export default AnnouncementBar \ No newline at end of file +export default AnnouncementBar diff --git a/www/components/AnnouncementBar/styles.module.css b/www/components/AnnouncementBar/styles.module.css index 702e0003..9e56eec9 100644 --- a/www/components/AnnouncementBar/styles.module.css +++ b/www/components/AnnouncementBar/styles.module.css @@ -1,51 +1,51 @@ .announcement { - position: relative; - width: 100%; - height: 2rem; - background-color: #20BEB6; - color: white; + position: relative; + width: 100%; + height: 2rem; + background-color: #20beb6; + color: white; } .announcement__close { - position: absolute; - display: flex; - right: 0; - top: 0; - height: 100%; - width: 4rem; - padding: 0; - justify-content: center; - align-items: center; - border: none; - outline: none; - background: none; - color: inherit; - font-size: 2rem; - cursor: pointer; + position: absolute; + display: flex; + right: 0; + top: 0; + height: 100%; + width: 4rem; + padding: 0; + justify-content: center; + align-items: center; + border: none; + outline: none; + background: none; + color: inherit; + font-size: 2rem; + cursor: pointer; } .announcement__content { - display: flex; - height: 100%; - width: 100%; - margin: 0; - align-items: center; - justify-content: center; - font-size: 14px; - font-weight: bold; + display: flex; + height: 100%; + width: 100%; + margin: 0; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; } .announcement__link { - color: inherit; - text-decoration: underline; + color: inherit; + text-decoration: underline; } .announcement__link:hover { - color: inherit; + color: inherit; } @media screen and (max-width: 576px) { - .announcement__close { - width: 2.5rem; - } -} \ No newline at end of file + .announcement__close { + width: 2.5rem; + } +} diff --git a/www/components/CallToAction.tsx b/www/components/CallToAction.tsx index 1ec73f28..ed985d17 100644 --- a/www/components/CallToAction.tsx +++ b/www/components/CallToAction.tsx @@ -1,28 +1,35 @@ -import AccentButton from "./AccentButton"; -import {faExternalLinkAlt} from "@fortawesome/free-solid-svg-icons"; -import {useRouter} from "next/router"; +import AccentButton from './AccentButton' +import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons' +import { useRouter } from 'next/router' type CallToActionProps = { - copy?: string + copy?: string } -const CallToAction = ({copy}: CallToActionProps) => { - const {basePath} = useRouter(); - copy = copy == null ? "Synth helps you write better software, faster. Join the community!" : copy; - return ( -
-
-
-
- {copy} -
-
- discord -
-
-
+const CallToAction = ({ copy }: CallToActionProps) => { + const { basePath } = useRouter() + copy = + copy == null + ? 'Synth helps you write better software, faster. Join the community!' + : copy + return ( +
+
+
+
{copy}
+
+ + discord + +
- ) +
+
+ ) } export default CallToAction diff --git a/www/components/Card.tsx b/www/components/Card.tsx index c95f3cc4..d30018be 100644 --- a/www/components/Card.tsx +++ b/www/components/Card.tsx @@ -1,48 +1,42 @@ -import React, {useState} from 'react'; -import {useRouter} from 'next/router'; +import React, { useState } from 'react' +import { useRouter } from 'next/router' -import LearnMore from "./LearnMore"; +import LearnMore from './LearnMore' type CardProps = { - header?: string, - title: string, - imageUrl: string, - copy?: string, - link?: string, + header?: string + title: string + imageUrl: string + copy?: string + link?: string } -const Card = ({header, title, copy, imageUrl, link}: CardProps) => { - const {basePath} = useRouter(); - const [arrow, setArrow] = useState(false); - return ( - -
setArrow(true)} - onMouseLeave={() => setArrow(false)} - > -
- {title}/ -
-
- { - header &&
- {header} -
- } -
- {title} -
- { - copy &&
- {copy} -
- } - -
-
-
- ); +const Card = ({ header, title, copy, imageUrl, link }: CardProps) => { + const { basePath } = useRouter() + const [arrow, setArrow] = useState(false) + return ( + +
setArrow(true)} + onMouseLeave={() => setArrow(false)} + > +
+ {title} +
+
+ {header &&
{header}
} +
{title}
+ {copy &&
{copy}
} + +
+
+
+ ) } -export default Card; \ No newline at end of file +export default Card diff --git a/www/components/CardNoLink.tsx b/www/components/CardNoLink.tsx index 363bbe4f..a0b4d089 100644 --- a/www/components/CardNoLink.tsx +++ b/www/components/CardNoLink.tsx @@ -1,40 +1,30 @@ -import React, {useState} from 'react'; +import React, { useState } from 'react' type CardProps = { - header?: string, - title: string, - copy?: string, - path?: string, + header?: string + title: string + copy?: string + path?: string } -const CardNoLink = ({header, title, copy, path}: CardProps) => { - const [arrow, setArrow] = useState(false); - return ( -
setArrow(true)} - onMouseLeave={() => setArrow(false)} - > -
- -
-
- { - header &&
- {header} -
- } -
- {title} -
- { - copy &&
- {copy} -
- } -
-
- ); +const CardNoLink = ({ header, title, copy, path }: CardProps) => { + const [arrow, setArrow] = useState(false) + return ( +
setArrow(true)} + onMouseLeave={() => setArrow(false)} + > +
+ +
+
+ {header &&
{header}
} +
{title}
+ {copy &&
{copy}
} +
+
+ ) } -export default CardNoLink; \ No newline at end of file +export default CardNoLink diff --git a/www/components/Code.tsx b/www/components/Code.tsx index c313b8fb..12a26642 100644 --- a/www/components/Code.tsx +++ b/www/components/Code.tsx @@ -1,30 +1,40 @@ -import ReactTooltip from 'react-tooltip'; +import ReactTooltip from 'react-tooltip' // Todo should rename this to something more descriptive type CodeProps = { - code: string, - lang?: string + code: string + lang?: string } const copyToClipboard = (code) => { - navigator.clipboard.writeText(code); + navigator.clipboard.writeText(code) } -const Code = ({code, lang}: CodeProps) => { - const language = lang ? lang : "language-javascript"; - return ( -
-
-                    
-                        $ {code}
-                    
-                
- copyToClipboard(code)}> - Copied to clipboard! - -
- ); +const Code = ({ code, lang }: CodeProps) => { + const language = lang ? lang : 'language-javascript' + return ( +
+
+        $ {code}
+      
+ copyToClipboard(code)} + > + Copied to clipboard! + +
+ ) } -export default Code; \ No newline at end of file +export default Code diff --git a/www/components/Contact.tsx b/www/components/Contact.tsx index 0e09232e..ce39177b 100644 --- a/www/components/Contact.tsx +++ b/www/components/Contact.tsx @@ -1,79 +1,109 @@ import Section from './Section' const Contact = () => { - return
-
-
- Drop us a line. -
- + return ( +
+
+
+ Drop us a line.
+ +
+ ) } -import {useForm} from '@formspree/react'; +import { useForm } from '@formspree/react' function MyForm() { - const [state, handleSubmit] = useForm('maypbzgq'); - if (state.succeeded) { - return
Thank you for your message! We'll get back to you shortly.
; - } - // @ts-ignore - return ( -
-
-
- - -
-
- - -
-
-
-
- - -
-
-
-
- -