Full Code of decaporg/decap-cms for AI

main 61d8b8bdc0a8 cached
954 files
43.4 MB
4.1M tokens
23 symbols
1 requests
Copy disabled (too large) Download .txt
Showing preview only (16,397K chars total). Download the full file to get everything.
Repository: decaporg/decap-cms
Branch: main
Commit: 61d8b8bdc0a8
Files: 954
Total size: 43.4 MB

Directory structure:
gitextract_3fklok8x/

├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── .kodiak.toml
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── stale.yml
│   └── workflows/
│       ├── create-release.yml
│       ├── labeler.yml
│       ├── nodejs.yml
│       ├── publish.yml
│       └── sponsors.yml
├── .gitignore
├── .husky/
│   └── commit-msg
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .storybook/
│   └── main.js
├── .stylelintrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── __mocks__/
│   └── styleMock.js
├── babel.config.js
├── commitlint.config.js
├── cypress/
│   ├── Readme.md
│   ├── e2e/
│   │   ├── common/
│   │   │   ├── editorial_workflow.js
│   │   │   ├── editorial_workflow_migrations.js
│   │   │   ├── entries.js
│   │   │   ├── i18n.js
│   │   │   ├── i18n_editorial_workflow_spec.js
│   │   │   ├── media_library.js
│   │   │   ├── open_authoring.js
│   │   │   ├── simple_workflow.js
│   │   │   └── spec_utils.js
│   │   ├── editorial_workflow_spec_bitbucket_backend.js
│   │   ├── editorial_workflow_spec_git-gateway_github_backend.js
│   │   ├── editorial_workflow_spec_git-gateway_gitlab_backend.js
│   │   ├── editorial_workflow_spec_github_backend_graphql.js
│   │   ├── editorial_workflow_spec_github_backend_graphql_open_authoring.js
│   │   ├── editorial_workflow_spec_github_backend_rest.js
│   │   ├── editorial_workflow_spec_github_backend_rest_open_authoring.js
│   │   ├── editorial_workflow_spec_gitlab_backend.js
│   │   ├── editorial_workflow_spec_proxy_git_backend.js
│   │   ├── editorial_workflow_spec_test_backend.js
│   │   ├── field_validations_spec.js
│   │   ├── i18n_editorial_workflow_spec_test_backend.js
│   │   ├── i18n_simple_workflow_spec_proxy_fs_backend.js
│   │   ├── markdown_widget_backspace_spec.js
│   │   ├── markdown_widget_code_block_spec.js
│   │   ├── markdown_widget_enter_spec.js
│   │   ├── markdown_widget_hotkeys_spec.js
│   │   ├── markdown_widget_link_spec.js
│   │   ├── markdown_widget_list_spec.js
│   │   ├── markdown_widget_marks_spec.js
│   │   ├── markdown_widget_quote_spec.js
│   │   ├── media_library_spec_bitbucket_backend.js
│   │   ├── media_library_spec_bitbucket_backend_large_media.js
│   │   ├── media_library_spec_git-gateway_github_backend_large_media.js
│   │   ├── media_library_spec_git-gateway_gitlab_backend_large_media.js
│   │   ├── media_library_spec_github_backend_graphql.js
│   │   ├── media_library_spec_github_backend_rest.js
│   │   ├── media_library_spec_gitlab_backend.js
│   │   ├── media_library_spec_proxy_git_backend.js
│   │   ├── media_library_spec_test_backend.js
│   │   ├── search_suggestion_spec.js
│   │   ├── simple_workflow_spec_bitbucket_backend.js
│   │   ├── simple_workflow_spec_git-gateway_github_backend.js
│   │   ├── simple_workflow_spec_git-gateway_gitlab_backend.js
│   │   ├── simple_workflow_spec_github_backend_graphql.js
│   │   ├── simple_workflow_spec_github_backend_rest.js
│   │   ├── simple_workflow_spec_gitlab_backend.js
│   │   ├── simple_workflow_spec_proxy_fs_backend.js
│   │   ├── simple_workflow_spec_proxy_git_backend.js
│   │   ├── simple_workflow_spec_test_backend.js
│   │   ├── view_filters_spec.js
│   │   └── view_groups_spec.js
│   ├── fixtures/
│   │   ├── BitBucket Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── BitBucket Backend Editorial Workflow__can change workflow status.json
│   │   ├── BitBucket Backend Editorial Workflow__can create an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can delete an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can update an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── BitBucket Backend Editorial Workflow__successfully loads.json
│   │   ├── BitBucket Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── BitBucket Backend Media Library - Large Media__can save entry with image.json
│   │   ├── BitBucket Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── BitBucket Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__can publish entry with image.json
│   │   ├── BitBucket Backend Media Library - REST API__can save entry with image.json
│   │   ├── BitBucket Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── BitBucket Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── BitBucket Backend Simple Workflow__can create an entry.json
│   │   ├── BitBucket Backend Simple Workflow__successfully loads.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can change workflow status.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can create an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can delete an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can update an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__successfully loads.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can save entry with image.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── Git Gateway (GitHub) Backend Simple Workflow__can create an entry.json
│   │   ├── Git Gateway (GitHub) Backend Simple Workflow__successfully loads.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can change workflow status.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can create an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can delete an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can update an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__successfully loads.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can save entry with image.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── Git Gateway (GitLab) Backend Simple Workflow__can create an entry.json
│   │   ├── Git Gateway (GitLab) Backend Simple Workflow__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can change entry status from fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can delete review entry from fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can return entry to draft and delete it.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update a draft entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully forks repository and loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can change status on and publish multiple entries.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can change workflow status.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can delete an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can update workflow status from within the editor.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can change entry status from fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can create an entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can delete review entry from fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can return entry to draft and delete it.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can update a draft entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__successfully forks repository and loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can change status on and publish multiple entries.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can change workflow status.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can delete an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can update workflow status from within the editor.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow Migration - REST API__migrate from 2.10.24 to latest.json
│   │   ├── GitHub Backend Editorial Workflow Migration - REST API__migrate from 2.9.7 to latest.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can delete image from global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can publish entry with image.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can save entry with image.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can upload image from entry media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can upload image from global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should not show draft entry image in global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should show published entry image in global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should show published entry image in grid view.json
│   │   ├── GitHub Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── GitHub Backend Media Library - REST API__can publish entry with image.json
│   │   ├── GitHub Backend Media Library - REST API__can save entry with image.json
│   │   ├── GitHub Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── GitHub Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── GitHub Backend Simple Workflow - GraphQL API__can create an entry.json
│   │   ├── GitHub Backend Simple Workflow - GraphQL API__successfully loads.json
│   │   ├── GitHub Backend Simple Workflow - REST API__can create an entry.json
│   │   ├── GitHub Backend Simple Workflow - REST API__successfully loads.json
│   │   ├── GitHub Backend Simple Workflow__successfully loads.json
│   │   ├── GitLab Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── GitLab Backend Editorial Workflow__can change workflow status.json
│   │   ├── GitLab Backend Editorial Workflow__can create an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can delete an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── GitLab Backend Editorial Workflow__can update an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── GitLab Backend Editorial Workflow__successfully loads.json
│   │   ├── GitLab Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── GitLab Backend Media Library - REST API__can publish entry with image.json
│   │   ├── GitLab Backend Media Library - REST API__can save entry with image.json
│   │   ├── GitLab Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── GitLab Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── GitLab Backend Simple Workflow__can create an entry.json
│   │   ├── GitLab Backend Simple Workflow__successfully loads.json
│   │   └── example.json
│   ├── plugins/
│   │   ├── bitbucket.js
│   │   ├── common.js
│   │   ├── gitGateway.js
│   │   ├── github.js
│   │   ├── gitlab.js
│   │   ├── index.js
│   │   ├── proxy.js
│   │   └── testBackend.js
│   ├── run.mjs
│   ├── support/
│   │   ├── commands.js
│   │   └── e2e.js
│   └── utils/
│       ├── README.md
│       ├── config.js
│       ├── constants.js
│       ├── dismiss-local-backup.js
│       ├── mock-server.js
│       ├── regexp.js
│       └── steps.js
├── cypress.config.ts
├── dev-test/
│   ├── backends/
│   │   ├── azure/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── bitbucket/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── git-gateway/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── gitea/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── github/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── gitlab/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── proxy/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   └── test/
│   │       ├── config.yml
│   │       └── index.html
│   ├── config.yml
│   ├── example.css
│   └── index.html
├── functions/
│   └── publish.js
├── jest.config.js
├── lerna.json
├── netlify.toml
├── nx.json
├── package.json
├── packages/
│   ├── decap-cms/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── shims/
│   │   │   ├── cms.css
│   │   │   └── deprecate-old-dist.js
│   │   ├── src/
│   │   │   ├── extensions.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-app/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── extensions.js
│   │   │   ├── index.js
│   │   │   └── locales.js
│   │   └── webpack.config.js
│   ├── decap-cms-backend-aws-cognito-github-proxy/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.tsx
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-azure/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-bitbucket/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   └── api.spec.js
│   │   │   ├── git-lfs-client.ts
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   └── types/
│   │   │       ├── semaphore.d.ts
│   │   │       └── what-the-diff.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-git-gateway/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── GitHubAPI.ts
│   │   │   ├── GitLabAPI.ts
│   │   │   ├── __tests__/
│   │   │   │   └── GitHubAPI.spec.js
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   ├── netlify-lfs-client.ts
│   │   │   └── types/
│   │   │       └── ini.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-gitea/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   └── implementation.spec.js
│   │   │   ├── implementation.tsx
│   │   │   ├── index.ts
│   │   │   └── types.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-github/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   └── createFragmentTypes.js
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── GraphQLAPI.ts
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   ├── GraphQLAPI.spec.js
│   │   │   │   └── implementation.spec.js
│   │   │   ├── fragmentTypes.js
│   │   │   ├── fragments.ts
│   │   │   ├── implementation.tsx
│   │   │   ├── index.ts
│   │   │   ├── mutations.ts
│   │   │   ├── queries.ts
│   │   │   └── types/
│   │   │       └── semaphore.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-gitlab/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   └── gitlab.spec.js
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   └── queries.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-proxy/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-test/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   └── implementation.spec.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-core/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── backend.spec.js
│   │   │   ├── actions/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── config.spec.js
│   │   │   │   │   ├── editorialWorkflow.spec.js
│   │   │   │   │   ├── entries.spec.js
│   │   │   │   │   ├── media.spec.ts
│   │   │   │   │   ├── mediaLibrary.spec.js
│   │   │   │   │   └── search.spec.js
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collections.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── deploys.ts
│   │   │   │   ├── editorialWorkflow.ts
│   │   │   │   ├── entries.ts
│   │   │   │   ├── media.ts
│   │   │   │   ├── mediaLibrary.ts
│   │   │   │   ├── notifications.ts
│   │   │   │   ├── search.ts
│   │   │   │   ├── status.ts
│   │   │   │   └── waitUntil.ts
│   │   │   ├── backend.ts
│   │   │   ├── bootstrap.js
│   │   │   ├── components/
│   │   │   │   ├── App/
│   │   │   │   │   ├── App.js
│   │   │   │   │   ├── Header.js
│   │   │   │   │   └── NotFoundPage.js
│   │   │   │   ├── Collection/
│   │   │   │   │   ├── Collection.js
│   │   │   │   │   ├── CollectionControls.js
│   │   │   │   │   ├── CollectionSearch.js
│   │   │   │   │   ├── CollectionTop.js
│   │   │   │   │   ├── ControlButton.js
│   │   │   │   │   ├── Entries/
│   │   │   │   │   │   ├── Entries.js
│   │   │   │   │   │   ├── EntriesCollection.js
│   │   │   │   │   │   ├── EntriesSearch.js
│   │   │   │   │   │   ├── EntryCard.js
│   │   │   │   │   │   ├── EntryListing.js
│   │   │   │   │   │   └── __tests__/
│   │   │   │   │   │       ├── EntriesCollection.spec.js
│   │   │   │   │   │       └── __snapshots__/
│   │   │   │   │   │           └── EntriesCollection.spec.js.snap
│   │   │   │   │   ├── FilterControl.js
│   │   │   │   │   ├── GroupControl.js
│   │   │   │   │   ├── NestedCollection.js
│   │   │   │   │   ├── Sidebar.js
│   │   │   │   │   ├── SortControl.js
│   │   │   │   │   ├── ViewStyleControl.js
│   │   │   │   │   └── __tests__/
│   │   │   │   │       ├── Collection.spec.js
│   │   │   │   │       ├── NestedCollection.spec.js
│   │   │   │   │       ├── Sidebar.spec.js
│   │   │   │   │       └── __snapshots__/
│   │   │   │   │           ├── Collection.spec.js.snap
│   │   │   │   │           ├── NestedCollection.spec.js.snap
│   │   │   │   │           └── Sidebar.spec.js.snap
│   │   │   │   ├── Editor/
│   │   │   │   │   ├── Editor.js
│   │   │   │   │   ├── EditorControlPane/
│   │   │   │   │   │   ├── EditorControl.js
│   │   │   │   │   │   ├── EditorControlPane.js
│   │   │   │   │   │   └── Widget.js
│   │   │   │   │   ├── EditorInterface.js
│   │   │   │   │   ├── EditorPreviewPane/
│   │   │   │   │   │   ├── EditorPreview.js
│   │   │   │   │   │   ├── EditorPreviewContent.js
│   │   │   │   │   │   ├── EditorPreviewPane.js
│   │   │   │   │   │   └── PreviewHOC.js
│   │   │   │   │   ├── EditorToolbar.js
│   │   │   │   │   ├── __tests__/
│   │   │   │   │   │   ├── Editor.spec.js
│   │   │   │   │   │   ├── EditorToolbar.spec.js
│   │   │   │   │   │   └── __snapshots__/
│   │   │   │   │   │       ├── Editor.spec.js.snap
│   │   │   │   │   │       └── EditorToolbar.spec.js.snap
│   │   │   │   │   └── withWorkflow.js
│   │   │   │   ├── EditorWidgets/
│   │   │   │   │   ├── Unknown/
│   │   │   │   │   │   ├── UnknownControl.js
│   │   │   │   │   │   └── UnknownPreview.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── MediaLibrary/
│   │   │   │   │   ├── EmptyMessage.js
│   │   │   │   │   ├── MediaLibrary.js
│   │   │   │   │   ├── MediaLibraryButtons.js
│   │   │   │   │   ├── MediaLibraryCard.js
│   │   │   │   │   ├── MediaLibraryCardGrid.js
│   │   │   │   │   ├── MediaLibraryHeader.js
│   │   │   │   │   ├── MediaLibraryModal.js
│   │   │   │   │   ├── MediaLibrarySearch.js
│   │   │   │   │   ├── MediaLibraryTop.js
│   │   │   │   │   └── __tests__/
│   │   │   │   │       ├── MediaLibraryButtons.spec.js
│   │   │   │   │       ├── MediaLibraryCard.spec.js
│   │   │   │   │       └── __snapshots__/
│   │   │   │   │           └── MediaLibraryCard.spec.js.snap
│   │   │   │   ├── UI/
│   │   │   │   │   ├── DragDrop.js
│   │   │   │   │   ├── ErrorBoundary.js
│   │   │   │   │   ├── FileUploadButton.js
│   │   │   │   │   ├── Modal.js
│   │   │   │   │   ├── Notifications.tsx
│   │   │   │   │   ├── SettingsDropdown.js
│   │   │   │   │   ├── __tests__/
│   │   │   │   │   │   └── ErrorBoundary.spec.js
│   │   │   │   │   └── index.js
│   │   │   │   └── Workflow/
│   │   │   │       ├── Workflow.js
│   │   │   │       ├── WorkflowCard.js
│   │   │   │       └── WorkflowList.js
│   │   │   ├── constants/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── configSchema.spec.js
│   │   │   │   ├── collectionTypes.ts
│   │   │   │   ├── collectionViews.js
│   │   │   │   ├── commitProps.ts
│   │   │   │   ├── configSchema.js
│   │   │   │   ├── fieldInference.tsx
│   │   │   │   ├── publishModes.ts
│   │   │   │   └── validationErrorTypes.js
│   │   │   ├── formats/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── formats.spec.js
│   │   │   │   │   ├── frontmatter.spec.js
│   │   │   │   │   ├── toml.spec.js
│   │   │   │   │   └── yaml.spec.js
│   │   │   │   ├── formats.ts
│   │   │   │   ├── frontmatter.ts
│   │   │   │   ├── helpers.ts
│   │   │   │   ├── json.ts
│   │   │   │   ├── toml.ts
│   │   │   │   └── yaml.ts
│   │   │   ├── index.js
│   │   │   ├── integrations/
│   │   │   │   ├── index.js
│   │   │   │   └── providers/
│   │   │   │       ├── algolia/
│   │   │   │       │   └── implementation.js
│   │   │   │       └── assetStore/
│   │   │   │           └── implementation.js
│   │   │   ├── lib/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── formatters.spec.js
│   │   │   │   │   ├── i18n.spec.js
│   │   │   │   │   ├── phrases.spec.js
│   │   │   │   │   ├── registry.spec.js
│   │   │   │   │   ├── serializeEntryValues.spec.js
│   │   │   │   │   └── urlHelper.spec.js
│   │   │   │   ├── consoleError.js
│   │   │   │   ├── formatters.ts
│   │   │   │   ├── i18n.ts
│   │   │   │   ├── phrases.js
│   │   │   │   ├── polyfill.js
│   │   │   │   ├── registry.js
│   │   │   │   ├── serializeEntryValues.js
│   │   │   │   ├── stega.ts
│   │   │   │   ├── textHelper.js
│   │   │   │   └── urlHelper.ts
│   │   │   ├── mediaLibrary.ts
│   │   │   ├── reducers/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── auth.spec.ts
│   │   │   │   │   ├── collections.spec.js
│   │   │   │   │   ├── config.spec.js
│   │   │   │   │   ├── deploys.spec.ts
│   │   │   │   │   ├── entries.spec.js
│   │   │   │   │   ├── entryDraft.spec.js
│   │   │   │   │   ├── globalUI.js
│   │   │   │   │   ├── integrations.spec.ts
│   │   │   │   │   ├── mediaLibrary.spec.js
│   │   │   │   │   └── medias.spec.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collections.ts
│   │   │   │   ├── combinedReducer.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── cursors.js
│   │   │   │   ├── deploys.ts
│   │   │   │   ├── editorialWorkflow.ts
│   │   │   │   ├── entries.ts
│   │   │   │   ├── entryDraft.js
│   │   │   │   ├── globalUI.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations.ts
│   │   │   │   ├── mediaLibrary.ts
│   │   │   │   ├── medias.ts
│   │   │   │   ├── notifications.ts
│   │   │   │   ├── search.ts
│   │   │   │   └── status.ts
│   │   │   ├── redux/
│   │   │   │   ├── index.ts
│   │   │   │   └── middleware/
│   │   │   │       └── waitUntilAction.ts
│   │   │   ├── routing/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── history.spec.ts
│   │   │   │   └── history.ts
│   │   │   ├── types/
│   │   │   │   ├── diacritics.d.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── immutable.ts
│   │   │   │   ├── redux.ts
│   │   │   │   └── tomlify-j0.4.d.ts
│   │   │   └── valueObjects/
│   │   │       ├── AssetProxy.ts
│   │   │       ├── EditorComponent.js
│   │   │       └── Entry.ts
│   │   └── webpack.config.js
│   ├── decap-cms-default-exports/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-editor-component-image/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-auth/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── implicit-oauth.js
│   │   │   ├── index.js
│   │   │   ├── netlify-auth.js
│   │   │   ├── pkce-oauth.js
│   │   │   └── utils.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-util/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── APIError.ts
│   │   │   ├── APIUtils.ts
│   │   │   ├── AccessTokenError.ts
│   │   │   ├── Cursor.ts
│   │   │   ├── EditorialWorkflowError.ts
│   │   │   ├── __tests__/
│   │   │   │   ├── api.spec.js
│   │   │   │   ├── apiUtils.spec.js
│   │   │   │   ├── asyncLock.spec.js
│   │   │   │   ├── backendUtil.spec.js
│   │   │   │   ├── implementation.spec.js
│   │   │   │   ├── path.spec.js
│   │   │   │   └── unsentRequest.spec.js
│   │   │   ├── asyncLock.ts
│   │   │   ├── backendUtil.ts
│   │   │   ├── getBlobSHA.ts
│   │   │   ├── git-lfs.ts
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   ├── loadScript.js
│   │   │   ├── localForage.ts
│   │   │   ├── path.ts
│   │   │   ├── promise.ts
│   │   │   ├── types/
│   │   │   │   └── semaphore.d.ts
│   │   │   ├── types.ts
│   │   │   └── unsentRequest.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-widgets/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── stringTemplate.spec.js
│   │   │   ├── index.ts
│   │   │   ├── stringTemplate.ts
│   │   │   └── validations.ts
│   │   └── webpack.config.js
│   ├── decap-cms-locales/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bg/
│   │   │   │   └── index.js
│   │   │   ├── ca/
│   │   │   │   └── index.js
│   │   │   ├── cs/
│   │   │   │   └── index.js
│   │   │   ├── da/
│   │   │   │   └── index.js
│   │   │   ├── de/
│   │   │   │   └── index.js
│   │   │   ├── en/
│   │   │   │   └── index.js
│   │   │   ├── es/
│   │   │   │   └── index.js
│   │   │   ├── fa/
│   │   │   │   └── index.js
│   │   │   ├── fr/
│   │   │   │   └── index.js
│   │   │   ├── gr/
│   │   │   │   └── index.js
│   │   │   ├── he/
│   │   │   │   └── index.js
│   │   │   ├── hr/
│   │   │   │   └── index.js
│   │   │   ├── hu/
│   │   │   │   └── index.js
│   │   │   ├── index.js
│   │   │   ├── it/
│   │   │   │   └── index.js
│   │   │   ├── ja/
│   │   │   │   └── index.js
│   │   │   ├── ko/
│   │   │   │   └── index.js
│   │   │   ├── lt/
│   │   │   │   └── index.js
│   │   │   ├── mk/
│   │   │   │   └── index.js
│   │   │   ├── nb_no/
│   │   │   │   └── index.js
│   │   │   ├── nl/
│   │   │   │   └── index.js
│   │   │   ├── nn_no/
│   │   │   │   └── index.js
│   │   │   ├── pl/
│   │   │   │   └── index.js
│   │   │   ├── pt/
│   │   │   │   └── index.js
│   │   │   ├── ro/
│   │   │   │   └── index.js
│   │   │   ├── ru/
│   │   │   │   └── index.js
│   │   │   ├── sl/
│   │   │   │   └── index.js
│   │   │   ├── sv/
│   │   │   │   └── index.js
│   │   │   ├── th/
│   │   │   │   └── index.js
│   │   │   ├── tr/
│   │   │   │   └── index.js
│   │   │   ├── ua/
│   │   │   │   └── index.js
│   │   │   ├── uk/
│   │   │   │   └── index.js
│   │   │   ├── vi/
│   │   │   │   └── index.js
│   │   │   ├── zh_Hans/
│   │   │   │   └── index.js
│   │   │   └── zh_Hant/
│   │   │       └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-media-library-cloudinary/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-media-library-uploadcare/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-ui-auth/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── NetlifyAuthenticationPage.js
│   │   │   ├── PKCEAuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── NetlifyAuthenticationPage.spec.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── NetlifyAuthenticationPage.spec.js.snap
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-ui-default/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── Dropdown.js
│   │   │   ├── FieldLabel.js
│   │   │   ├── GoBackButton.js
│   │   │   ├── Icon/
│   │   │   │   ├── icons.js
│   │   │   │   └── images/
│   │   │   │       └── _index.js
│   │   │   ├── Icon.js
│   │   │   ├── IconButton.js
│   │   │   ├── ListItemTopBar.js
│   │   │   ├── Loader.js
│   │   │   ├── ObjectWidgetTopBar.js
│   │   │   ├── Toggle.js
│   │   │   ├── WidgetPreviewContainer.js
│   │   │   ├── index.js
│   │   │   └── styles.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-boolean/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── BooleanControl.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-code/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── data/
│   │   │   ├── languages-raw.yml
│   │   │   └── languages.json
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   └── process-languages.js
│   │   ├── src/
│   │   │   ├── CodeControl.js
│   │   │   ├── CodePreview.js
│   │   │   ├── SettingsButton.js
│   │   │   ├── SettingsPane.js
│   │   │   ├── index.js
│   │   │   ├── languageSelectStyles.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-colorstring/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ColorControl.js
│   │   │   ├── ColorPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-datetime/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── DateTimeControl.js
│   │   │   ├── DateTimePreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── DateTimeControl.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-file/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── FilePreview.js
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── withFileControl.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-image/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ImagePreview.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-list/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ListControl.js
│   │   │   ├── __tests__/
│   │   │   │   ├── ListControl.spec.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── ListControl.spec.js.snap
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── typedListHelpers.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-map/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── MapPreview.js
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── withMapControl.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-markdown/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── MarkdownControl/
│   │   │   │   ├── RawEditor.js
│   │   │   │   ├── Toolbar.js
│   │   │   │   ├── ToolbarButton.js
│   │   │   │   ├── VisualEditor.js
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── VisualEditor.spec.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── parser.spec.js.snap
│   │   │   │   │   └── parser.spec.js
│   │   │   │   ├── components/
│   │   │   │   │   ├── Shortcode.js
│   │   │   │   │   └── VoidBlock.js
│   │   │   │   ├── index.js
│   │   │   │   ├── plugins/
│   │   │   │   │   ├── BreakToDefaultBlock.js
│   │   │   │   │   ├── CloseBlock.js
│   │   │   │   │   ├── CommandsAndQueries.js
│   │   │   │   │   ├── ForceInsert.js
│   │   │   │   │   ├── Hotkey.js
│   │   │   │   │   ├── LineBreak.js
│   │   │   │   │   ├── Link.js
│   │   │   │   │   ├── blocks/
│   │   │   │   │   │   ├── defaultEmptyBlock.js
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownBackspace.js
│   │   │   │   │   │   │   ├── keyDownEnter.js
│   │   │   │   │   │   │   └── toggleBlock.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   ├── areCurrentAndPreviousBlocksOfType.js
│   │   │   │   │   │   │   ├── getListTypeAtCursor.js
│   │   │   │   │   │   │   ├── isCursorAtEndOfParagraph.js
│   │   │   │   │   │   │   ├── isCursorAtStartOfBlockType.js
│   │   │   │   │   │   │   ├── isCursorAtStartOfNonEmptyHeading.js
│   │   │   │   │   │   │   ├── isCursorCollapsedAfterSoftBreak.js
│   │   │   │   │   │   │   ├── isCursorInBlockType.js
│   │   │   │   │   │   │   └── isCursorInNonDefaultBlock.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── splitIntoParagraph.js
│   │   │   │   │   │   │   ├── unwrapIfCursorAtStart.js
│   │   │   │   │   │   │   └── wrapListItemsInBlock.js
│   │   │   │   │   │   └── withBlocks.js
│   │   │   │   │   ├── html/
│   │   │   │   │   │   └── withHtml.js
│   │   │   │   │   ├── inlines/
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownShiftEnter.js
│   │   │   │   │   │   │   ├── toggleLink.js
│   │   │   │   │   │   │   └── toggleMark.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   └── isMarkActive.js
│   │   │   │   │   │   ├── selectors/
│   │   │   │   │   │   │   └── getActiveLink.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── unwrapLink.js
│   │   │   │   │   │   │   └── wrapLink.js
│   │   │   │   │   │   └── withInlines.js
│   │   │   │   │   ├── lists/
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownBackspace.js
│   │   │   │   │   │   │   ├── keyDownEnter.js
│   │   │   │   │   │   │   ├── keyDownShiftTab.js
│   │   │   │   │   │   │   ├── keyDownTab.js
│   │   │   │   │   │   │   └── toggleListType.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   ├── isCursorAtListItemStart.js
│   │   │   │   │   │   │   ├── isCursorAtNoninitialParagraphStart.js
│   │   │   │   │   │   │   ├── isCursorInItemContainingNestedList.js
│   │   │   │   │   │   │   ├── isCursorInListItem.js
│   │   │   │   │   │   │   └── isSelectionWithinNoninitialListItem.js
│   │   │   │   │   │   ├── selectors/
│   │   │   │   │   │   │   ├── getListContainedInListItem.js
│   │   │   │   │   │   │   ├── getLowestAncestorList.js
│   │   │   │   │   │   │   └── getLowestAncestorQuote.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── changeListType.js
│   │   │   │   │   │   │   ├── convertParagraphToListItem.js
│   │   │   │   │   │   │   ├── liftFirstMatchedParent.js
│   │   │   │   │   │   │   ├── liftListItem.js
│   │   │   │   │   │   │   ├── mergeWithPreviousListItem.js
│   │   │   │   │   │   │   ├── moveListToListItem.js
│   │   │   │   │   │   │   ├── splitListItem.js
│   │   │   │   │   │   │   ├── splitToNestedList.js
│   │   │   │   │   │   │   ├── unwrapFirstMatchedParent.js
│   │   │   │   │   │   │   ├── unwrapSelectionFromList.js
│   │   │   │   │   │   │   ├── wrapFirstMatchedParent.js
│   │   │   │   │   │   │   └── wrapSelectionInList.js
│   │   │   │   │   │   └── withLists.js
│   │   │   │   │   ├── matchers/
│   │   │   │   │   │   ├── lowestMatchedAncestor.js
│   │   │   │   │   │   ├── matchLink.js
│   │   │   │   │   │   └── matchedAncestors.js
│   │   │   │   │   ├── shortcodes/
│   │   │   │   │   │   ├── insertShortcode.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   └── isCursorInEmptyParagraph.js
│   │   │   │   │   │   └── withShortcodes.js
│   │   │   │   │   └── util.js
│   │   │   │   └── renderers.js
│   │   │   ├── MarkdownPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── renderer.spec.js
│   │   │   ├── index.js
│   │   │   ├── regexHelper.js
│   │   │   ├── schema.js
│   │   │   ├── serializers/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── __fixtures__/
│   │   │   │   │   │   ├── commonmarkExpected.json
│   │   │   │   │   │   └── duplicate_marks_github_issue_3280.md
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── remarkShortcodes.spec.js.snap
│   │   │   │   │   ├── commonmark.spec.js
│   │   │   │   │   ├── index.spec.js
│   │   │   │   │   ├── remarkAllowHtmlEntities.spec.js
│   │   │   │   │   ├── remarkAssertParents.spec.js
│   │   │   │   │   ├── remarkEscapeMarkdownEntities.spec.js
│   │   │   │   │   ├── remarkPaddedLinks.spec.js
│   │   │   │   │   ├── remarkPlugins.spec.js
│   │   │   │   │   ├── remarkShortcodes.spec.js
│   │   │   │   │   ├── remarkSlate.spec.js
│   │   │   │   │   ├── remarkStripTrailingBreaks.spec.js
│   │   │   │   │   └── slate.spec.js
│   │   │   │   ├── index.js
│   │   │   │   ├── rehypePaperEmoji.js
│   │   │   │   ├── remarkAllowHtmlEntities.js
│   │   │   │   ├── remarkAssertParents.js
│   │   │   │   ├── remarkEscapeMarkdownEntities.js
│   │   │   │   ├── remarkImagesToText.js
│   │   │   │   ├── remarkPaddedLinks.js
│   │   │   │   ├── remarkRehypeShortcodes.js
│   │   │   │   ├── remarkShortcodes.js
│   │   │   │   ├── remarkSlate.js
│   │   │   │   ├── remarkSquashReferences.js
│   │   │   │   ├── remarkStripTrailingBreaks.js
│   │   │   │   ├── remarkWrapHtml.js
│   │   │   │   └── slateRemark.js
│   │   │   ├── styles.js
│   │   │   └── types.js
│   │   ├── test-helpers/
│   │   │   └── h.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-number/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── NumberControl.js
│   │   │   ├── NumberPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── number.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-object/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ObjectControl.js
│   │   │   ├── ObjectPreview.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-relation/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── RelationCache.js
│   │   │   ├── RelationControl.js
│   │   │   ├── RelationPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── relation.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-select/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── SelectControl.js
│   │   │   ├── SelectPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── select.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-string/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── StringControl.js
│   │   │   ├── StringPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-text/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── TextControl.js
│   │   │   ├── TextPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   └── decap-server/
│       ├── CHANGELOG.md
│       ├── README.md
│       ├── jest.config.js
│       ├── package.json
│       ├── src/
│       │   ├── global.d.ts
│       │   ├── index.ts
│       │   ├── logger.ts
│       │   ├── middlewares/
│       │   │   ├── common/
│       │   │   │   └── index.ts
│       │   │   ├── joi/
│       │   │   │   ├── customValidators.ts
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── localFs/
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── localGit/
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── types.ts
│       │   │   └── utils/
│       │   │       ├── entries.ts
│       │   │       └── fs.ts
│       │   ├── middlewares.ts
│       │   └── what-the-diff.d.ts
│       ├── tsconfig.json
│       └── webpack.config.js
├── renovate.json
├── scripts/
│   ├── cache.js
│   ├── externals.js
│   ├── pack-and-install.sh
│   ├── revert_publish.sh
│   └── webpack.js
├── setupTestFramework.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# https://editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.js]
quote_type = single
spaces_around_operators = true

[*.css]
quote_type = single

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .eslintrc.js
================================================
const fs = require('fs');

const packages = fs
  .readdirSync(`${__dirname}/packages`, { withFileTypes: true })
  .filter(dirent => dirent.isDirectory())
  .map(dirent => dirent.name);

module.exports = {
  parser: '@babel/eslint-parser',
  parserOptions: {
    requireConfigFile: false,
    babelOptions: {
      presets: ['@babel/preset-react'],
    },
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:cypress/recommended',
    'prettier',
    'plugin:import/recommended',
  ],
  env: {
    es6: true,
    browser: true,
    node: true,
    jest: true,
    'cypress/globals': true,
  },
  globals: {
    DECAP_CMS_VERSION: false,
    DECAP_CMS_APP_VERSION: false,
    DECAP_CMS_CORE_VERSION: false,
    CMS_ENV: false,
  },
  rules: {
    'no-console': [0],
    'react/prop-types': [0],
    'import/no-named-as-default': 0,
    'import/order': [
      'error',
      {
        'newlines-between': 'always',
        groups: [['builtin', 'external'], ['internal', 'parent', 'sibling', 'index'], ['type']],
      },
    ],
    'no-duplicate-imports': 'error',
    '@emotion/no-vanilla': 'error',
    '@emotion/pkg-renaming': 'error',
    '@emotion/import-from-emotion': 'error',
    '@emotion/styled-import': 'error',
    'require-atomic-updates': [0],
    'object-shorthand': ['error', 'always'],
    'func-style': ['error', 'declaration'],
    'prefer-const': [
      'error',
      {
        destructuring: 'all',
      },
    ],
    'unicorn/prefer-string-slice': 'error',
    'react/no-unknown-property': ['error', { ignore: ['css', 'bold', 'italic', 'delete'] }],
  },
  plugins: ['babel', '@emotion', 'cypress', 'unicorn'],
  settings: {
    react: {
      version: 'detect',
    },
    'import/resolver': {
      node: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
      },
    },
    'import/core-modules': [...packages, 'decap-cms-app/dist/esm'],
  },
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      parser: '@typescript-eslint/parser',
      extends: [
        'eslint:recommended',
        'plugin:react/recommended',
        'plugin:cypress/recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier',
        'plugin:import/recommended',
        'plugin:import/typescript',
      ],
      parserOptions: {
        ecmaVersion: 2018,
        sourceType: 'module',
        ecmaFeatures: {
          jsx: true,
        },
      },
      rules: {
        'no-duplicate-imports': [0], // handled by @typescript-eslint
        '@typescript-eslint/ban-types': [0], // TODO enable in future
        '@typescript-eslint/no-non-null-assertion': [0],
        '@typescript-eslint/consistent-type-imports': 'error',
        '@typescript-eslint/explicit-function-return-type': [0],
        '@typescript-eslint/explicit-module-boundary-types': [0],
        '@typescript-eslint/no-duplicate-imports': 'error',
        '@typescript-eslint/no-use-before-define': [
          'error',
          { functions: false, classes: true, variables: true },
        ],
      },
    },
  ],
};


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf


================================================
FILE: .github/.kodiak.toml
================================================
version = 1

[merge.automerge_dependencies]
versions = ["minor", "patch"]
usernames = ["renovate"]

[approve]
auto_approve_usernames = ["renovate"]

================================================
FILE: .github/CODEOWNERS
================================================
*                 @decaporg/maintainers


================================================
FILE: .github/FUNDING.yml
================================================
github: decaporg
open_collective: decap


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Report a problem you are experiencing
title: 'Please replace with a clear and descriptive title'
labels: 'type: bug'
---

<!--
If you are reporting a new issue, make sure that we do not have any duplicates already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please add a comment to the existing issue instead.

Please include as much of the information requested below as possible. If you fail to provide the requested information within 7 days, we cannot debug your issue and will close it. We will, however, reopen it if you later provide the information.

If you have an issue that can be shown visually, please provide a screenshot or GIF of the problem as well.
-->

**Describe the bug**
<!-- A clear and concise description of what the bug is. -->

**To Reproduce**
<!--
Steps to reproduce the behavior. For example:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->

**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->

**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->

**Applicable Versions:**
<!--You can find the CMS version by checking your web browser's developer tools console while in the CMS. -->
 - Decap CMS version: [e.g. 2.0.4]
 - Git provider: [e.g. GitHub, BitBucket]
 - OS: [e.g. Windows 7]
 - Browser version [e.g. chrome 22, safari 11]
<!-- If using NPM: -->
 - Node.JS version:

**CMS configuration**
<!-- Please link or paste your CMS `config.yml` here. -->


**Additional context**
<!-- Add any other context about the problem here. -->


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: 'Please replace with a clear and descriptive title'
labels: 'type: feature'
---

<!--
Please make sure that we do not have any requests for this feature already open. You can ensure this by searching the issue list for this repository. If there is a duplicate, please add a comment to the existing issue instead.
-->

**Is your feature request related to a problem? Please describe.**
<!--
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-->

**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->

**Describe alternatives you've considered**
<!--
A clear and concise description of any alternative solutions or features you've considered.
-->

**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thanks for submitting a pull request!

Please make sure you've read and understood our contributing guidelines here:
https://github.com/decaporg/decap-cms/blob/main/CONTRIBUTING.md

If this is a bug fix, make sure your description includes "fixes #xxxx", or
"closes #xxxx", where #xxxx is the issue number.

Please provide enough information so that others can review your pull request.
The first two fields are mandatory:
-->

**Summary**

<!--
Explain the **motivation** for making this change.
What existing problem does the pull request solve?
-->

**Test plan**

<!--
Demonstrate the code is solid.
Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.
-->

**Checklist**

Please add a `x` inside each checkbox:

- [ ] I have read the [contribution guidelines](https://github.com/decaporg/decap-cms/blob/main/CONTRIBUTING.md).

**A picture of a cute animal (not mandatory but encouraged)**


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
  - pinned
  - security
# Label to use when marking an issue as stale
staleLabel: 'status: stale'
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. It will be closed if no further activity occurs. Thank you
  for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false


================================================
FILE: .github/workflows/create-release.yml
================================================
name: Create release

on:
  create

jobs:
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/decap-cms@')

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Get semver number
        id: get_semver
        env:
          TAG_NAME: ${{ github.ref }}
        run: echo "::set-output name=pkg::${TAG_NAME:10}"

      - name: Create release on GitHub API
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ steps.get_semver.outputs.pkg }}
          body: |
            :scroll: [Changelog](https://github.com/${{ github.repository }}/blob/${{ steps.get_semver.outputs.pkg }}/CHANGELOG.md)
          draft: false
          prerelease: false


================================================
FILE: .github/workflows/labeler.yml
================================================
name: Label PR
on:
  pull_request:
    types: [opened, edited]

jobs:
  label-pr:
    if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]'
    runs-on: ubuntu-latest
    steps:
      - uses: netlify/pr-labeler-action@v1.0.0
        if: startsWith(github.event.pull_request.title, 'fix')
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          label: 'type: bug'
      - uses: netlify/pr-labeler-action@v1.0.0
        if: startsWith(github.event.pull_request.title, 'chore')
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          label: 'type: chore'
      - uses: netlify/pr-labeler-action@v1.0.0
        if: startsWith(github.event.pull_request.title, 'feat')
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          label: 'type: feature'
      - uses: netlify/pr-labeler-action@v1.0.0
        if: startsWith(github.event.pull_request.title, 'security')
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          label: 'type: security'


================================================
FILE: .github/workflows/nodejs.yml
================================================
name: Node CI

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    branches:
      - main
      - master
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      cms: ${{ steps.filter.outputs.cms }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            cms:
              - '!website/**'

  build-unit:
    needs: changes
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [24.x]
        include:
          - os: ubuntu-latest
            node-version: 22.x
      fail-fast: true
    if: ${{ needs.changes.outputs.cms == 'true' }}
    steps:
      - uses: actions/checkout@v4
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          check-latest: true
          cache: 'npm'
      - name: log versions
        run: node --version && npm --version
      - name: install dependencies (skip Cypress binary)
        run: npm ci
        env:
          CYPRESS_INSTALL_BINARY: 0
      - name: run lint + types + unit tests
        run: npm run test:ci

  e2e:
    needs: changes
    if: ${{ needs.changes.outputs.cms == 'true' }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: true
      matrix:
        machine: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - name: Use Node.js 24.x
        uses: actions/setup-node@v4
        with:
          node-version: 24.x
          check-latest: true
          cache: 'npm'
      - name: Cache Nx local cache
        uses: actions/cache@v4
        with:
          path: .nx/cache
          key: nx-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            nx-${{ runner.os }}-
      - name: Cache Cypress binary
        uses: actions/cache@v4
        with:
          path: ~/.cache/Cypress
          key: cypress-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            cypress-${{ runner.os }}-
      - name: install dependencies
        run: npm ci
      - name: build demo site
        run: npm run build:demo
      - name: run e2e tests (parallel)
        run: npm run test:e2e:run-ci
        env:
          CI_BUILD_ID: ${{ github.run_id }}-${{ github.run_attempt }}
          IS_FORK: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true || github.repository_owner != 'decaporg' }}
          # Only used for fork PRs (can't access CYPRESS_RECORD_KEY)
          MACHINE_INDEX: ${{ matrix.machine }}
          MACHINE_COUNT: 4
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
          NODE_OPTIONS: --max-old-space-size=4096
          TZ: Europe/Amsterdam
          GITHUB_EVENT_NAME: ${{ github.event_name }}
          GITHUB_BASE_REF: ${{ github.base_ref }}
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: cypress-results-${{ matrix.machine }}
          path: |
            cypress/screenshots
            cypress/videos



================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish Packages

on:
  push:
    tags:
      - 'decap-*@*'

permissions:
  contents: read
  id-token: write  # Required for OIDC trusted publishers

jobs:
  publish:
    runs-on: ubuntu-latest
    environment: production  # Optional: adds approval requirements and deployment protection
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for Lerna to detect changes

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'

      - name: Install dependencies
        run: npm ci

      - name: Build packages
        run: npm run build

      - name: Run tests
        run: npm run test:ci

      - name: Debug
        run: |
          echo "::group::Node & npm versions"
          node -v
          npm -v
          echo "::endgroup::"

          echo "::group::npm config (redacted)"
          npm config get registry || true
          npm config list -l | sed 's/_authToken.*/_authToken=[REDACTED]/' || true
          echo "::endgroup::"

          echo "::group::Env checks"
          if [ -n "${NODE_AUTH_TOKEN}" ]; then echo "NODE_AUTH_TOKEN set? yes"; else echo "NODE_AUTH_TOKEN set? no"; fi
          if [ -n "${NPM_CONFIG_USERCONFIG}" ]; then echo "NPM_CONFIG_USERCONFIG set? yes"; else echo "NPM_CONFIG_USERCONFIG set? no"; fi
          echo "::endgroup::"

          echo "::group::Git state"
          git status --porcelain || true
          git tag --list --points-at HEAD || true
          echo "::endgroup::"

          echo "::group::Registry package info"
          npm view decap-server version || true
          npm view decap-server versions --json || true
          echo "::endgroup::"

          echo "::group::Lerna packages"
          npx lerna ls --json || true
          echo "::endgroup::"

      - name: Publish to npm
        run: npm run lerna:publish -- --loglevel silly
        # No NODE_AUTH_TOKEN needed - uses OIDC automatically via trusted publishers


================================================
FILE: .github/workflows/sponsors.yml
================================================
# Automatically updates the README with our GitHub Sponsors.
# Fetches sponsor data from GitHub and adds their avatars to the README file.
# Runs weekly on Sundays at midnight UTC and can be triggered manually.

name: Generate Sponsors README
on:
  workflow_dispatch:
  schedule:
    - cron: 0 0 * * 0
permissions:
  contents: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Generate Sponsors
        uses: JamesIves/github-sponsors-readme-action@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          file: 'README.md'
          organization: true
          active-only: false
          template: '<a href="https://github.com/{{{ login }}}"><img src="https://github.com/{{{ login }}}.png" width="48px" alt="{{{ login }}}" style="border-radius:50%" /></a> &nbsp; '

      - name: Deploy
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          branch: readme-sponsors
          folder: '.'

================================================
FILE: .gitignore
================================================
dist/
bin/
public/
node_modules/
npm-debug.log
.DS_Store
.tern-project
yarn-error.log
.vscode/
.idea/
.claude/
manifest.yml
.imdone/
cypress/videos
cypress/screenshots
__diff_output__
coverage/
.cache
*.log
.env
.temp/
storybook-static/
.nx


================================================
FILE: .husky/commit-msg
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit $1


================================================
FILE: .npmrc
================================================
legacy-peer-deps=true


================================================
FILE: .nvmrc
================================================
lts/*


================================================
FILE: .prettierignore
================================================
dist/
bin/
public/
.cache/
packages/decap-cms-backend-github/src/fragmentTypes.js
packages/decap-cms-backend-gitlab/src/AuthenticationPage.js
packages/decap-cms-backend-proxy/src/implementation.ts

/.nx/cache

================================================
FILE: .prettierrc
================================================
{
  "arrowParens": "avoid",
  "trailingComma": "all",
  "singleQuote": true,
  "printWidth": 100
}


================================================
FILE: .storybook/main.js
================================================
module.exports = {
  stories: [
    '../packages/decap-cms-core/src/**/*.stories.js',
    '../packages/decap-cms-ui-default/src/**/*.stories.js',
  ],
  addons: ['@storybook/addon-actions', '@storybook/addon-links'],
};


================================================
FILE: .stylelintrc
================================================
{
  "extends": ["stylelint-config-standard-scss"],
  "customSyntax": "postcss-styled-syntax",
  "rules": {
    "block-no-empty": null,
    "no-duplicate-selectors": null,
    "no-empty-source": null,
    "no-extra-semicolons": null,
    "declaration-empty-line-before": null,
    "string-quotes": null,
    "selector-class-pattern": null,
    "selector-pseudo-element-colon-notation": null,
    "rule-empty-line-before": null,
    "declaration-colon-newline-after": null,
    "at-rule-empty-line-before": null,
    "alpha-value-notation": null,
    "color-function-notation": null,
    "keyframes-name-pattern": null,
    "value-list-comma-newline-after": null,
    "no-descending-specificity": null,
    "indentation": null,
    "no-empty-first-line": null,
    "no-eol-whitespace": null,
    "no-missing-end-of-source-newline": null,
    "scss/operator-no-unspaced": null,
    "scss/operator-no-newline-after": null,
    "value-keyword-case": null,
    "function-name-case": null,
    "function-whitespace-after": null,
    "selector-max-empty-lines": null,
    "selector-type-no-unknown": [
      true,
      {
        "ignoreTypes": ["$dummyValue"]
      }
    ]
  },
  "ignoreFiles": ["packages/decap-cms-lib-auth/index.d.ts"]
}


================================================
FILE: CHANGELOG.md
================================================
# Changelog
Decap CMS is a collection of npm packages with their own versions and changelogs, each listed
below. The legacy pre-2.0 changelog is below as well.

## Core and distributions
- [decap-cms](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms/CHANGELOG.md)
- [decap-cms-core](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-core/CHANGELOG.md)

## Shared libraries
- [decap-cms-lib-auth](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-lib-auth/CHANGELOG.md)
- [decap-cms-lib-util](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-lib-util/CHANGELOG.md)
- [decap-cms-ui-default](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-ui-default/CHANGELOG.md)

## Backends
- [decap-cms-backend-bitbucket](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-backend-bitbucket/CHANGELOG.md)
- [decap-cms-backend-git-gateway](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-backend-git-gateway/CHANGELOG.md)
- [decap-cms-backend-github](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-backend-github/CHANGELOG.md)
- [decap-cms-backend-gitlab](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-backend-gitlab/CHANGELOG.md)
- [decap-cms-backend-test](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-backend-test/CHANGELOG.md)

## Editor Components
- [decap-cms-editor-component-image](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-editor-component-image/CHANGELOG.md)

## Widgets
- [decap-cms-widget-boolean](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-boolean/CHANGELOG.md)
- [decap-cms-widget-date](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-date/CHANGELOG.md)
- [decap-cms-widget-datetime](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-datetime/CHANGELOG.md)
- [decap-cms-widget-file](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-file/CHANGELOG.md)
- [decap-cms-widget-image](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-image/CHANGELOG.md)
- [decap-cms-widget-list](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-list/CHANGELOG.md)
- [decap-cms-widget-markdown](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-markdown/CHANGELOG.md)
- [decap-cms-widget-number](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-number/CHANGELOG.md)
- [decap-cms-widget-object](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-object/CHANGELOG.md)
- [decap-cms-widget-relation](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-relation/CHANGELOG.md)
- [decap-cms-widget-select](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-select/CHANGELOG.md)
- [decap-cms-widget-string](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-string/CHANGELOG.md)
- [decap-cms-widget-text](https://github.com/decaporg/decap-cms/blob/main/packages/decap-cms-widget-text/CHANGELOG.md)

## Legacy Changelog

## [Unreleased] ([demo](https://cms-demo.netlify.com/))
<details>
  <summary>
    Changes that have landed in main but are not yet released.
    Click to see more.
  </summary>

  ## v2
  * (possibly breaking): return date object from date/datetime widgets if no format set ([@erquhart](https://github.com/erquhart) in [#1296](https://github.com/decaporg/decap-cms/pull/1296))
  * check for title/slug field on config load ([@tech4him1](https://github.com/tech4him1) in [#1203](https://github.com/decaporg/decap-cms/pull/1203))
</details>

## 1.9.4 (July 21, 2018) ([demo](https://1-9-4--cms-demo.netlify.com))
Fix multipart extension support for GitLab

## Bug Fixes
* Support extensions with multiple parts for GitLab ([@Nic128](https://github.com/Nic128) in [#1478](https://github.com/decaporg/decap-cms/pull/1478))


## 1.9.3 (July 3, 2018) ([demo](https://1-9-3--cms-demo.netlify.com))
Fix numbers in TOML output

## Bug Fixes
* fix int value output in TOML format file (@slathrop in #1458)


## 1.9.2 (June 15, 2018) ([demo](https://1-9-2--cms-demo.netlify.com))
Fix test repo crash

## Bug Fixes
* fix test-repo crash on non-existent folder ([@tech4him1](https://github.com/tech4him1) in [#1444](https://github.com/decaporg/decap-cms/pull/1444))


## 1.9.1 (June 14, 2018) ([demo](https://1-9-1--cms-demo.netlify.com))
Fix GitLab Implicit OAuth

## Bug Fixes
* fix GitLab Implicit OAuth ([@tech4him1](https://github.com/tech4him1) in [#1439](https://github.com/decaporg/decap-cms/pull/1439))


## 1.9.0 (June 12, 2018) ([demo](https://1-9-0--cms-demo.netlify.com))
GitLab support is here!!! 🎉🎉🎉

### Features
* add GitLab backend with Cursor API ([@Benaiah](https://github.com/Benaiah) in [#1343](https://github.com/decaporg/decap-cms/pull/1343))

## Bug Fixes
* fix workflow top panel styling ([@erquhart](https://github.com/erquhart) in [#1398](https://github.com/decaporg/decap-cms/pull/1398))
* only use `label_singular` when one item is rendered in List widget ([@robertkarlsson](https://github.com/robertkarlsson) in [#1422](https://github.com/decaporg/decap-cms/pull/1422))
* fix hidden widgets being rendered in editor components ([@robertkarlsson](https://github.com/robertkarlsson) in [#1414](https://github.com/decaporg/decap-cms/pull/1414))


## 1.8.4 (May 25, 2018) ([demo](https://1-8-4--cms-demo.netlify.com))
Fix markdown widget styling.

### Bug Fixes
  * fix markdown widget styling ([@erquhart](https://github.com/erquhart) in [#1384](https://github.com/decaporg/decap-cms/pull/1384))


## 1.8.3 (May 25, 2018) ([demo](https://1-8-3--cms-demo.netlify.com/))
Update dependencies.


## 1.8.2 (May 24, 2018) ([demo](https://1-8-2--cms-demo.netlify.com/))
Fix failure to save/publish.

### Bug Fixes
  * fix save/publish failure, revert overwrite prevention feature (@erquhart)


## 1.8.1 (May 23, 2018) ([demo](https://1-8-1--cms-demo.netlify.com/))
Allow upload of files larger than 1MB to GitHub, prevent unintentional file overwrites.

### Bug Fixes
* prevent overwriting when generated slug matches an existing file ([@brianlmacdonald](https://github.com/brianlmacdonald) in [#1239](https://github.com/decaporg/decap-cms/pull/1239))
* fix large files failing to load ([@tech4him1](https://github.com/tech4him1) in [#1224](https://github.com/decaporg/decap-cms/pull/1224))

### Beta Features
* enable custom commit message templates ([@delucis](https://github.com/delucis) in [#1359](https://github.com/decaporg/decap-cms/pull/1359))


## 1.8.0 (May 16, 2018) ([demo](https://1-8-0--cms-demo.netlify.com/))
Customizable relation widget display fields, squash merges for editorial workflow, perf
improvements.

### Features
* support `displayFields` config property for the relation widget ([@zurawiki](https://github.com/zurawiki) in [#1303](https://github.com/decaporg/decap-cms/pull/1303))

### Improvements
* prevent login for `git-gateway` backend when Git Gateway is not enabled for Netlify site ([@tech4him1](https://github.com/tech4him1) in [#1295](https://github.com/decaporg/decap-cms/pull/1295))

### Performance
* use `cloneElement` when possible for editor preview pane widgets ([@danielmahon](https://github.com/danielmahon) in [#1248](https://github.com/decaporg/decap-cms/pull/1248))
* upgrade to Webpack 4 ([@tech4him1](https://github.com/tech4him1) in [#1214](https://github.com/decaporg/decap-cms/pull/1214))

### Beta Features
* support `squash_merges` config option for GitHub backend ([@delucis](https://github.com/delucis) in [#1330](https://github.com/decaporg/decap-cms/pull/1330))


## 1.7.0 (April 24, 2018) ([demo](https://1-7-0--cms-demo.netlify.com/))
Allow custom auth endpoint, bug fixes.

### Features
* allow custom auth endpoint ([@erquhart](https://github.com/erquhart) in [#1294](https://github.com/decaporg/decap-cms/pull/1294))

### Improvements
* skip validation of optional fields when empty (@Dammmien in #1237)

### Bug Fixes
* fix GitHub auth button icon alignment (@erquhart in #1299)
* fix Git Gateway login hang (@ekoeryanto in #1240)


## 1.6.0 (April 19, 2018) ([demo](https://1-6-0--cms-demo.netlify.com/))
Markdown toolbar customization, manual date widget entry, bug fixes.

### Features
* Allow markdown editor toolbar customization ([@Dammmien](https://github.com/Dammmien) in [#1236](https://github.com/decaporg/decap-cms/pull/1236))
* Allow login screen to be skipped for test repo backend ([@erquhart](https://github.com/erquhart) in [#1291](https://github.com/decaporg/decap-cms/pull/1291))

### Bug Fixes
* Fix button/icon alignment on Safari 10 ([@maciejmatu](https://github.com/maciejmatu) in [#1227](https://github.com/decaporg/decap-cms/pull/1227))
* Allow typing in date widget ([@Dammmien](https://github.com/Dammmien) in [#1247](https://github.com/decaporg/decap-cms/pull/1247))


## 1.5.0 (April 11, 2018) ([demo](https://1-5-0--cms-demo.netlify.com/))
New time based slug placeholders, set config.yml URL with <link>.

### Features
* Add hour, minute, and second slug fields ([@terrierscript](https://github.com/terrierscript) in [#1207](https://github.com/decaporg/decap-cms/pull/1207))
* Allow setting config URL with <link> ([@brianlmacdonald](https://github.com/brianlmacdonald) in [#1146](https://github.com/decaporg/decap-cms/pull/1146))

### Bug Fixes
* Fix broken new media uploads for Git Gateway ([@tech4him1](https://github.com/tech4him1) in [#1221](https://github.com/decaporg/decap-cms/pull/1221))

### Dev Experience
* Enable editorial workflow for test backend ([@erquhart](https://github.com/erquhart) in [#1225](https://github.com/decaporg/decap-cms/pull/1225))


## 1.4.0 (March 29, 2018) ([demo](https://1-4-0--cms-demo.netlify.com/))
Filename creation can now be customized to exclude Unicode! Also, check out the new Beta Features! 💥

### Features
* Add option to strip Unicode from entry filenames ([@tech4him1](https://github.com/tech4him1) in [#1135](https://github.com/decaporg/decap-cms/pull/1135))

### Improvements
* Hide "create new" button for single files ([@tech4him1](https://github.com/tech4him1) in [#1200](https://github.com/decaporg/decap-cms/pull/1200))
* Filter editorial workflow entries by PR base branch ([@erquhart](https://github.com/erquhart) in [#1155](https://github.com/decaporg/decap-cms/pull/1155))

### Bug Fixes
* Allow list widget "add" button to be disabled ([@gazebosx3](https://github.com/gazebosx3) in [#1102](https://github.com/decaporg/decap-cms/pull/1102))
* Fix broken thumbnail when uploading an image to a private repository ([@Quicksaver](https://github.com/Quicksaver) in [#994](https://github.com/decaporg/decap-cms/pull/994))
* Get default value from each widget rather than setting all to null ([@MichaelRomani](https://github.com/MichaelRomani) in [#1126](https://github.com/decaporg/decap-cms/pull/1126))
* Fix editor validation notifications for editorial workflow ([@erquhart](https://github.com/erquhart) in [#1204](https://github.com/decaporg/decap-cms/pull/1204))
* Prevent Git Gateway users with invalid tokens from logging in ([@tech4him1](https://github.com/tech4him1) in [#1209](https://github.com/decaporg/decap-cms/pull/1209))
* Fix relation list preview ([@Quicksaver](https://github.com/Quicksaver) in [#1199](https://github.com/decaporg/decap-cms/pull/1199))
* Fix missing config file handling ([@talves](https://github.com/talves) in [#1182](https://github.com/decaporg/decap-cms/pull/1182))
* Fix initially blank date fields ([@tech4him1](https://github.com/tech4him1) in [#1210](https://github.com/decaporg/decap-cms/pull/1210))

### Beta Features
* Accept CSS strings in `registerPreviewStyle` ([@erquhart](https://github.com/erquhart) in [#1162](https://github.com/decaporg/decap-cms/pull/1162))
* Change manual init API to use the same bundle as auto init ([@talves](https://github.com/talves) and @erquhart in [#1173](https://github.com/decaporg/decap-cms/pull/1173))

### 4 tha devz
* Ship source code to npm ([@tech4him1](https://github.com/tech4him1) in [#1095](https://github.com/decaporg/decap-cms/pull/1095))


## 1.3.5 (March 6, 2018) ([demo](https://1-3-5--cms-demo.netlify.com/))
Fixes styling issues

* Revert lockfile update due to breaking changes in css processing deps ([@erquhart](https://github.com/erquhart))


## 1.3.4 (March 6, 2018) ([demo](https://1-3-4--cms-demo.netlify.com/))
Fixes editorial workflow entry failure

* Fix editorial workflow entries not loading ([@erquhart](https://github.com/erquhart))


## 1.3.3 (March 6, 2018) ([demo](https://1-3-3--cms-demo.netlify.com/))
Fixes load failure

* Fix bugs introduced by manual initialization ([@erquhart](https://github.com/erquhart) in [#1157](https://github.com/decaporg/decap-cms/pull/1157))


## 1.3.2 (March 6, 2018) ([demo](https://1-3-2--cms-demo.netlify.com/))
Fixes date widget default format, collection load failure when entry fails

* Fix date widget default format ([@erquhart](https://github.com/erquhart) in [#1143](https://github.com/decaporg/decap-cms/pull/1143))
* Fix collection failure when individual entries fail to load ([@tech4him1](https://github.com/tech4him1) in [#1093](https://github.com/decaporg/decap-cms/pull/1093))

### Beta Features
* Allow manual initialization and config injection ([@erquhart](https://github.com/erquhart) in [#1149](https://github.com/decaporg/decap-cms/pull/1149))


## 1.3.1 (March 3, 2018) ([demo](https://1-3-1--cms-demo.netlify.com/))
Fixes editorial workflow failure for unknown collections.

* Report editorial workflow load errors, ignore entries with unknown collections ([@erquhart](https://github.com/erquhart) in [#1153](https://github.com/decaporg/decap-cms/pull/1153))


## 1.3.0 (February 27, 2018) ([demo](https://1-3-0--cms-demo.netlify.com/))
Multi-part extensions, e.g. "en.md", a11y improvements in the editor, and bugfixes.

* Ensure unique id for each editor field ([@xifengjin88](https://github.com/xifengjin88) in [#1087](https://github.com/decaporg/decap-cms/pull/1087))
* Fix lists crashing when first value is not a string ([@tech4him1](https://github.com/tech4him1) in [#1115](https://github.com/decaporg/decap-cms/pull/1115))
* Support extensions with multiple parts (i.e. `en.md`) ([@tech4him1](https://github.com/tech4him1) in [#1123](https://github.com/decaporg/decap-cms/pull/1123))
* Fix lost unsaved changes when updating status or publishing from editor ([@erquhart](https://github.com/erquhart) in [#987](https://github.com/decaporg/decap-cms/pull/987))


## 1.2.2 (February 21, 2018) ([demo](https://1-2-2--cms-demo.netlify.com/))
Fixes ES5 transpiling.

* Remove babel-preset-env, fix ES5 transpiling ([@erquhart](https://github.com/erquhart) in [#1127](https://github.com/decaporg/decap-cms/pull/1127))


## 1.2.1 (February 21, 2018) ([demo](https://1-2-1--cms-demo.netlify.com/))
Allows `label_singular` config for collections and lists and distinct frontmatter delimiters.

* Accept `label_singular` in collection config ([@peduarte](https://github.com/peduarte) in [#1086](https://github.com/decaporg/decap-cms/pull/1086))
* Transpile down to ES5 to support older tooling eg. Webpack 1 ([@tech4him1](https://github.com/tech4him1) in [#1107](https://github.com/decaporg/decap-cms/pull/1107))
* Allow different opening and closing frontmatter delimiters ([@tech4him1](https://github.com/tech4him1) in [#1094](https://github.com/decaporg/decap-cms/pull/1094))


## 1.2.0 (February 13, 2018) ([demo](https://1-2-0--cms-demo.netlify.com/))
Adds support for multiple frontmatter formats and custom delimiters, UI improvements.

* Use babel-preset-env to transpile for supported environments only ([@tech4him1](https://github.com/tech4him1) in [#765](https://github.com/decaporg/decap-cms/pull/765))
* Change direction of collapsed editor widget arrow indicators ([@Doocey](https://github.com/Doocey) in [#1059](https://github.com/decaporg/decap-cms/pull/1059))
* Support for writing frontmatter in JSON, TOML, or YAML ([@tech4him1](https://github.com/tech4him1) in [#933](https://github.com/decaporg/decap-cms/pull/933))
* Add collection label next to search results ([@solpark](https://github.com/solpark) in [#1068](https://github.com/decaporg/decap-cms/pull/1068))
* Support custom delimiters for frontmatter ([@Swieckowski](https://github.com/Swieckowski) in [#1064](https://github.com/decaporg/decap-cms/pull/1064))


## 1.1.0 (January 25, 2018) ([demo](https://1-1-0--cms-demo.netlify.com/))

* Fix metadata handling for all children of a list field ([@Quicksaver](https://github.com/Quicksaver) in [#719](https://github.com/decaporg/decap-cms/pull/719))
* Allow registry of external backends ([@talves](https://github.com/talves) in [#1011](https://github.com/decaporg/decap-cms/pull/1011))


## 1.0.4 (January 23, 2018) ([demo](https://1-0-4--cms-demo.netlify.com/))

* Fix markdown widget re-rendering after load ([@erquhart](https://github.com/erquhart) in [#955](https://github.com/decaporg/decap-cms/pull/955))
* Fix image form not displaying when added as first item in markdown widget ([@Dammmien](https://github.com/Dammmien) in [#926](https://github.com/decaporg/decap-cms/pull/926))
* Add collapse all/expand all functionality to List widget ([@drlogout](https://github.com/drlogout) in [#912](https://github.com/decaporg/decap-cms/pull/912))
* Add expand/collapse functionality to object widget ([@drlogout](https://github.com/drlogout) in [#927](https://github.com/decaporg/decap-cms/pull/927))
* Fix vertically centered icon positioning in Firefox ([@jimmaaay](https://github.com/jimmaaay) in [#976](https://github.com/decaporg/decap-cms/pull/976))
* Fix new uploads not showing in media library ([@tech4him1](https://github.com/tech4him1) in [#925](https://github.com/decaporg/decap-cms/pull/925))
* Overhaul widgets section in docs ([@hcavalieri](https://github.com/hcavalieri) in [#866](https://github.com/decaporg/decap-cms/pull/866))
* Use proper formatting when writing JSON files ([@tech4him1](https://github.com/tech4him1) in [#979](https://github.com/decaporg/decap-cms/pull/979))
* Ensure temporary storage is available before attempting to write ([@vencax](https://github.com/vencax) in [#550](https://github.com/decaporg/decap-cms/pull/550))
* Show SVG preview images in the media library ([@Jinksi](https://github.com/Jinksi) in [#954](https://github.com/decaporg/decap-cms/pull/954))
* Fix failed PR force-merge showing success message ([@tech4him1](https://github.com/tech4him1) in [#1016](https://github.com/decaporg/decap-cms/pull/1016))
* Fix false proptype warning for collection view ([@Quicksaver](https://github.com/Quicksaver) in [#998](https://github.com/decaporg/decap-cms/pull/998))


## 1.0.3 (December 19, 2017) ([demo](https://1-0-3--cms-demo.netlify.com/))

* Fix select widgets with object type options ([@tech4him1](https://github.com/tech4him1) in [#920](https://github.com/decaporg/decap-cms/pull/920))
* Warn when uploading asset with same name as existing asset ([@Dammmien](https://github.com/Dammmien) in [#853](https://github.com/decaporg/decap-cms/pull/853))
* Fix Slate plugins broken during 0.30 migration ([@Dammmien](https://github.com/Dammmien) in [#856](https://github.com/decaporg/decap-cms/pull/856))
* Fix infinite scrolling for collections with integrations ([@erquhart](https://github.com/erquhart) in [#940](https://github.com/decaporg/decap-cms/pull/940))


## 1.0.2 (December 7, 2017) ([demo](https://1-0-2--cms-demo.netlify.com/))

* Fix position of editor view controls ([@biilmann](https://github.com/biilmann) in [#886](https://github.com/decaporg/decap-cms/pull/886))
* Update docs intro to direct to new content ([@verythorough](https://github.com/verythorough) in [#891](https://github.com/decaporg/decap-cms/pull/891))


## 1.0.1 (December 7, 2017) ([demo](https://1-0-1--cms-demo.netlify.com/))

* Add configuration options doc ([@verythorough](https://github.com/verythorough) in [#885](https://github.com/decaporg/decap-cms/pull/885))
* Add new docs website landing page ([@ziburski](https://github.com/ziburski) in [#880](https://github.com/decaporg/decap-cms/pull/880))
* Rework Test Drive and Quick Start docs ([@verythorough](https://github.com/verythorough) in [#888](https://github.com/decaporg/decap-cms/pull/888))


## 1.0.0 (December 7, 2017) ([demo](https://1-0-0--cms-demo.netlify.com/))

The first major release of Netlify CMS!! Here are the big features:

### All New UI 💫
The CMS UI has been completely redesigned from the ground up!

* All new visuals and reprised UX throughout
* List view/grid view option for collections
* Deletion now works for editorial workflow
* Control publishing and editorial workflow status from the entry editor
* Descriptions can now be added for each collection

## All New Docs 💥
The docs at netlifycms.org have been rewritten and vastly improved!

* Full references with code samples for every configuration option, collection type, and widget
* Easier docs contributions with the website built directly in the repo
* Updated intro docs with a new Gatsby starter template in addition to the Hugo one

## Changes

* Fix backspace not removing empty block in markdown editor ([@Dammmien](https://github.com/Dammmien) in [#854](https://github.com/decaporg/decap-cms/pull/854))
* Add select widget documentation ([@ackushiw](https://github.com/ackushiw) in [#806](https://github.com/decaporg/decap-cms/pull/806))
* Migrate netlifycms.org source into this repo ([@verythorough](https://github.com/verythorough) in [#860](https://github.com/decaporg/decap-cms/pull/860))
* Fix Slate mark rendering ([@erquhart](https://github.com/erquhart) in [#858](https://github.com/decaporg/decap-cms/pull/858))
* Do not infer file format if format specified in config ([@tech4him1](https://github.com/tech4him1) in [#795](https://github.com/decaporg/decap-cms/pull/795))
* Infer format from extension for new entries ([@tech4him1](https://github.com/tech4him1) in [#796](https://github.com/decaporg/decap-cms/pull/796))
* Throw on unsupported format ([@tech4him1](https://github.com/tech4him1) in [#831](https://github.com/decaporg/decap-cms/pull/831))
* Update widget docs ([@verythorough](https://github.com/verythorough) in [#876](https://github.com/decaporg/decap-cms/pull/876))
* Implement new UI, restructure/refactor project ([@erquhart](https://github.com/erquhart) and [@neutyp](https://github.com/neutyp) in [#785](https://github.com/decaporg/decap-cms/pull/785))


## 0.7.6 (November 27, 2017) ([demo](https://0-7-6--cms-demo.netlify.com/))

* Migrate to Slate 0.30.x ([@erquhart](https://github.com/erquhart) in [#826](https://github.com/decaporg/decap-cms/pull/826))
* Fix empty image fields saving null or undefined ([@tech4him1](https://github.com/tech4him1) in [#829](https://github.com/decaporg/decap-cms/pull/829))
* Add JSON as manually supported format ([@tech4him1](https://github.com/tech4him1) in [#830](https://github.com/decaporg/decap-cms/pull/830))
* Enable webpack scope hoisting ([@tech4him1](https://github.com/tech4him1) in [#840](https://github.com/decaporg/decap-cms/pull/840))
* Update bundled version of gotrue-js to latest ([@biilmann](https://github.com/biilmann) in [#837](https://github.com/decaporg/decap-cms/pull/837))
* Add global error boundary ([@tech4him1](https://github.com/tech4him1) in [#847](https://github.com/decaporg/decap-cms/pull/847))
* Fix datetime formatting, allow empty value ([@biilmann](https://github.com/biilmann) in [#842](https://github.com/decaporg/decap-cms/pull/842))

### Docs

* Update authentication doc to cover all backends ([@verythorough](https://github.com/verythorough) in [#751](https://github.com/decaporg/decap-cms/pull/751))
* Add oauth-provider-go to custom-authentication.md ([@igk1972](https://github.com/igk1972) in [#845](https://github.com/decaporg/decap-cms/pull/845))


## 0.7.5 (November 19, 2017) ([demo](https://0-7-5--cms-demo.netlify.com/))

* Add private media support for asset integrations ([@erquhart](https://github.com/erquhart) in [#834](https://github.com/decaporg/decap-cms/pull/834))


## 0.7.4 (November 15, 2017) ([demo](https://0-7-4--cms-demo.netlify.com/))

* Remove trailing slash from directory listing path ([@biilmann](https://github.com/biilmann) in [#817](https://github.com/decaporg/decap-cms/pull/817))
* Fix images with non-lowercase extensions not being treated as images ([@erquhart](https://github.com/erquhart) in [#816](https://github.com/decaporg/decap-cms/pull/816))
* Prompt before closing window with unsaved changes in the editor ([@benaiah](https://github.com/benaiah) in [#815](https://github.com/decaporg/decap-cms/pull/815))


## 0.7.3 (November 11, 2017) ([demo](https://0-7-3--cms-demo.netlify.com/))

* Fix persisting files with no body/data files ([@ebello](https://github.com/ebello) in [#808](https://github.com/decaporg/decap-cms/pull/808))
* Fix ControlHOC ref for redux container widgets ([@erquhart](https://github.com/erquhart) in [#812](https://github.com/decaporg/decap-cms/pull/812))
* Fix entries not saving due to null integrations state ([@erquhart](https://github.com/erquhart) in [#814](https://github.com/decaporg/decap-cms/pull/814))
* Fix requestAnimationFrame warnings in tests ([@tech4him1](https://github.com/tech4him1) in [#811](https://github.com/decaporg/decap-cms/pull/811))


## 0.7.2 (November 11, 2017) ([demo](https://0-7-2--cms-demo.netlify.com/))

* Only rebase editorial workflow pull requests if assets are stored in content repo ([@erquhart](https://github.com/erquhart) in [#804](https://github.com/decaporg/decap-cms/pull/804))
* Fix Netlify Identity widget logout method being called after signup redirect ([@tech4him1](https://github.com/tech4him1) in [#805](https://github.com/decaporg/decap-cms/pull/805))


## 0.7.1 (November 11, 2017) ([demo](https://0-7-1--cms-demo.netlify.com/))

* Enable sourcemaps ([@erquhart](https://github.com/erquhart) in [#803](https://github.com/decaporg/decap-cms/pull/803))
* Add unselected option to select widget when no default is set ([@benaiah](https://github.com/benaiah) in [#673](https://github.com/decaporg/decap-cms/pull/673))
* Fix image not shown after upload for Git Gateway ([@erquhart](https://github.com/erquhart) in [#790](https://github.com/decaporg/decap-cms/pull/790))
* Fix empty media folder loading error ([@erquhart](https://github.com/erquhart) in [#791](https://github.com/decaporg/decap-cms/pull/791))
* Fix error for non-markdown files in editorial workflow ([@tech4him1](https://github.com/tech4him1) in [#794](https://github.com/decaporg/decap-cms/pull/794))
* Fix login when accept_roles is set ([@tech4him1](https://github.com/tech4him1) in [#801](https://github.com/decaporg/decap-cms/pull/801))
* Add error boundary to editor preview iframe ([@erquhart](https://github.com/erquhart) in [#779](https://github.com/decaporg/decap-cms/pull/779))


## 0.7.0 (November 9, 2017) ([demo](https://0-7-0--cms-demo.netlify.com/))

### Media Library UI
The CMS now features a media library UI for browsing, adding, and removing media from your content
repo! The library shows assets in from the directory set as `media_library` in the CMS config. The
media library is fully backwards compatible for existing CMS installations.

### All Changes
* Add config option to disable deletion for a collection ([@rpullinger](https://github.com/rpullinger) in [#707](https://github.com/decaporg/decap-cms/pull/707))
* Fix TOML files not being saved with the correct extension ([@tech4him1](https://github.com/tech4him1) in [#757](https://github.com/decaporg/decap-cms/pull/757))
* Clean up file formatters ([@tech4him1](https://github.com/tech4him1) in [#759](https://github.com/decaporg/decap-cms/pull/759))
* Add scroll sync toggle to editor ([@Jinksi](https://github.com/Jinksi) in [#693](https://github.com/decaporg/decap-cms/pull/693))
* Disable login button while login is in progress ([@tech4him1](https://github.com/tech4him1) in [#741](https://github.com/decaporg/decap-cms/pull/741))
* Improve markdown editor active style indicator accuracy ([@pjsier](https://github.com/pjsier) in [#774](https://github.com/decaporg/decap-cms/pull/774))
* Add media library UI ([@erquhart](https://github.com/erquhart) in [#554](https://github.com/decaporg/decap-cms/pull/554))
* Fix transparent background on list widget ([@Jinksi](https://github.com/Jinksi) in [#768](https://github.com/decaporg/decap-cms/pull/768))


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at decap@p-m.si. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# CONTRIBUTING

Contributions are always welcome, no matter how large or small. Before contributing,
please read the [code of conduct](CODE_OF_CONDUCT.md).

For details on contributing to documentation, see [Website Readme](https://github.com/decaporg/decap-website/blob/main/README.md).

## Setup

> Install [Node.js (LTS)](https://nodejs.org/) on your system.

### Install dependencies

```sh
git clone https://github.com/decaporg/decap-cms
cd decap-cms
npm install
```

### Run locally

```sh
npm run start
```

## Available scripts

### clean

Removes all of the CMS package `dist` directories.

```sh
npm run clean
```

### reset

Runs the `clean` script and removes all the `node_modules` from the CMS packages.

```sh
npm run reset
```

### build

Runs the `clean` script and builds the CMS packages.

```sh
npm run build
```

### build-preview

Runs the `build` and `build-preview` scripts in each package and serves the resulting build locally.

```sh
npm run build-preview
```

### test

Runs linting and Jest tests.

```sh
npm run test
```

### test:all

Runs linting, Jest, and Cypress tests.

```sh
npm run test:all
```

### test:e2e

Runs Cypress e2e tests.

```sh
npm run test:e2e
```

### test:e2e:dev

Runs Cypress e2e tests on watch mode with an open instance of Chrome.

```sh
npm run test:e2e:dev
```

### format

Formats code and docs according to our style guidelines.

```sh
npm run format
```

## Pull Requests

We actively welcome your pull requests!

If you need help with Git or our workflow, please ask in our [community chat](https://decapcms.org/chat). We want your contributions even if you're just learning Git. Our maintainers are happy to help!

Decap CMS uses the [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow) + [Feature Branches](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow). Additionally, PR's should be [rebased](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) on main when opened, and again before merging.

1. Fork the repo.
2. Create a branch from `main`. If you're addressing a specific issue, prefix your branch name with the issue number.
3. If you've added code that should be tested, add tests.
4. If you've changed APIs, update the documentation.
5. Run `npm run test` and ensure the test suite passes.
6. Use `npm run format` to format and lint your code.
7. PR's must be rebased before merge (feel free to ask for help).
8. PR should be reviewed by two maintainers prior to merging.

## Debugging

`npm run start` spawns a development server and uses `dev-test/config.yml` and `dev-test/index.html` to serve the CMS.
In order to debug a specific issue follow the next steps:

1. Replace `dev-test/config.yml` with the relevant `config.yml`. If you want to test the backend, make sure that the `backend` property of the config indicates which backend you use (GitHub, Gitlab, Bitbucket etc) and path to the repo.

```js
backend:
  name: github
  repo: owner-name/repo-name
```

2. Change the content of `dev-test/index.html` to:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Decap CMS</title>
  </head>
  <body>
    <script src="dist/decap-cms.js"></script>
    <!-- <script>
      // this is the place to add CMS customizations if you need to, e.g.
      CMS.registerPreviewTemplate('posts', PostPreview);
    </script> -->
  </body>
</html>
```
The most important thing is to make sure that Decap CMS is loaded from the `dist` folder. This way, every time you make changes to the source code, they will be compiled and reflected immediately on `localhost`.

3. Run `npm run start`
4. Open `http://localhost:8080/` in the browser and you should have access to the CMS

### Debugging Git Gateway

When debugging the CMS with Git Gateway you must:

1. Have a Netlify site with [Git Gateway](https://docs.netlify.com/visitor-access/git-gateway/) and [Netlify Identity](https://docs.netlify.com/visitor-access/identity/) enabled. An easy way to create such a site is to use a [template](https://www.decapcms.org/docs/start-with-a-template/), for example the [Gatsby template](https://app.netlify.com/start/deploy?repository=https://github.com/decaporg/gatsby-starter-decap-cms&stack=cms)
2. Tell the CMS the URL of your Netlify site using a local storage item. To do so:

    1. Open `http://localhost:8080/` in the browser
    2. Open the Developer Console. Write the below command and press enter: `localStorage.setItem('netlifySiteURL', 'https://yourwebsiteurl.netlify.app/')`
    3. To be sure, you can run this command as well: `localStorage.getItem('netlifySiteURL')`
    4. Refresh the page
    5. You should be able to log in via your Netlify Identity email/password

### Fine tune the way you run unit tests

There are situations where you would want to run a specific test file, or tests that match a certain pattern.

To run all the tests for a specific file, use this command:

```
npx jest <filename or file path>
```

The first part of the command, `npx jest` means running the locally installed version of `jest`. It is equivalent to running `node_modules/.bin/jest`.

Example for running all the tests for the file `gitlab.spec.js`: `npx jest gitlab.spec.js`

Some test files like `API.spec.js` is available in several packages. You can pass a regexp pattern instead of file path to narrow down files.

Example for running all the tests for the file `API.spec.js` in the `decap-cms-backend-gitlab` package:

`npx jest ".+backend-gitlab/.+/API.spec.js`

To run a specific test in a file, add the flag `--testNamePattern`, or `-t` for short followed by a regexp to match your test name.

Example for running the test "should return true on project access_level >= 30" in the API.spec.js in `decap-cms-backend-gitlab` package:

```
npx jest -t "true on p" ".+backend-gitlab/.+/API.spec.js"
```

For more information about running tests exactly the way you want, check out the official documentation for [Jest CLI](https://jestjs.io/docs/cli).

## Releasing

Decap CMS uses NPM trusted publishers with OIDC for secure, automated package publishing.

### How It Works

- Publishing is automated via GitHub Actions when version tags are pushed
- Uses OpenID Connect (OIDC) for authentication. No NPM tokens required
- Each package has a trusted publisher configured on npmjs.com
- Workflow generates short-lived, cryptographically-signed tokens automatically
- Publishes all changed packages in the monorepo via Lerna

### Release Process

1. **Prepare the release:**
  ```sh
  # Ensure your local `main` branch is up to date
  npm prune
  npm install
  npm run test

  # Bump versions for changed packages
  npx lerna version

  # This will:
  # - Detect changed packages since last release
  # - Bump versions according to conventional commits
  # - Update CHANGELOG.md
  # - Create git commit and tags
  # - Push to upstream
  ```

2. **Automated publishing:**
   - Tags pushed to `main` trigger the publish workflow automatically
   - GitHub Actions runs tests and builds packages
   - Lerna publishes changed packages to npm using OIDC
   - Provenance attestations are generated automatically

3. **Create GitHub release:**
   - Go to [Releases](https://github.com/decaporg/decap-cms/releases)
   - Draft a new release from the tag
   - Add release notes highlighting changes

### Manual Publishing (Emergency Only)

If automated publishing fails and you need to publish manually:

```sh
# Authenticate with npm (uses session-based auth with 2FA)
npm login

# Publish changed packages
npm run lerna:publish
```

Note: Manual publishing still requires 2FA. Use recovery codes if you don't have access to your 2FA device.

## License

By contributing to Decap CMS, you agree that your contributions will be licensed
under its [MIT license](LICENSE).


================================================
FILE: LICENSE
================================================
Copyright (c) 2016 Netlify <decap@p-m.si>

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
![Decap CMS](/img/decap.svg)

[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/decaporg/decap-cms/blob/main/LICENSE) [![Netlify Status](https://api.netlify.com/api/v1/badges/8b87160b-0a11-4f75-8050-1d21bc1cff8c/deploy-status)](https://app.netlify.com/sites/decap-www/deploys) [![npm version](https://img.shields.io/npm/v/decap-cms.svg?style=flat)](https://www.npmjs.com/package/decap-cms) [![Build Status](https://github.com/decaporg/decap-cms/workflows/Node%20CI/badge.svg)](https://github.com/decaporg/decap-cms/actions?query=branch%3Amain+workflow%3A%22Node+CI%22) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/decaporg/decap-cms/blob/main/CONTRIBUTING.md)

[decapcms.org](https://www.decapcms.org/)

A CMS for static site generators. Give users a simple way to edit and add content to any site built with a static site generator.

_Decap CMS is the new name of Netlify CMS [since February 2023](https://www.netlify.com/blog/netlify-cms-to-become-decap-cms/)._

<a href="https://decapcms.org/chat">Join us on Discord</a> for community chat.

## How It Works

Decap CMS is a single-page app that you pull into the `/admin` part of your site.

It presents a clean UI for editing content stored in a Git repository.

You setup a YAML config to describe the content model of your site, and typically
tweak the main layout of the CMS a bit to fit your own site.

When a user navigates to `/admin/` they'll be prompted to log in, and once authenticated
they'll be able to create new content or edit existing content.

Read more about Decap CMS [Core Concepts](https://www.decapcms.org/docs/intro/).

## Installation and Configuration

The Decap CMS can be used in two different ways.

* A Quick and easy install, that requires you to create a single HTML file and a configuration file. All the CMS JavaScript and CSS are loaded from a CDN.
  To learn more about this installation method, refer to the [Quick Start Guide](https://www.decapcms.org/docs/quick-start/)
* A complete, more complex install, that gives you more flexibility but requires that you use a static site builder with a build system that supports npm packages.

## Sponsor

Help support Decap CMS development by becoming a sponsor! Your contributions help us maintain and improve this open-source project.

[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?style=for-the-badge&logo=github)](https://github.com/sponsors/decaporg)
[![Open Collective](https://img.shields.io/badge/Sponsor-Open%20Collective-blue?style=for-the-badge&logo=opencollective)](https://opencollective.com/decap)

These are our sponsors on both platforms. Thank you for your support!

![Open Collective Backers](https://opencollective.com/decap/backers.svg?limit=30&button=false&avatarHeight=48&width=400)

<!-- sponsors --><a href="https://github.com/Zwyx"><img src="https://github.com/Zwyx.png" width="48px" alt="Zwyx" style="border-radius:50%" /></a> &nbsp;<a href="https://github.com/smolcodes"><img src="https://github.com/smolcodes.png" width="48px" alt="smolcodes" style="border-radius:50%" /></a> &nbsp;<a href="https://github.com/shizik"><img src="https://github.com/shizik.png" width="48px" alt="shizik" style="border-radius:50%" /></a> &nbsp;<a href="https://github.com/JacquesRaoult"><img src="https://github.com/JacquesRaoult.png" width="48px" alt="JacquesRaoult" style="border-radius:50%" /></a> &nbsp;<!-- sponsors -->

## Contribute

New contributors are always welcome! Check out [CONTRIBUTING.md](https://github.com/decaporg/decap-cms/blob/main/CONTRIBUTING.md) to get involved.

## Change Log

This project adheres to [Semantic Versioning](http://semver.org/).
Every release is documented on the GitHub [Releases](https://github.com/decaporg/decap-cms/releases) page.

## License

Decap CMS is released under the [MIT License](LICENSE).
Please make sure you understand its [implications and guarantees](https://writing.kemitchell.com/2016/09/21/MIT-License-Line-by-Line.html).

## Maintainers

Maintained with care by <a href="https://techhub.p-m.si/">PM TechHub</a> & friends.

## Professional help

Our partners offer a range of services that can help you get the most out of Decap CMS. Find onboarding, priority support, and development of custom features.

[Read more on our professional help page](https://decapcms.org/services/)


================================================
FILE: SECURITY.md
================================================
# Security Policy

Decap CMS takes security seriously. This document outlines our security policy, supported versions, and how to report security vulnerabilities.

## Supported Versions

Security updates are provided for:

| Version | Status | Lifecycle |
|---------|--------|-----------|
| 3.x | ✅ Actively Supported | Current stable release |
| 2.x (Netlify CMS) | ❌ Unsupported | Legacy - no updates |
| 1.x (Netlify CMS) | ❌ Unsupported | Legacy - no updates |

**Note:** Decap CMS was renamed from Netlify CMS in February 2023. Versions 1.x and 2.x are no longer maintained. We recommend upgrading to version 3.x for security updates and new features.

## Reporting a Vulnerability

If you discover a security vulnerability in Decap CMS, please report it **confidentially** through GitHub Security Advisories. This allows us to investigate and address the issue without exposing it to the public until a fix is ready.

**Submit your report at:** https://github.com/decaporg/decap-cms/security/advisories/new

### What NOT to Do

- Do not open a public GitHub issue for the vulnerability
- Do not post details on social media or public forums
- Do not attempt to exploit the vulnerability beyond confirming it exists
- Do not access data beyond what's necessary to demonstrate the issue

## Response Timeline

This project follows a 90-day disclosure timeline.

## Security Practices

- Dependabot is enabled for automated security update checks
- All code changes are tested in CI, including linting
- End-to-end tests provide coverage of critical functionality
- All pull requests require code review before merging
- Passwords are not stored by Decap CMS; authentication is delegated to providers

## Known Limitations

- This is a **community-maintained open-source project**, not a commercial product with dedicated security resources
- Security depends on the stability and practices of underlying dependencies and backend providers
- Some vulnerabilities in dependencies may not be immediately patchable if they break backwards compatibility
- This is a project with a long history, and many legacy dependencies can't be updated without significant refactoring


================================================
FILE: __mocks__/styleMock.js
================================================
module.exports = {};


================================================
FILE: babel.config.js
================================================
const path = require('path');

const appVersion = require('./packages/decap-cms-app/package.json').version;
const coreVersion = require('./packages/decap-cms-core/package.json').version;
const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
const isESM = process.env.NODE_ENV === 'esm';

console.log('Build Package:', path.basename(process.cwd()));

// Always enabled plugins
const basePlugins = ['babel-plugin-inline-json-import'];

// All legacy transforms have been removed as they are now included in @babel/preset-env
// Features like class properties, optional chaining, nullish coalescing are now standard in modern JS

const defaultPlugins = [...basePlugins];

const svgo = {
  plugins: [
    {
      name: 'preset-default',
      params: {
        overrides: {
          removeViewBox: false,
        },
      },
    },
  ],
};

function presets() {
  return [
    '@babel/preset-react',
    ...(!isESM ? [['@babel/preset-env', {}]] : []),
    [
      '@emotion/babel-preset-css-prop',
      {
        autoLabel: 'always',
      },
    ],
    '@babel/preset-typescript',
  ];
}

function plugins() {
  if (isESM) {
    return [
      ...defaultPlugins,
      [
        'transform-define',
        {
          DECAP_CMS_APP_VERSION: `${appVersion}`,
          DECAP_CMS_CORE_VERSION: `${coreVersion}`,
        },
      ],
      [
        'inline-react-svg',
        {
          svgo,
        },
      ],
      [
        'inline-import',
        {
          extensions: ['.css'],
        },
      ],
    ];
  }

  if (isTest) {
    return [
      ...defaultPlugins,
      [
        'inline-react-svg',
        {
          svgo,
        },
      ],
    ];
  }

  if (!isProduction) {
    return [...defaultPlugins];
  }

  return defaultPlugins;
}

module.exports = {
  presets: presets(),
  plugins: plugins(),
};


================================================
FILE: commitlint.config.js
================================================
module.exports = { extends: ['@commitlint/config-conventional'] };


================================================
FILE: cypress/Readme.md
================================================
# Cypress Tests Guide

## Introduction

[Cypress](https://www.cypress.io/) is a JavaScript End to End Testing Framework that runs in the browser.

Cypress tests run with a [local version](../dev-test) of the CMS.

During the setup of a spec file, the relevant `index.html` and `config.yml` are copied from `dev-test/backends/<backend>` to `dev-test`.

Tests for the `test` backend use mock data generated in `dev-test/backends/test/index.html`.

Tests for the other backends use previously [recorded data](fixtures) and stub `fetch` [calls](support/commands.js#L52). See more about recording tests data [here](#recording-tests-data).

## Run Tests Locally

```bash
npm run test:e2e # builds the demo site and runs Cypress in headless mode with mock data
```

## Debug Tests

```bash
npm run develop # starts a local dev server with the demo site
npm run test:e2e:exec # runs Cypress in non-headless mode with mock data
```

## Recording Tests Data

When recording tests, access to the relevant backend API is required, thus one must set up a `.env` file in the root project directory in the following format:

```bash
GITHUB_REPO_OWNER=owner
GITHUB_REPO_NAME=repo
GITHUB_REPO_TOKEN=tokenWithWritePermissions
GITHUB_OPEN_AUTHORING_OWNER=forkOwner
GITHUB_OPEN_AUTHORING_TOKEN=tokenWithWritePermissions

GITLAB_REPO_OWNER=owner
GITLAB_REPO_NAME=repo
GITLAB_REPO_TOKEN=tokenWithWritePermissions

BITBUCKET_REPO_OWNER=owner
BITBUCKET_REPO_NAME=repo
BITBUCKET_OUATH_CONSUMER_KEY=ouathConsumerKey
BITBUCKET_OUATH_CONSUMER_SECRET=ouathConsumerSecret

NETLIFY_API_TOKEN=netlifyApiToken
NETLIFY_INSTALLATION_ID=netlifyGitHubInstallationId
```

> The structure of the relevant repo should match the settings in [`config.yml`](../dev-test/backends/<backend>/config.yml#L1)

To start a recording run the following commands:

```bash
npm run develop # starts a local dev server with the demo site
npm run mock:server:start # starts the recording proxy
npm run test:e2e:record-fixtures:dev # runs Cypress in non-headless and pass data through the recording proxy
npm run mock:server:stop # stops the recording proxy
```

> During the recorded process a clone of the relevant repo will be created under `.temp` and reset between tests.

Recordings are sanitized from any possible sensitive data and [transformed](plugins/common.js#L34) into an easier to process format.

To avoid recording all the tests over and over again, a recommended process is to:

1. Mark the specific test as `only` by changing `it("some test...` to `it.only("some test...` for the relevant test.
2. Run the test in recording mode.
3. Exit Cypress and stop the proxy.
4. Run the test normally (with mock data) to verify the recording works.

## Debugging Playback Failures

Most common failures are:

1. The [recorded data](utils/mock-server.js#L17) is not [transformed](plugins/common.js#L34) properly (e.g. sanitization broke something).
2. The [stubbed requests and responses](support/commands.js#L82) are not [matched](support/commands.js#L32) properly (e.g. timestamp changes in request body between recording and playback).

Dumping all recorded data as is to a file [here](utils/mock-server.js#L24) and adding a `debugger;` statement [here](support/commands.js#L52) is useful to gain insights.

Also comparing console log messages between recording and playback is very useful (ordering of requests, etc.)


================================================
FILE: cypress/e2e/common/editorial_workflow.js
================================================
import '../../utils/dismiss-local-backup';
import {
  login,
  createPost,
  createPostAndExit,
  updateExistingPostAndExit,
  exitEditor,
  goToWorkflow,
  goToCollections,
  updateWorkflowStatus,
  publishWorkflowEntry,
  assertWorkflowStatusInEditor,
  assertPublishedEntry,
  deleteEntryInEditor,
  assertOnCollectionsPage,
  assertEntryDeleted,
  assertWorkflowStatus,
  updateWorkflowStatusInEditor,
} from '../../utils/steps';
import { workflowStatus, editorStatus } from '../../utils/constants';

export default function({ entries, getUser }) {
  it('successfully loads', () => {
    login(getUser());
  });

  it('can create an entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
  });

  it('can update an entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    updateExistingPostAndExit(entries[0], entries[1]);
  });

  it('can publish an editorial workflow entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entries[0]);
  });

  it('can change workflow status', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
    updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.ready);
    updateWorkflowStatus(entries[0], workflowStatus.ready, workflowStatus.review);
    updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.draft);
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
  });

  it('can change status on and publish multiple entries', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    createPostAndExit(entries[1]);
    createPostAndExit(entries[2]);
    goToWorkflow();
    updateWorkflowStatus(entries[2], workflowStatus.draft, workflowStatus.ready);
    updateWorkflowStatus(entries[1], workflowStatus.draft, workflowStatus.ready);
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entries[2]);
    publishWorkflowEntry(entries[1]);
    publishWorkflowEntry(entries[0]);
    goToCollections();
    assertPublishedEntry([entries[2], entries[1], entries[0]]);
  });

  it('can delete an entry', () => {
    login(getUser());
    createPost(entries[0]);
    deleteEntryInEditor();
    assertOnCollectionsPage();
    assertEntryDeleted(entries[0]);
  });

  it('can update workflow status from within the editor', () => {
    login(getUser());
    createPost(entries[0]);
    assertWorkflowStatusInEditor(editorStatus.draft);
    updateWorkflowStatusInEditor(editorStatus.review);
    assertWorkflowStatusInEditor(editorStatus.review);
    updateWorkflowStatusInEditor(editorStatus.ready);
    assertWorkflowStatusInEditor(editorStatus.ready);
    exitEditor();
    goToWorkflow();
    assertWorkflowStatus(entries[0], workflowStatus.ready);
  });
}


================================================
FILE: cypress/e2e/common/editorial_workflow_migrations.js
================================================
import '../../utils/dismiss-local-backup';
import {
  login,
  createPostAndExit,
  goToWorkflow,
  goToCollections,
  updateWorkflowStatus,
  publishWorkflowEntry,
  assertPublishedEntry,
} from '../../utils/steps';
import { workflowStatus } from '../../utils/constants';

const versions = ['2.9.7', '2.10.24'];

export default function({ entries, getUser }) {
  versions.forEach(version => {
    it(`migrate from ${version} to latest`, () => {
      cy.task('switchToVersion', {
        version,
      });
      cy.reload();

      login(getUser());
      createPostAndExit(entries[0]);
      createPostAndExit(entries[1]);
      createPostAndExit(entries[2]);
      goToWorkflow();
      updateWorkflowStatus(entries[2], workflowStatus.draft, workflowStatus.ready);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(1500); // older versions of the CMS didn't wait fully for the update to be resolved
      updateWorkflowStatus(entries[1], workflowStatus.draft, workflowStatus.ready);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(1500); // older versions of the CMS didn't wait fully for the update to be resolved
      updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(1500); // older versions of the CMS didn't wait fully for the update to be resolved

      cy.task('switchToVersion', {
        version: 'latest',
      });
      cy.reload();

      // allow migration code to run for 5 minutes
      publishWorkflowEntry(entries[2], 5 * 60 * 1000);
      publishWorkflowEntry(entries[1]);
      publishWorkflowEntry(entries[0]);
      goToCollections();
      assertPublishedEntry([entries[2], entries[1], entries[0]]);
    });
  });
}


================================================
FILE: cypress/e2e/common/entries.js
================================================
export const entry1 = {
  title: 'first title',
  body: 'first body',
  description: 'first description',
  category: 'first category',
  tags: 'tag1',
};
export const entry2 = {
  title: 'second title',
  body: 'second body',
  description: 'second description',
  category: 'second category',
  tags: 'tag2',
};
export const entry3 = {
  title: 'third title',
  body: 'third body',
  description: 'third description',
  category: 'third category',
  tags: 'tag3',
};


================================================
FILE: cypress/e2e/common/i18n.js
================================================
import { newPost, populateEntry, publishEntry, flushClockAndSave } from '../../utils/steps';

const enterTranslation = str => {
  cy.get(`[id^="title-field"]`)
    .first()
    .clear({ force: true });
  cy.get(`[id^="title-field"]`)
    .first()
    .type(str, { force: true });
};

const createAndTranslate = entry => {
  newPost();
  // fill the main entry
  populateEntry(entry, () => undefined);

  // fill the translation
  cy.get('.Pane2').within(() => {
    enterTranslation('de');

    cy.contains('span', 'Writing in DE').click();
    cy.contains('span', 'fr').click();

    enterTranslation('fr');
  });
};

export const updateTranslation = () => {
  cy.get('.Pane2').within(() => {
    enterTranslation('fr fr');

    cy.contains('span', 'Writing in FR').click();
    cy.contains('span', 'de').click();

    enterTranslation('de de');
  });
  cy.contains('button', 'Save').click();
};

export const assertTranslation = () => {
  cy.get('.Pane2').within(() => {
    cy.get(`[id^="title-field"]`).should('have.value', 'de');

    cy.contains('span', 'Writing in DE').click();
    cy.contains('span', 'fr').click();

    cy.get(`[id^="title-field"]`).should('have.value', 'fr');
  });
};

export const createEntryTranslateAndPublish = entry => {
  createAndTranslate(entry);
  publishEntry();
};

export const createEntryTranslateAndSave = entry => {
  createAndTranslate(entry);
  flushClockAndSave();
};


================================================
FILE: cypress/e2e/common/i18n_editorial_workflow_spec.js
================================================
import '../../utils/dismiss-local-backup';
import {
  login,
  goToWorkflow,
  updateWorkflowStatus,
  exitEditor,
  publishWorkflowEntry,
  goToEntry,
  updateWorkflowStatusInEditor,
  publishEntryInEditor,
  assertPublishedEntryInEditor,
  assertUnpublishedEntryInEditor,
  assertUnpublishedChangesInEditor,
} from '../../utils/steps';
import { createEntryTranslateAndSave, assertTranslation, updateTranslation } from './i18n';
import { workflowStatus, editorStatus, publishTypes } from '../../utils/constants';

export default function({ entry, getUser }) {
  const structures = ['multiple_folders', 'multiple_files', 'single_file'];
  structures.forEach(structure => {
    it(`can create and publish entry with translation in ${structure} mode`, () => {
      cy.task('updateConfig', { i18n: { structure } });

      login(getUser());

      createEntryTranslateAndSave(entry);
      assertUnpublishedEntryInEditor();
      exitEditor();
      goToWorkflow();
      updateWorkflowStatus(entry, workflowStatus.draft, workflowStatus.ready);
      publishWorkflowEntry(entry);
      goToEntry(entry);
      assertTranslation();
      assertPublishedEntryInEditor();
    });

    it(`can update translated entry in ${structure} mode`, () => {
      cy.task('updateConfig', { i18n: { structure: 'multiple_folders' } });

      login(getUser());

      createEntryTranslateAndSave(entry);
      assertUnpublishedEntryInEditor();
      updateWorkflowStatusInEditor(editorStatus.ready);
      publishEntryInEditor(publishTypes.publishNow);
      exitEditor();
      goToEntry(entry);
      assertTranslation();
      assertPublishedEntryInEditor();
      updateTranslation();
      assertUnpublishedChangesInEditor();
    });
  });
}


================================================
FILE: cypress/e2e/common/media_library.js
================================================
import '../../utils/dismiss-local-backup';
import {
  login,
  goToMediaLibrary,
  newPost,
  populateEntry,
  exitEditor,
  goToWorkflow,
  updateWorkflowStatus,
  publishWorkflowEntry,
  goToEntry,
  goToCollections,
} from '../../utils/steps';
import { workflowStatus } from '../../utils/constants';

function uploadMediaFile() {
  assertNoImagesInLibrary();

  const fixture = 'cypress/fixtures/media/netlify.png';
  cy.get('input[type="file"]').selectFile(fixture, { force: true });
  cy.contains('span', 'Uploading...').should('not.exist');

  assertImagesInLibrary();
}

function assertImagesInLibrary() {
  cy.get('img[class*="CardImage"]').should('exist');
}

function assertNoImagesInLibrary() {
  cy.get('h1')
    .contains('Loading...')
    .should('not.exist');
  cy.get('img[class*="CardImage"]').should('not.exist');
}

function deleteImage() {
  cy.get('img[class*="CardImage"]').click();
  cy.contains('button', 'Delete selected').click();
  assertNoImagesInLibrary();
}

function chooseSelectedMediaFile() {
  cy.contains('button', 'Choose selected').should('not.be.disabled');
  cy.contains('button', 'Choose selected').click();
}

function chooseAnImage() {
  cy.contains('button', 'Choose an image').click();
}

function waitForEntryToLoad() {
  cy.contains('button', 'Saving...').should('not.exist');
  cy.clock().tick(5000);
  cy.contains('div', 'Loading entry...').should('not.exist');
}

function matchImageSnapshot() {
  // cy.matchImageSnapshot();
}

function newPostAndUploadImage() {
  newPost();
  chooseAnImage();
  uploadMediaFile();
}

function newPostWithImage(entry) {
  newPostAndUploadImage();
  chooseSelectedMediaFile();
  populateEntry(entry);
  waitForEntryToLoad();
}

function publishPostWithImage(entry) {
  newPostWithImage(entry);
  // eslint-disable-next-line cypress/no-unnecessary-waiting
  cy.wait(500);
  exitEditor();
  goToWorkflow();
  updateWorkflowStatus(entry, workflowStatus.draft, workflowStatus.ready);
  publishWorkflowEntry(entry);
}

function closeMediaLibrary() {
  cy.get('button[class*="CloseButton"]').click();
}

function switchToGridView() {
  cy.get('div[class*="ViewControls"]').within(() => {
    cy.get('button')
      .last()
      .click();
  });
}

function assertGridEntryImage(entry) {
  cy.contains('li', entry.title).within(() => {
    cy.get('div[class*="CardImage"]').should('be.visible');
  });
}

export default function({ entries, getUser }) {
  beforeEach(() => {
    console.log('[media_library.beforeEach] START');
    const user = getUser && getUser();
    console.log('[media_library.beforeEach] user=', user ? JSON.stringify(user) : 'none');
    login(user);
    console.log('[media_library.beforeEach] login() returned');
  });

  it('can upload image from global media library', () => {
    console.log('[TEST] can upload image from global media library - START');
    goToMediaLibrary();
    console.log('[TEST] goToMediaLibrary() completed');
    uploadMediaFile();
    console.log('[TEST] uploadMediaFile() completed');
    matchImageSnapshot();
    closeMediaLibrary();
    console.log('[TEST] can upload image from global media library - END');
  });

  it('can delete image from global media library', () => {
    goToMediaLibrary();
    uploadMediaFile();
    closeMediaLibrary();
    goToMediaLibrary();
    deleteImage();
    matchImageSnapshot();
    closeMediaLibrary();
  });

  it('can upload image from entry media library', () => {
    newPostAndUploadImage();
    matchImageSnapshot();
    closeMediaLibrary();
    exitEditor();
  });

  it('can save entry with image', () => {
    newPostWithImage(entries[0]);
    matchImageSnapshot();
    exitEditor();
  });

  it('can publish entry with image', () => {
    publishPostWithImage(entries[0]);
    goToEntry(entries[0]);
    waitForEntryToLoad();
    matchImageSnapshot();
  });

  it('should not show draft entry image in global media library', () => {
    newPostWithImage(entries[0]);
    cy.clock().then(clock => {
      if (clock) {
        clock.tick(150);
        clock.tick(150);
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.wait(500);
      }
      exitEditor();
      goToMediaLibrary();
      assertNoImagesInLibrary();
      matchImageSnapshot();
    });
  });

  it('should show published entry image in global media library', () => {
    publishPostWithImage(entries[0]);
    cy.clock().tick();
    goToMediaLibrary();
    assertImagesInLibrary();
    matchImageSnapshot();
  });

  it('should show published entry image in grid view', () => {
    publishPostWithImage(entries[0]);
    goToCollections();
    switchToGridView();
    assertGridEntryImage(entries[0]);

    matchImageSnapshot();
  });
}


================================================
FILE: cypress/e2e/common/open_authoring.js
================================================
import '../../utils/dismiss-local-backup';
import {
  login,
  createPostAndExit,
  updateExistingPostAndExit,
  goToWorkflow,
  deleteWorkflowEntry,
  updateWorkflowStatus,
  publishWorkflowEntry,
} from '../../utils/steps';
import { workflowStatus } from '../../utils/constants';

export default function({ entries, getUser, getForkUser }) {
  it('successfully loads', () => {
    login(getUser());
  });

  it('can create an entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
  });

  it('can update an entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    updateExistingPostAndExit(entries[0], entries[1]);
  });

  it('can publish an editorial workflow entry', () => {
    login(getUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entries[0]);
  });

  it('successfully forks repository and loads', () => {
    login(getForkUser());
  });

  it('can create an entry on fork', () => {
    login(getForkUser());
    createPostAndExit(entries[0]);
  });

  it('can update a draft entry on fork', () => {
    login(getForkUser());
    createPostAndExit(entries[0]);
    updateExistingPostAndExit(entries[0], entries[1]);
  });

  it('can change entry status from fork', () => {
    login(getForkUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
  });

  it('can delete review entry from fork', () => {
    login(getForkUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
    deleteWorkflowEntry(entries[0]);
  });

  it('can return entry to draft and delete it', () => {
    login(getForkUser());
    createPostAndExit(entries[0]);
    goToWorkflow();
    updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);

    updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.draft);
    deleteWorkflowEntry(entries[0]);
  });
}


================================================
FILE: cypress/e2e/common/simple_workflow.js
================================================
import '../../utils/dismiss-local-backup';
import { login, createPostAndPublish, assertPublishedEntry } from '../../utils/steps';

export default function({ entries, getUser }) {
  it('successfully loads', () => {
    login(getUser());
  });

  it('can create an entry', () => {
    login(getUser());
    createPostAndPublish(entries[0]);
    assertPublishedEntry(entries[0]);
  });
}


================================================
FILE: cypress/e2e/common/spec_utils.js
================================================
export function before(taskResult, options, backend) {
  console.log(`[spec_utils.before] START backend=${backend}`);
  Cypress.config('taskTimeout', 5 * 60 * 1000); // 5 minutes
  cy.task('setupBackend', { backend, options }).then(data => {
    console.log('[spec_utils.before] setupBackend completed, data=', data);
    taskResult.data = data;
    Cypress.config('defaultCommandTimeout', data.mockResponses ? 5 * 1000 : 1 * 60 * 1000);
    console.log(`[spec_utils.before] COMPLETE mockResponses=${data.mockResponses} timeout=${data.mockResponses ? 5000 : 60000}ms`);
  });
}

export function after(taskResult, backend) {
  console.log(`[spec_utils.after] START backend=${backend}`);
  cy.task('teardownBackend', {
    backend,
    ...taskResult.data,
  }).then(() => {
    console.log('[spec_utils.after] COMPLETE');
  });
}

export function beforeEach(taskResult, backend) {
  const spec = Cypress.mocha.getRunner().suite.ctx.currentTest.parent.title;
  const testName = Cypress.mocha.getRunner().suite.ctx.currentTest.title;

  console.log(`[spec_utils.beforeEach] START backend=${backend} spec="${spec}" test="${testName}"`);
  console.log(`[spec_utils.beforeEach] mockResponses=${taskResult.data.mockResponses}`);
  console.log(`[spec_utils.beforeEach] user=`, JSON.stringify(taskResult.data.user || {}));

  cy.task('setupBackendTest', {
    backend,
    ...taskResult.data,
    spec,
    testName,
  }).then(() => {
    console.log('[spec_utils.beforeEach] setupBackendTest completed');
  });

  if (taskResult.data.mockResponses) {
    const fixture = `${spec}__${testName}.json`;
    console.log(`[spec_utils.beforeEach] Loading fixture: ${fixture}`);
    cy.stubFetch({ fixture }).then(() => {
      console.log('[spec_utils.beforeEach] stubFetch completed');
    });
  } else {
    console.log('[spec_utils.beforeEach] WARNING: mockResponses is false/undefined - no fixture loaded');
  }

  // cy.clock(0, ['Date']) was hanging git-gateway tests after page load
  // Hypothesis: freezing time to 0 breaks app initialization during cy.visit()
  // Temporary fix: skip cy.clock for git-gateway, use default clock for others
  if (backend !== 'git-gateway') {
    console.log('[spec_utils.beforeEach] Setting clock to epoch 0');
    return cy.clock(0, ['Date']);
  }

  console.log('[spec_utils.beforeEach] COMPLETE - skipped clock for git-gateway');
}

export function afterEach(taskResult, backend) {
  const spec = Cypress.mocha.getRunner().suite.ctx.currentTest.parent.title;
  const testName = Cypress.mocha.getRunner().suite.ctx.currentTest.title;

  cy.task('teardownBackendTest', {
    backend,
    ...taskResult.data,
    spec,
    testName,
  });

  if (!process.env.RECORD_FIXTURES) {
    const {
      suite: {
        ctx: {
          currentTest: { state, _retries: retries, _currentRetry: currentRetry },
        },
      },
    } = Cypress.mocha.getRunner();

    if (state === 'failed' && retries === currentRetry) {
      Cypress.runner.stop();
    }
  }
}

export function seedRepo(taskResult, backend) {
  cy.task('seedRepo', {
    backend,
    ...taskResult.data,
  });
}


================================================
FILE: cypress/e2e/editorial_workflow_spec_bitbucket_backend.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'bitbucket';

describe('BitBucket Backend Editorial Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow' }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_git-gateway_github_backend.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'git-gateway';
const provider = 'github';

describe('Git Gateway (GitHub) Backend Editorial Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', provider }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_git-gateway_gitlab_backend.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'git-gateway';
const provider = 'gitlab';

describe('Git Gateway (GitLab) Backend Editorial Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', provider }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_github_backend_graphql.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Editorial Workflow - GraphQL API', () => {
  const taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: true, open_authoring: false },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_github_backend_graphql_open_authoring.js
================================================
import fixture from './common/open_authoring';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Editorial Workflow - GraphQL API - Open Authoring', () => {
  const taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: true, open_authoring: true },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
    getForkUser: () => taskResult.data.forkUser,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_github_backend_rest.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Editorial Workflow - REST API', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: false, open_authoring: false },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_github_backend_rest_open_authoring.js
================================================
import fixture from './common/open_authoring';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Editorial Workflow - REST API - Open Authoring', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: false, open_authoring: true },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
    getForkUser: () => taskResult.data.forkUser,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_gitlab_backend.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'gitlab';

describe('GitLab Backend Editorial Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow' }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    if (
      Cypress.mocha.getRunner().suite.ctx.currentTest.title ===
      'can change status on and publish multiple entries'
    ) {
      Cypress.mocha.getRunner().suite.ctx.currentTest.skip();
    }
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_proxy_git_backend.js
================================================
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'proxy';
const mode = 'git';

describe.skip(`Proxy Backend Editorial Workflow - '${mode}' mode`, () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', mode }, backend);
    Cypress.config('defaultCommandTimeout', 5 * 1000);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/editorial_workflow_spec_test_backend.js
================================================
import '../utils/dismiss-local-backup';
import {
  login,
  createPost,
  createPostAndExit,
  exitEditor,
  goToWorkflow,
  goToCollections,
  updateWorkflowStatus,
  publishWorkflowEntry,
  assertWorkflowStatusInEditor,
  assertPublishedEntry,
  deleteEntryInEditor,
  assertOnCollectionsPage,
  assertEntryDeleted,
  assertWorkflowStatus,
  updateWorkflowStatusInEditor,
  unpublishEntry,
  publishEntryInEditor,
  duplicateEntry,
  goToEntry,
  populateEntry,
  publishAndCreateNewEntryInEditor,
  publishAndDuplicateEntryInEditor,
  assertNotification,
  assertFieldValidationError,
} from '../utils/steps';
import { workflowStatus, editorStatus, publishTypes, notifications } from '../utils/constants';

const entry1 = {
  title: 'first title',
  body: 'first body',
};
const entry2 = {
  title: 'second title',
  body: 'second body',
};
const entry3 = {
  title: 'third title',
  body: 'third body',
};

describe('Test Backend Editorial Workflow', () => {
  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  beforeEach(() => {
    cy.task('updateConfig', { collections: [{ publish: true }] });
  });

  it('successfully loads', () => {
    login();
  });

  it('can create an entry', () => {
    login();
    createPost(entry1);

    // new entry should show 'Delete unpublished entry'
    cy.contains('button', 'Delete unpublished entry');
    cy.url().should(
      'eq',
      `http://localhost:8080/#/collections/posts/entries/1970-01-01-${entry1.title
        .toLowerCase()
        .replace(/\s/, '-')}`,
    );
    exitEditor();
  });

  it('can publish an editorial workflow entry', () => {
    login();
    createPostAndExit(entry1);
    goToWorkflow();
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entry1);
  });

  it('can update an entry', () => {
    login();
    createPostAndExit(entry1);
    goToWorkflow();
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entry1);

    goToEntry(entry1);
    populateEntry(entry2);
    // existing entry should show 'Delete unpublished changes'
    cy.contains('button', 'Delete unpublished changes');
    // existing entry slug should remain the same after save'
    cy.url().should(
      'eq',
      `http://localhost:8080/#/collections/posts/entries/1970-01-01-${entry1.title
        .toLowerCase()
        .replace(/\s/, '-')}`,
    );
    exitEditor();
  });

  it('can change workflow status', () => {
    login();
    createPostAndExit(entry1);
    goToWorkflow();
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.review);
    updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.ready);
    updateWorkflowStatus(entry1, workflowStatus.ready, workflowStatus.review);
    updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.draft);
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
  });

  it('can change status on and publish multiple entries', () => {
    login();
    createPostAndExit(entry1);
    createPostAndExit(entry2);
    createPostAndExit(entry3);
    goToWorkflow();
    updateWorkflowStatus(entry3, workflowStatus.draft, workflowStatus.ready);
    updateWorkflowStatus(entry2, workflowStatus.draft, workflowStatus.ready);
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entry3);
    publishWorkflowEntry(entry2);
    publishWorkflowEntry(entry1);
    goToCollections();
    assertPublishedEntry([entry3, entry2, entry1]);
  });

  it('can delete an entry', () => {
    login();
    createPost(entry1);
    deleteEntryInEditor();
    assertOnCollectionsPage();
    assertEntryDeleted(entry1);
  });

  it('can update workflow status from within the editor', () => {
    login();
    createPost(entry1);
    assertWorkflowStatusInEditor(editorStatus.draft);
    updateWorkflowStatusInEditor(editorStatus.review);
    assertWorkflowStatusInEditor(editorStatus.review);
    updateWorkflowStatusInEditor(editorStatus.ready);
    assertWorkflowStatusInEditor(editorStatus.ready);
    exitEditor();
    goToWorkflow();
    assertWorkflowStatus(entry1, workflowStatus.ready);
  });

  it('can unpublish an existing entry', () => {
    // first publish an entry
    login();
    createPostAndExit(entry1);
    goToWorkflow();
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
    publishWorkflowEntry(entry1);
    // then unpublish it
    unpublishEntry(entry1);
  });

  it('can duplicate an existing entry', () => {
    login();
    createPost(entry1);
    updateWorkflowStatusInEditor(editorStatus.ready);
    publishEntryInEditor(publishTypes.publishNow);
    duplicateEntry(entry1);
  });

  it('cannot publish when "publish" is false', () => {
    cy.task('updateConfig', { collections: [{ publish: false }] });
    login();
    createPost(entry1);
    cy.contains('span', 'Publish').should('not.exist');
    exitEditor();
    goToWorkflow();
    updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
    cy.contains('button', 'Publish new entry').should('not.exist');
  });

  it('can create a new entry, publish and create new', () => {
    login();
    createPost(entry1);
    updateWorkflowStatusInEditor(editorStatus.ready);

    publishAndCreateNewEntryInEditor(entry1);
  });

  it('can create a new entry, publish and duplicate', () => {
    login();
    createPost(entry1);
    updateWorkflowStatusInEditor(editorStatus.ready);
    publishAndDuplicateEntryInEditor(entry1);
  });

  const inSidebar = func => {
    cy.get('[class*=SidebarNavList]').within(func);
  };

  const inGrid = func => {
    cy.get('[class*=CardsGrid]').within(func);
  };

  it('can access nested collection items', () => {
    login();

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', /^Directory$/));
    inGrid(() => cy.contains('a', 'Root Page'));

    inSidebar(() => cy.contains('a', /^Directory$/).click());

    inSidebar(() => cy.contains('a', /^Sub Directory$/));
    inSidebar(() => cy.contains('a', 'Another Sub Directory'));

    inSidebar(() => cy.contains('a', /^Sub Directory$/).click());
    inSidebar(() => cy.contains('a', 'Nested Directory'));
    cy.url().should(
      'eq',
      'http://localhost:8080/#/collections/pages/filter/directory/sub-directory',
    );

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', 'Pages').click());

    inGrid(() => cy.contains('a', 'Another Sub Directory').should('not.exist'));
  });

  it('can navigate to nested entry', () => {
    login();

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inSidebar(() => cy.contains('a', 'Another Sub Directory').click());
    inGrid(() => cy.contains('a', 'Another Sub Directory'));
  });

  it(`can create a new entry with custom path`, () => {
    login();

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inSidebar(() => cy.contains('a', /^Sub Directory$/).click());
    cy.contains('a', 'New Page').click();

    cy.get('[id^="path-field"]').should('have.value', 'directory/sub-directory');
    cy.get('[id^="path-field"]').type('/new-path');
    cy.get('[id^="title-field"]').type('New Path Title');
    cy.clock().then(clock => {
      clock.tick(150);
    });
    cy.contains('button', 'Save').click();
    assertNotification(notifications.saved);
    updateWorkflowStatusInEditor(editorStatus.ready);
    publishEntryInEditor(publishTypes.publishNow);
    exitEditor();

    inSidebar(() => cy.contains('a', 'New Path Title'));
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inGrid(() => cy.contains('a', 'New Path Title').should('not.exist'));
  });

  it(`can't create an entry with an existing path`, () => {
    login();

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inSidebar(() => cy.contains('a', /^Sub Directory$/).click());

    cy.contains('a', 'New Page').click();
    cy.get('[id^="title-field"]').type('New Path Title');
    cy.clock().then(clock => {
      clock.tick(150);
    });
    cy.contains('button', 'Save').click();

    assertFieldValidationError({
      message: `Path 'directory/sub-directory' already exists`,
      fieldLabel: 'Path',
    });
  });

  it('can move an existing entry to a new path', () => {
    login();

    inSidebar(() => cy.contains('a', 'Pages').click());
    inSidebar(() => cy.contains('a', /^Directory$/).click());
    inGrid(() => cy.contains('a', /^Directory$/).click());

    cy.get('[id^="path-field"]').should('have.value', 'directory');
    cy.get('[id^="path-field"]').clear();
    cy.get('[id^="path-field"]').type('new-directory');
    cy.get('[id^="title-field"]').clear();
    cy.get('[id^="title-field"]').type('New Directory');
    cy.clock().then(clock => {
      clock.tick(150);
    });
    cy.contains('button', 'Save').click();
    assertNotification(notifications.saved);
    updateWorkflowStatusInEditor(editorStatus.ready);
    publishEntryInEditor(publishTypes.publishNow);
    exitEditor();

    inSidebar(() => cy.contains('a', 'New Directory').click());

    inSidebar(() => cy.contains('a', /^Sub Directory$/));
    inSidebar(() => cy.contains('a', 'Another Sub Directory'));
  });
});


================================================
FILE: cypress/e2e/field_validations_spec.js
================================================
import '../utils/dismiss-local-backup';
import {
  login,
  validateObjectFieldsAndExit,
  validateNestedObjectFieldsAndExit,
  validateListFieldsAndExit,
  validateNestedListFieldsAndExit,
} from '../utils/steps';
import { setting1, setting2 } from '../utils/constants';

const nestedListConfig = {
  collections: [
    {},
    {},
    {
      name: 'settings',
      label: 'Settings',
      editor: { preview: false },
      files: [
        {},
        {},
        {
          name: 'hotel_locations',
          label: 'Hotel Locations',
          file: '_data/hotel_locations.yml',
          fields: [
            {
              label: 'Country',
              name: 'country',
              widget: 'string',
            },
            {
              label: 'Hotel Locations',
              name: 'hotel_locations',
              widget: 'list',
              fields: [
                {
                  label: 'Cities',
                  name: 'cities',
                  widget: 'list',
                  fields: [
                    {
                      label: 'City',
                      name: 'city',
                      widget: 'string',
                    },
                    {
                      label: 'Number of Hotels in City',
                      name: 'number_of_hotels_in_city',
                      widget: 'number',
                    },
                    {
                      label: 'City Locations',
                      name: 'city_locations',
                      widget: 'list',
                      fields: [
                        {
                          label: 'Hotel Name',
                          name: 'hotel_name',
                          widget: 'string',
                        },
                      ],
                    },
                  ],
                },
              ],
            },
          ],
        },
      ],
    },
  ],
};

describe('Test Backend Editorial Workflow', () => {
  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
  });

  beforeEach(() => {
    cy.task('setupBackend', { backend: 'test' });
  });

  it('can validate object fields', () => {
    login();
    validateObjectFieldsAndExit(setting1);
  });

  it('can validate fields nested in an object field', () => {
    login();
    validateNestedObjectFieldsAndExit(setting1);
  });

  it('can validate list fields', () => {
    login();
    validateListFieldsAndExit(setting2);
  });

  it('can validate deeply nested list fields', () => {
    cy.task('updateConfig', nestedListConfig);

    login();
    validateNestedListFieldsAndExit(setting2);
  });
});


================================================
FILE: cypress/e2e/i18n_editorial_workflow_spec_test_backend.js
================================================
import fixture from './common/i18n_editorial_workflow_spec';

const backend = 'test';

describe(`I18N Test Backend Editorial Workflow`, () => {
  const taskResult = { data: {} };

  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', {
      backend,
      options: {
        publish_mode: 'editorial_workflow',
        i18n: {
          locales: ['en', 'de', 'fr'],
        },
        collections: [
          {
            folder: 'content/i18n',
            i18n: true,
            fields: [{ i18n: true }, {}, { i18n: 'duplicate' }],
          },
        ],
      },
    });
  });

  after(() => {
    cy.task('teardownBackend', { backend });
  });

  const entry = {
    title: 'first title',
    body: 'first body',
  };

  fixture({ entry, getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/i18n_simple_workflow_spec_proxy_fs_backend.js
================================================
import * as specUtils from './common/spec_utils';
import { login } from '../utils/steps';
import { createEntryTranslateAndPublish } from './common/i18n';

const backend = 'proxy';
const mode = 'fs';

const expectedEnContent = `---
template: post
title: first title
date: 1970-01-01T01:00
description: first description
category: first category
tags:
  - tag1
---
`;

const expectedDeContent = `---
title: de
date: 1970-01-01T01:00
---
`;

const expectedFrContent = `---
title: fr
date: 1970-01-01T01:00
---
`;

const contentSingleFile = `---
en:
  template: post
  date: 1970-01-01T01:00
  title: first title
  body: first body
  description: first description
  category: first category
  tags:
    - tag1
de:
  date: 1970-01-01T01:00
  title: de
fr:
  date: 1970-01-01T01:00
  title: fr
---
`;

describe(`I18N Proxy Backend Simple Workflow - '${mode}' mode`, () => {
  const taskResult = { data: {} };

  const entry = {
    title: 'first title',
    body: 'first body',
    description: 'first description',
    category: 'first category',
    tags: 'tag1',
  };

  before(() => {
    specUtils.before(
      taskResult,
      {
        mode,
        publish_mode: 'simple',
        i18n: {
          locales: ['en', 'de', 'fr'],
        },
        collections: [{ i18n: true, fields: [{}, { i18n: true }, {}, { i18n: 'duplicate' }] }],
      },
      backend,
    );
    Cypress.config('taskTimeout', 15 * 1000);
    Cypress.config('defaultCommandTimeout', 5 * 1000);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  it('can create entry with translation in locale_folders mode', () => {
    cy.task('updateConfig', { i18n: { structure: 'multiple_folders' } });

    login(taskResult.data.user);

    createEntryTranslateAndPublish(entry);

    cy.readFile(`${taskResult.data.tempDir}/content/posts/en/1970-01-01-first-title.md`).should(
      'contain',
      expectedEnContent,
    );

    cy.readFile(`${taskResult.data.tempDir}/content/posts/de/1970-01-01-first-title.md`).should(
      'eq',
      expectedDeContent,
    );

    cy.readFile(`${taskResult.data.tempDir}/content/posts/fr/1970-01-01-first-title.md`).should(
      'eq',
      expectedFrContent,
    );
  });

  it('can create entry with translation in single_file mode', () => {
    cy.task('updateConfig', { i18n: { structure: 'multiple_files' } });

    login(taskResult.data.user);

    createEntryTranslateAndPublish(entry);

    cy.readFile(`${taskResult.data.tempDir}/content/posts/1970-01-01-first-title.en.md`).should(
      'contain',
      expectedEnContent,
    );

    cy.readFile(`${taskResult.data.tempDir}/content/posts/1970-01-01-first-title.de.md`).should(
      'eq',
      expectedDeContent,
    );

    cy.readFile(`${taskResult.data.tempDir}/content/posts/1970-01-01-first-title.fr.md`).should(
      'eq',
      expectedFrContent,
    );
  });

  it('can create entry with translation in locale_file_extensions mode', () => {
    cy.task('updateConfig', { i18n: { structure: 'single_file' } });

    login(taskResult.data.user);

    createEntryTranslateAndPublish(entry);

    cy.readFile(`${taskResult.data.tempDir}/content/posts/1970-01-01-first-title.md`).should(
      'eq',
      contentSingleFile,
    );
  });
});


================================================
FILE: cypress/e2e/markdown_widget_backspace_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget', () => {

  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });

  });

  beforeEach(() => {
    cy.loginAndNewPost();
    cy.clearMarkdownEditorContent();
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  // describe('pressing backspace', () => {
    it('sets non-default block to default when empty', () => {
      cy.focused()
        .clickHeadingOneButton()
        .backspace()
        .confirmMarkdownEditorContent(`
          <p></p>
        `);
    });
    it('moves to previous block when no character left to delete', () => {
      cy.focused()
        .type('foo')
        .enter()
        .clickHeadingOneButton()
        .type('a')
        .backspace({times: 2})
        .confirmMarkdownEditorContent(`
          <p>foo</p>
        `);
    });
    it('does nothing at start of first block in document when non-empty and non-default', () => {
      cy.focused()
        .clickHeadingOneButton()
        .type('foo')
        .setCursorBefore('foo')
        .backspace({ times: 4 })
        .confirmMarkdownEditorContent(`
          <h1>foo</h1>
        `);
    });
    it('deletes individual characters in middle of non-empty non-default block in document', () => {
      cy.focused()
        .clickHeadingOneButton()
        .type('foo')
        .setCursorAfter('fo')
        .backspace({ times: 3 })
        .confirmMarkdownEditorContent(`
          <h1>o</h1>
        `);
    });
    it('at beginning of non-first block, moves default block content to previous block', () => {
      cy.focused()
        .clickHeadingOneButton()
        .type('foo')
        .enter()
        .type('bar')
        .setCursorBefore('bar')
        .backspace()
        .confirmMarkdownEditorContent(`
          <h1>foobar</h1>
        `);
    });
    it('at beginning of non-first block, moves non-default block content to previous block', () => {
      cy.focused()
        .type('foo')
        .enter()
        .clickHeadingOneButton()
        .type('bar')
        .enter()
        .clickHeadingTwoButton()
        .type('baz')
        .setCursorBefore('baz')
        .backspace()
        .confirmMarkdownEditorContent(`
          <p>foo</p>
          <h1>barbaz</h1>
        `)
        .setCursorBefore('bar')
        .backspace()
        .confirmMarkdownEditorContent(`
          <p>foobarbaz</p>
        `);
    // });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_code_block_spec.js
================================================
import { oneLineTrim, stripIndent } from 'common-tags';
import '../utils/dismiss-local-backup';

describe('Markdown widget code block', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  beforeEach(() => {
    cy.loginAndNewPost();
    cy.clearMarkdownEditorContent();
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });
  describe('code block', () => {
    it('outputs code', () => {
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy
        .insertCodeBlock()
        .type('foo')
        .enter()
        .type('bar')
        .confirmMarkdownEditorContent(
          `
          ${codeBlock(`
            foo
            bar
          `)}
        `,
        )
        .wait(500)
        .clickModeToggle().confirmMarkdownEditorContent(`
          ${codeBlockRaw(`
            foo
            bar
          `)}
        `);
    });
  });
});

function codeBlockRaw(content) {
  return ['```', ...stripIndent(content).split('\n'), '```']
    .map(
      line => oneLineTrim`
    <div>
      <span>
        <span>
          <span>${line}</span>
        </span>
      </span>
    </div>
  `,
    )
    .join('');
}

function codeBlock(content) {
  const lines = stripIndent(content)
    .split('\n')
    .map(
      (line, idx) => `
    <div>
      <div>
        <div>${idx + 1}</div>
      </div>
      <pre><span>${line}</span></pre>
    </div>
  `,
    )
    .join('');

  return oneLineTrim`
    <div>
      <div></div>
      <div>
        <div>
          <div><label>Code Block</label></div>
          <div><button><span><svg>
                  <path></path>
                </svg></span></button>
            <div>
              <div>
                <div><textarea></textarea></div>
                <div>
                  <div></div>
                </div>
                <div>
                  <div></div>
                </div>
                <div></div>
                <div></div>
                <div>
                  <div>
                    <div>
                      <div>
                        <div>
                          <div>
                            <pre><span>xxxxxxxxxx</span></pre>
                          </div>
                          <div></div>
                          <div></div>
                          <div>
                            <div> </div>
                          </div>
                          <div>
                            ${lines}
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div></div>
                  <div>
                    <div></div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div>
            <span>
                <span>
                    <span></span>
                </span>
            </span>
        </div>
      </div>
      <div></div>
    </div>
  `;
}


================================================
FILE: cypress/e2e/markdown_widget_enter_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget breaks', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });

  });

  beforeEach(() => {
    cy.loginAndNewPost();
    cy.clearMarkdownEditorContent();
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  describe('pressing enter', () => {
    it('creates new default block from empty block', () => {
      cy.focused()
        .enter()
        .confirmMarkdownEditorContent(`
          <p></p>
          <p></p>
        `);
    });
    it('creates new default block when selection collapsed at end of block', () => {
      cy.focused()
        .type('foo')
        .enter()
        .confirmMarkdownEditorContent(`
          <p>foo</p>
          <p></p>
        `);
    });
    it('creates new default block when selection collapsed at end of non-default block', () => {
      cy.clickHeadingOneButton()
        .type('foo')
        .enter()
        .confirmMarkdownEditorContent(`
          <h1>foo</h1>
          <p></p>
        `);
    });
    it('creates new default block when selection collapsed in empty non-default block', () => {
      cy.clickHeadingOneButton()
        .enter()
        .confirmMarkdownEditorContent(`
          <h1></h1>
          <p></p>
        `);
    });
    it('splits block into two same-type blocks when collapsed selection at block start', () => {
      cy.clickHeadingOneButton()
        .type('foo')
        .setCursorBefore('foo')
        .enter()
        .confirmMarkdownEditorContent(`
          <h1></h1>
          <h1>foo</h1>
        `);
    });
    it('splits block into two same-type blocks when collapsed in middle of selection at block start', () => {
      cy.clickHeadingOneButton()
        .type('foo')
        .setCursorBefore('oo')
        .enter()
        .confirmMarkdownEditorContent(`
          <h1>f</h1>
          <h1>oo</h1>
        `);
    });
    it('deletes selected content and splits to same-type block when selection is expanded', () => {
      cy.clickHeadingOneButton()
        .type('foo bar')
        .setSelection('o b')
        .enter()
        .confirmMarkdownEditorContent(`
          <h1>fo</h1>
          <h1>ar</h1>
        `);
    });
  });

  describe('pressing shift+enter', () => {
    it('creates line break', () => {
      cy.focused()
        .enter({ shift: true })
        .confirmMarkdownEditorContent(`
          <p>
            <br>
          </p>
        `);
    });
    it('creates consecutive line break', () => {
      cy.focused()
        .enter({ shift: true, times: 4 })
        .confirmMarkdownEditorContent(`
          <p>
            <br>
            <br>
            <br>
            <br>
          </p>
        `);
    });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_hotkeys_spec.js
================================================
import '../utils/dismiss-local-backup';
import {HOT_KEY_MAP} from "../utils/constants";
const headingNumberToWord = ['', 'one', 'two', 'three', 'four', 'five', 'six'];
const isMac = Cypress.platform === 'darwin';
const modifierKey = isMac ? '{meta}' : '{ctrl}';
// eslint-disable-next-line func-style
const replaceMod = (str) => str.replace(/mod\+/g, modifierKey).replace(/shift\+/g, '{shift}');

describe('Markdown widget hotkeys', () => {
  describe('hot keys', () => {
    before(() => {
      Cypress.config('defaultCommandTimeout', 4000);
      cy.task('setupBackend', { backend: 'test' });
    });

    beforeEach(() => {
      cy.loginAndNewPost();
      cy.clearMarkdownEditorContent();
      cy.focused()
        .type('foo')
        .setSelection('foo').as('selection');
    });

    after(() => {
      cy.task('teardownBackend', { backend: 'test' });
    });

    describe('bold', () => {
      it('pressing mod+b bolds the text', () => {
        cy.get('@selection')
          .type(replaceMod(HOT_KEY_MAP['bold']))
          .confirmMarkdownEditorContent(`
            <p>
              <strong>foo</strong>
            </p>
          `)
          .type(replaceMod(HOT_KEY_MAP['bold']));
      });
    });

    describe('italic', () => {
      it('pressing mod+i italicizes the text', () => {
        cy.get('@selection')
          .type(replaceMod(HOT_KEY_MAP['italic']))
          .confirmMarkdownEditorContent(`
            <p>
              <em>foo</em>
            </p>
          `)
          .type(replaceMod(HOT_KEY_MAP['italic']));
      });
    });

    describe('strikethrough', () => {
      it('pressing mod+shift+s displays a strike through the text', () => {
        cy.get('@selection')
          .type(replaceMod(HOT_KEY_MAP['strikethrough']))
          .confirmMarkdownEditorContent(`
            <p>
              <s>foo</s>
            </p>
          `).type(replaceMod(HOT_KEY_MAP['strikethrough']));
      });
    });

    describe('code', () => {
      it('pressing mod+shift+c displays a code block around the text', () => {
        cy.get('@selection')
          .type(replaceMod(HOT_KEY_MAP['code']))
          .confirmMarkdownEditorContent(`
            <p>
              <code>foo</code>
            </p>
          `).type(replaceMod(HOT_KEY_MAP['code']));
      });
    });

    describe('link', () => {
      before(() => {

      });
      it('pressing mod+k transforms the text to a link', () => {
        cy.window().then((win) => {
          cy.get('@selection')
          .type(replaceMod(HOT_KEY_MAP['link']))
          cy.stub(win, 'prompt').returns('https://google.com');
          cy.confirmMarkdownEditorContent('<p><a>foo</a></p>')
          .type(replaceMod(HOT_KEY_MAP['link']));
        });


      });
    });

    describe('headings', () => {
      for (let i = 1; i <= 6; i++) {
        it(`pressing mod+${i} transforms the text to a heading`, () => {
            cy.get('@selection')
              .type(replaceMod(HOT_KEY_MAP[`heading-${headingNumberToWord[i]}`]))
              .confirmMarkdownEditorContent(`<h${i}>foo</h${i}>`)
              .type(replaceMod(HOT_KEY_MAP[`heading-${headingNumberToWord[i]}`]))
        });
      }
    });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_link_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget link', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  beforeEach(() => {
    cy.loginAndNewPost();
    cy.clearMarkdownEditorContent();
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  describe('link', () => {
    it('can add a new valid link', () => {
      const link = 'https://www.decapcms.org/';
      cy.window().then(win => {
        cy.stub(win, 'prompt').returns(link);
      });
      cy.focused().clickLinkButton();

      cy.confirmMarkdownEditorContent(`<p><a>${link}</a></p>`);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(300);
      cy.clickModeToggle();

      cy.confirmRawEditorContent(`<${link}>`);
    });

    it('can add a new invalid link', () => {
      const link = 'www.decapcms.org';
      cy.window().then(win => {
        cy.stub(win, 'prompt').returns(link);
      });
      cy.focused().clickLinkButton();

      cy.confirmMarkdownEditorContent(`<p><a>${link}</a></p>`);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(300);
      cy.clickModeToggle();

      cy.confirmRawEditorContent(`[${link}](${link})`);
    });

    it('can select existing text as link', () => {
      const link = 'https://www.decapcms.org';
      cy.window().then(win => {
        cy.stub(win, 'prompt').returns(link);
      });

      const text = 'Decap CMS';
      cy.focused()
        .getMarkdownEditor()
        .type(text)
        .setSelection(text)
        .clickLinkButton();

      cy.confirmMarkdownEditorContent(`<p><a>${text}</a></p>`);
      // eslint-disable-next-line cypress/no-unnecessary-waiting
      cy.wait(300);
      cy.clickModeToggle();

      cy.confirmRawEditorContent(`[${text}](${link})`);
    });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_list_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget', () => {
  describe('list', () => {
    before(() => {
      Cypress.config('defaultCommandTimeout', 4000);
      cy.task('setupBackend', { backend: 'test' });
    });

    beforeEach(() => {
      cy.loginAndNewPost();
      cy.clearMarkdownEditorContent();
    });

    after(() => {
      cy.task('teardownBackend', { backend: 'test' });
    });

    // describe('toolbar buttons', () => {
      it('creates and focuses empty list', () => {
        cy.clickUnorderedListButton().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p></p>
              </li>
            </ul>
          `);
      });

      it('removes list', () => {
        cy.clickUnorderedListButton().clickUnorderedListButton().confirmMarkdownEditorContent(`
            <p></p>
          `);
      });

      it('converts a list item to a paragraph block which is a sibling of the parent list', () => {
        cy.clickUnorderedListButton().type('foo').enter().clickUnorderedListButton()
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
            </ul>
            <p></p>
          `);
      });

      it('converts empty nested list item to empty paragraph block in parent list item', () => {
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .tabkey()
          .type('bar')
          .enter()
          .tabkey()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                    <ul>
                      <li>
                        <p></p>
                      </li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .clickUnorderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                    <p></p>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .backspace({ times: 4 })
          .clickUnorderedListButton().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
                <p></p>
              </li>
            </ul>
          `);
      });

      it('moves nested list item content to parent list item when in first block', () => {
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .tabkey()
          .type('bar')
          .enter()
          .tabkey()
          .type('baz')
          .clickUnorderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                    <p>baz</p>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .up()
          .clickUnorderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <p>bar</p>
                <p>baz</p>
              </li>
            </ul>
          `,
          )
          .up()
          .clickUnorderedListButton().confirmMarkdownEditorContent(`
            <p>foo</p>
            <p>bar</p>
            <p>baz</p>
          `);
      });

      it('affects only the current block with collapsed selection', () => {
        cy
          .focused()
          .type('foo')
          .enter()
          .type('bar')
          .enter()
          .type('baz')
          .up()
          .clickUnorderedListButton().confirmMarkdownEditorContent(`
            <p>foo</p>
            <ul>
              <li>
                <p>bar</p>
              </li>
            </ul>
            <p>baz</p>
          `);
      });

      it('wrap each bottom-most block in a selection with a list item block', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy
          .focused()
          .type('foo')
          .enter()
          .type('bar')
          .enter()
          .type('baz')
          .setSelection('foo', 'baz')
          .wait(500)
          .clickUnorderedListButton().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `);
      });

      it('unwraps list item block from each selected list item and unwraps all of them from the outer list block', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .type('bar')
          .enter()
          .type('baz')
          .setSelection('foo', 'baz')
          .wait(500)
          .clickUnorderedListButton().confirmMarkdownEditorContent(`
            <p>foo</p>
            <p>bar</p>
            <p>baz</p>
          `);
      });

      it('combines adjacent same-typed lists, not differently typed lists', () => {
        cy.focused()
          .type('foo')
          .enter()
          .type('bar')
          .enter()
          .type('baz')
          .up()
          .clickUnorderedListButton()
          .up()
          .clickUnorderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
              </li>
            </ul>
            <p>baz</p>
          `,
          )
          .down({ times: 2 })
          .focused()
          .clickUnorderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `,
          )
          .up()
          .enter()
          .type('qux')
          .tabkey()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
                <ul>
                  <li>
                    <p>qux</p>
                  </li>
                </ul>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `,
          )
          .up()
          .enter()
          .type('quux')
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
                <ul>
                  <li>
                    <p>quux</p>
                  </li>
                  <li>
                    <p>qux</p>
                  </li>
                </ul>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `,
          )
          .clickOrderedListButton()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
                <ol>
                  <li>
                    <p>quux</p>
                  </li>
                </ol>
                <ul>
                  <li>
                    <p>qux</p>
                  </li>
                </ul>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `,
          )
          .setSelection({
            anchorQuery: 'ul > li > ol p',
            anchorOffset: 1,
            focusQuery: 'ul > li > ul:last-child p',
            focusOffset: 2,
          });
      });

      // while this works on dev environment, it will always fail in cypress - has something to do with text selection
      // it('affects only selected list items', () => {
      //   cy
      //     .clickUnorderedListButton()
      //     .type('foo')
      //     .enter()
      //     .type('bar')
      //     .enter()
      //     .type('baz')
      //     .setSelection('bar')
      //     .clickUnorderedListButton()
      //     .confirmMarkdownEditorContent(
      //       `
      //       <ul>
      //         <li>
      //           <p>foo</p>
      //         </li>
      //       </ul>
      //       <p>bar</p>
      //       <ul>
      //         <li>
      //           <p>baz</p>
      //         </li>
      //       </ul>
      //     `,
      //     )
      //     .clickUnorderedListButton()
      //     .setSelection('bar', 'baz')
      //     .clickUnorderedListButton()
      //     .confirmMarkdownEditorContent(
      //       `
      //       <ul>
      //         <li>
      //           <p>foo</p>
      //         </li>
      //       </ul>
      //       <p>bar</p>
      //       <p>baz</p>
      //     `,
      //     )
      //     .clickUnorderedListButton()
      //     .confirmMarkdownEditorContent(
      //       `
      //       <ul>
      //         <li>
      //           <p>foo</p>
      //         </li>
      //         <li>
      //           <p>bar</p>
      //         </li>
      //         <li>
      //           <p>baz</p>
      //         </li>
      //       </ul>
      //     `,
      //     )
      //     .setSelection('baz')
      //     .clickUnorderedListButton()
      //     .confirmMarkdownEditorContent(
      //       `
      //       <ul>
      //         <li>
      //           <p>foo</p>
      //         </li>
      //         <li>
      //           <p>bar</p>
      //         </li>
      //       </ul>
      //       <p>baz</p>
      //     `,
      //     )
      //     .clickUnorderedListButton()
      //     .tabkey()
      //     .setCursorAfter('baz')
      //     .enter()
      //     .tabkey()
      //     .type('qux')
      //     .confirmMarkdownEditorContent(
      //       `
      //     <ul>
      //       <li>
      //         <p>foo</p>
      //       </li>
      //       <li>
      //         <p>bar</p>
      //         <ul>
      //           <li>
      //             <p>baz</p>
      //             <ul>
      //               <li>
      //                 <p>qux</p>
      //               </li>
      //             </ul>
      //           </li>
      //         </ul>
      //       </li>
      //     </ul>
      //     `,
      //     )
      //     .setSelection('baz')
      //     .clickOrderedListButton()
      //     .confirmMarkdownEditorContent(
      //       `
      //     <ul>
      //       <li>
      //         <p>foo</p>
      //       </li>
      //       <li>
      //         <p>bar</p>
      //         <ol>
      //           <li>
      //             <p>baz</p>
      //             <ul>
      //               <li>
      //                 <p>qux</p>
      //               </li>
      //             </ul>
      //           </li>
      //         </ol>
      //       </li>
      //     </ul>
      //     `,
      //     )
      //     .setCursorAfter('qux')
      //     .enter({ times: 2 })
      //     .clickUnorderedListButton()
      //     .confirmMarkdownEditorContent(`
      //       <ul>
      //         <li>
      //           <p>foo</p>
      //         </li>
      //         <li>
      //           <p>bar</p>
      //           <ol>
      //             <li>
      //               <p>baz</p>
      //               <ul>
      //                 <li>
      //                   <p>qux</p>
      //                 </li>
      //               </ul>
      //             </li>
      //           </ol>
      //           <ul>
      //             <li>
      //               <p></p>
      //             </li>
      //           </ul>
      //         </li>
      //       </ul>
      //     `);
      // });
    // });
// });

//     describe('on Enter', () => {
      it('removes the list item and list if empty', () => {
        cy.clickUnorderedListButton().enter().confirmMarkdownEditorContent(`
            <p></p>
          `);
      });

      it('creates a new list item in a non-empty list', () => {
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p></p>
              </li>
            </ul>
          `,
          )
          .type('bar')
          .enter().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
              </li>
              <li>
                <p></p>
              </li>
            </ul>
          `);
      });

      it('creates a new default block below a list when hitting Enter twice on an empty list item of the list', () => {
        cy.clickUnorderedListButton().type('foo').enter({ times: 2 }).confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
            </ul>
            <p></p>
          `);
      });
    // });

    // describe('on Backspace', () => {
      it('removes the list item and list if empty', () => {
        cy.clickUnorderedListButton().backspace().confirmMarkdownEditorContent(`
            <p></p>
          `);
      });

      it('removes the list item if list not empty', () => {
        cy.clickUnorderedListButton().type('foo').enter().backspace().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
                <p></p>
              </li>
            </ul>
          `);
      });

      it('does not remove list item if empty with non-default block', () => {
        cy.clickUnorderedListButton().clickHeadingOneButton().backspace()
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p></p>
              </li>
            </ul>
          `);
      });
    // });

    // describe('on Tab', () => {
      it('does nothing in top level list', () => {
        cy
          .clickUnorderedListButton()
          .tabkey()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p></p>
              </li>
            </ul>
          `,
          )
          .type('foo')
          .tabkey().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
            </ul>
          `);
      });

      it('indents nested list items', () => {
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .type('bar')
          .tabkey()
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .enter()
          .tabkey().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                    <ul>
                      <li>
                        <p></p>
                      </li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          `);
      });

      it('only nests up to one level down from the parent list', () => {
        cy.clickUnorderedListButton().type('foo').enter().tabkey().confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p></p>
                  </li>
                </ul>
              </li>
            </ul>
          `);
      });

      it('unindents nested list items with shift', () => {
        cy.clickUnorderedListButton().type('foo').enter().tabkey().tabkey({ shift: true })
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p></p>
              </li>
            </ul>
          `);
      });

      it('indents and unindents from one level below parent back to document root', () => {
        cy
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .tabkey()
          .type('bar')
          .enter()
          .tabkey()
          .type('baz')
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                    <ul>
                      <li>
                        <p>baz</p>
                      </li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .tabkey({ shift: true })
          .confirmMarkdownEditorContent(
            `
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                  </li>
                  <li>
                    <p>baz</p>
                  </li>
                </ul>
              </li>
            </ul>
          `,
          )
          .tabkey({ shift: true }).confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
                <ul>
                  <li>
                    <p>bar</p>
                  </li>
                </ul>
              </li>
              <li>
                <p>baz</p>
              </li>
            </ul>
          `);
      });
    // });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_marks_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget', () => {
  describe('code mark', () => {
    before(() => {
      Cypress.config('defaultCommandTimeout', 4000);
      cy.task('setupBackend', { backend: 'test' });

    });

    beforeEach(() => {
      cy.loginAndNewPost();
      cy.clearMarkdownEditorContent();
    });

    after(() => {
      cy.task('teardownBackend', { backend: 'test' });
    });

    describe('toolbar button', () => {
      it('can combine code mark with other marks', () => {
        cy.clickItalicButton()
          .type('foo')
          .setSelection('oo')
          .clickCodeButton()
          .confirmMarkdownEditorContent(`
            <p>
              <em>f</em>
              <code>
                <em>oo</em>
              </code>
            </p>
          `);
      });
    });
  });
});


================================================
FILE: cypress/e2e/markdown_widget_quote_spec.js
================================================
import '../utils/dismiss-local-backup';

describe('Markdown widget', () => {
  describe('quote block', () => {
    before(() => {
      Cypress.config('defaultCommandTimeout', 4000);
      cy.task('setupBackend', { backend: 'test' });
    });

    beforeEach(() => {
      cy.loginAndNewPost();
      cy.clearMarkdownEditorContent();
    });

    after(() => {
      cy.task('teardownBackend', { backend: 'test' });
    });

    // describe('toggle quote', () => {
      it('toggles empty quote block on and off in empty editor', () => {
        cy.clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p></p>
            </blockquote>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <p></p>
          `);
      });
      it('toggles empty quote block on and off for current block', () => {
        cy.focused()
          .type('foo')
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
            </blockquote>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <p>foo</p>
          `);
      });
      it('toggles entire quote block without expanded selection', () => {
        cy.clickQuoteButton()
          .type('foo')
          .enter()
          .type('bar')
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <p>foo</p>
            <p>bar</p>
          `);
      });
      it('toggles entire quote block with complex content', () => {
        cy.clickQuoteButton()
          .clickUnorderedListButton()
          .clickHeadingOneButton()
          .type('foo')
          .enter({ times: 2 }) // First Enter creates new list item. Second Enter turns that list item into a default block.
          .clickQuoteButton() // Unwrap the quote block.
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <h1>foo</h1>
              </li>
            </ul>
            <p></p>
          `);
      });
      it('toggles empty quote block on and off for selected blocks', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.focused()
          .type('foo')
          .enter()
          .type('bar')
          .setSelection('foo', 'bar')
          .wait(500)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>bar</p>
            </blockquote>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <p>foo</p>
            <p>bar</p>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>bar</p>
            </blockquote>
          `);
      });
      it('toggles empty quote block on and off for partially selected blocks', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.focused()
          .type('foo')
          .enter()
          .type('bar')
          .setSelection('oo', 'ba')
          .wait(500)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>bar</p>
            </blockquote>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <p>foo</p>
            <p>bar</p>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>bar</p>
            </blockquote>
          `);
      });
      it('toggles quote block on and off for multiple selected list items', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.focused()
          .clickUnorderedListButton()
          .type('foo')
          .enter()
          .type('bar')
          .setSelection('foo', 'bar')
          .wait(500)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <ul>
                <li>
                  <p>foo</p>
                </li>
                <li>
                  <p>bar</p>
                </li>
              </ul>
            </blockquote>
          `)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
              <li>
                <p>bar</p>
              </li>
            </ul>
          `)
          .setCursorAfter('bar')
          .wait(500)
          .enter()
          .type('baz')
          .setSelection('bar', 'baz')
          .wait(500)
          .clickQuoteButton()
          .confirmMarkdownEditorContent(`
            <ul>
              <li>
                <p>foo</p>
              </li>
            </ul>
            <blockquote>
              <ul>
                <li>
                  <p>bar</p>
                </li>
                <li>
                  <p>baz</p>
                </li>
              </ul>
            </blockquote>
          `)
      });
      it('creates new quote block if parent is not a quote, can deeply nest', () => {
        cy.clickQuoteButton()
          .clickUnorderedListButton()
          .clickQuoteButton()
          .clickUnorderedListButton()
          .clickQuoteButton()
          .clickUnorderedListButton()
          .clickQuoteButton()
          .type('foo')
          // Content should contains 4 <blockquote> tags and 3 <ul> tags
          .confirmMarkdownEditorContent(`
            <blockquote>
              <ul>
                <li>
                  <blockquote>
                    <ul>
                      <li>
                        <blockquote>
                          <ul>
                            <li>
                              <blockquote>
                                <p>foo</p>
                              </blockquote>
                            </li>
                          </ul>
                        </blockquote>
                      </li>
                    </ul>
                  </blockquote>
                </li>
              </ul>
            </blockquote>
          `)
          /*
           * First Enter creates new paragraph within the innermost block quote.
           * Second Enter moves that paragraph one level up to become sibling of the previous quote block and direct child of a list item.
           * Third Enter to turn that paragraph into a list item and move it one level up.
           * Repeat the circle for three more times to reach the second list item of the outermost list block.
           * Then Enter again to turn that list item into a paragraph and move it one level up to become sibling of the outermost list and
           * direct child of the outermost block quote.
          */
          .enter({ times: 10 })
          .type('bar')
          .confirmMarkdownEditorContent(`
            <blockquote>
              <ul>
                <li>
                  <blockquote>
                    <ul>
                      <li>
                        <blockquote>
                          <ul>
                            <li>
                              <blockquote>
                                <p>foo</p>
                              </blockquote>
                            </li>
                          </ul>
                        </blockquote>
                      </li>
                    </ul>
                  </blockquote>
                </li>
              </ul>
              <p>bar</p>
            </blockquote>
          `)
          /* The GOAL is to delete all the text content inside this deeply nested block quote and turn it into a default paragraph block on top level.
           * We need:
           *  3 Backspace to delete the word “bar”.
           *  1 Backspace to remove the paragraph that contains bar and bring cursor to the end of the unordered list which is direct child of the outermost block quote.
           *  3 Backspace to remove the word “foo”.
           *  1 Backspace to remove the current block quote that the cursor is on, 1 Backspace to remove the list that wraps the block quote. Repeat this step for three times for a total of 6 Backspace until the cursor is on the outermost block quote.
           * 1 Backspace to remove to toggle off the outermost block quote and turn it into a default paragraph.
           * Total Backspaces required: 3 + 1 + 3 + ((1 + 1) * 3) + 1 = 14
          */
          .backspace({ times: 14 })
      });
    // });

    // describe('backspace inside quote', () => {
      it('joins two paragraphs', () => {
        cy.clickQuoteButton()
          .type('foo')
          .enter()
          .type('bar')
          .setCursorBefore('bar')
          .backspace()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foobar</p>
            </blockquote>
          `);
      });
      it('joins quote with previous quote', () => {
        cy.clickQuoteButton()
          .type('foo')
          .enter({ times: 2 })
          .clickQuoteButton()
          .type('bar')
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
            </blockquote>
            <blockquote>
              <p>bar</p>
            </blockquote>
          `)
          .setCursorBefore('bar')
          .backspace()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>bar</p>
            </blockquote>
          `);
      });
      it('removes first block from quote when focused at first block at start', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.clickQuoteButton()
          .type('foo')
          .enter()
          .type('bar')
          .setCursorBefore('foo')
          .wait(500)
          .backspace()
          .confirmMarkdownEditorContent(`
            <p>foo</p>
            <blockquote>
              <p>bar</p>
            </blockquote>
          `)
      });
    // });

    // describe('enter inside quote', () => {
      it('creates new block inside quote', () => {
        // eslint-disable-next-line cypress/no-unnecessary-waiting
        cy.clickQuoteButton()
          .type('foo')
          .enter()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p></p>
            </blockquote>
          `)
          .type('bar')
          .setCursorAfter('ba')
          .wait(500)
          .enter()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
              <p>ba</p>
              <p>r</p>
            </blockquote>
          `);
      });
      it('creates new block after quote from empty last block', () => {
        cy.clickQuoteButton()
          .type('foo')
          .enter()
          .enter()
          .confirmMarkdownEditorContent(`
            <blockquote>
              <p>foo</p>
            </blockquote>
            <p></p>
          `)
      });
    // });
  });
});


================================================
FILE: cypress/e2e/media_library_spec_bitbucket_backend.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'bitbucket';

describe('BitBucket Backend Media Library - REST API', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, {}, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_bitbucket_backend_large_media.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'bitbucket';
const lfs = true;

describe('BitBucket Backend Media Library - Large Media', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { lfs }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_git-gateway_github_backend_large_media.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'git-gateway';
const provider = 'github';

describe('Git Gateway (GitHub) Backend Media Library - Large Media', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', provider }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_git-gateway_gitlab_backend_large_media.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'git-gateway';
const provider = 'gitlab';

describe('Git Gateway (GitLab) Backend Media Library - Large Media', () => {
  const taskResult = { data: {} };

  before(() => {
    console.log('[SPEC before] START');
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', provider }, backend);
    console.log('[SPEC before] COMPLETE, taskResult.data=', taskResult.data);
  });

  after(() => {
    console.log('[SPEC after] START');
    specUtils.after(taskResult, backend);
    console.log('[SPEC after] COMPLETE');
  });

  beforeEach(() => {
    console.log('[SPEC beforeEach] START, taskResult.data=', taskResult.data);
    specUtils.beforeEach(taskResult, backend);
    console.log('[SPEC beforeEach] COMPLETE');
  });

  afterEach(() => {
    console.log('[SPEC afterEach] START');
    specUtils.afterEach(taskResult, backend);
    console.log('[SPEC afterEach] COMPLETE');
  });

  console.log('[SPEC] About to call fixture()');
  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
  console.log('[SPEC] fixture() returned');
});


================================================
FILE: cypress/e2e/media_library_spec_github_backend_graphql.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'github';

describe('GitHub Backend Media Library - GraphQL API', () => {
  const taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: true },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_github_backend_rest.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'github';

describe('GitHub Backend Media Library - REST API', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: false },
        publish_mode: 'editorial_workflow',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_gitlab_backend.js
================================================
import fixture from './common/media_library';
import { entry1 } from './common/entries';
import * as specUtils from './common/spec_utils';

const backend = 'gitlab';

describe('GitLab Backend Media Library - REST API', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow' }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_proxy_git_backend.js
================================================
import fixture from './common/media_library';
import * as specUtils from './common/spec_utils';
import { entry1 } from './common/entries';

const backend = 'proxy';
const mode = 'git';

describe(`Proxy Backend Media Library - '${mode}' mode`, () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'editorial_workflow', mode }, backend);
    Cypress.config('defaultCommandTimeout', 5 * 1000);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({ entries: [entry1], getUser: () => taskResult.data.user });
});


================================================
FILE: cypress/e2e/media_library_spec_test_backend.js
================================================
import fixture from './common/media_library';

const entries = [
  {
    title: 'first title',
    body: 'first body',
  },
];

describe('Test Backend Media Library', () => {
  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  fixture({ entries });
});


================================================
FILE: cypress/e2e/search_suggestion_spec.js
================================================
import { login } from '../utils/steps';

const search = (term, collection) => {
  cy.get('[class*=SearchInput]').clear({ force: true });
  cy.get('[class*=SearchInput]').type(term, { force: true });
  cy.get('[class*=SuggestionsContainer]').within(() => {
    cy.contains(collection).click();
  });
};

const assertSearchHeading = title => {
  cy.get('[class*=SearchResultHeading]').should('have.text', title);
};

const assertSearchResult = (text, collection) => {
  cy.get('[class*=ListCardLink] h2').contains(collection ?? text)
};

const assertNotInSearch = text => {
  cy.get('[class*=ListCardLink] h2').contains(text).should('not.exist');
};

describe('Search Suggestion', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  beforeEach(() => {
    login();
  });

  it('can search in all collections', () => {
    search('this', 'All Collections');

    assertSearchHeading('Search Results for "this"');

    assertSearchResult('This is post # 20', 'Posts');
    assertSearchResult('This is a TOML front matter post', 'Posts');
    assertSearchResult('This is a JSON front matter post', 'Posts');
    assertSearchResult('This is a YAML front matter post', 'Posts');
    assertSearchResult('This FAQ item # 5', 'FAQ');
  });

  it('can search in posts collection', () => {
    search('this', 'Posts');

    assertSearchHeading('Search Results for "this" in Posts');

    assertSearchResult('This is post # 20');
    assertSearchResult('This is a TOML front matter post');
    assertSearchResult('This is a JSON front matter post');
    assertSearchResult('This is a YAML front matter post');

    assertNotInSearch('This FAQ item # 5');
  });

  it('can search in faq collection', () => {
    search('this', 'FAQ');

    assertSearchHeading('Search Results for "this" in FAQ');

    assertSearchResult('This FAQ item # 5');

    assertNotInSearch('This is post # 20');
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_bitbucket_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'bitbucket';

describe('BitBucket Backend Simple Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple' }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_git-gateway_github_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'git-gateway';
const provider = 'github';

describe('Git Gateway (GitHub) Backend Simple Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple', provider }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_git-gateway_gitlab_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'git-gateway';
const provider = 'gitlab';

describe('Git Gateway (GitLab) Backend Simple Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple', provider }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_github_backend_graphql.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Simple Workflow - GraphQL API', () => {
  const taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: true },
        publish_mode: 'simple',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_github_backend_rest.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'github';

describe('GitHub Backend Simple Workflow - REST API', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(
      taskResult,
      {
        backend: { use_graphql: false },
        publish_mode: 'simple',
      },
      backend,
    );
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_gitlab_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'gitlab';

describe('GitLab Backend Simple Workflow', () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple' }, backend);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_proxy_fs_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'proxy';
const mode = 'fs';

describe(`Proxy Backend Simple Workflow - '${mode}' mode`, () => {
  const taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple', mode }, backend);
    Cypress.config('defaultCommandTimeout', 5 * 1000);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_proxy_git_backend.js
================================================
import fixture from './common/simple_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'proxy';
const mode = 'git';

describe(`Proxy Backend Simple Workflow - '${mode}' mode`, () => {
  let taskResult = { data: {} };

  before(() => {
    specUtils.before(taskResult, { publish_mode: 'simple', mode }, backend);
    Cypress.config('defaultCommandTimeout', 5 * 1000);
  });

  after(() => {
    specUtils.after(taskResult, backend);
  });

  beforeEach(() => {
    if (Cypress.mocha.getRunner().suite.ctx.currentTest.title === 'can create an entry') {
      Cypress.mocha.getRunner().suite.ctx.currentTest.skip();
    }
    specUtils.beforeEach(taskResult, backend);
  });

  afterEach(() => {
    specUtils.afterEach(taskResult, backend);
  });

  fixture({
    entries: [entry1, entry2, entry3],
    getUser: () => taskResult.data.user,
  });
});


================================================
FILE: cypress/e2e/simple_workflow_spec_test_backend.js
================================================
import '../utils/dismiss-local-backup';
import {
  login,
  newPost,
  populateEntry,
  exitEditor,
  createPostAndPublish,
  assertPublishedEntry,
  editPostAndPublish,
  createPostPublishAndCreateNew,
  createPostPublishAndDuplicate,
  editPostPublishAndCreateNew,
  editPostPublishAndDuplicate,
  duplicatePostAndPublish,
} from '../utils/steps';

const entry1 = {
  title: 'first title',
  body: 'first body',
};
const entry2 = {
  title: 'second title',
  body: 'second body',
};

const backend = 'test';

describe('Test Backend Simple Workflow', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend, options: { publish_mode: 'simple' } });
  });

  after(() => {
    cy.task('teardownBackend', { backend });
  });

  it('successfully loads', () => {
    login();
  });

  it('can create a new entry', () => {
    login();
    newPost();
    populateEntry(entry1, () => {});

    // new entry should show 'Unsaved changes'
    cy.contains('div', 'Unsaved Changes');
    cy.url().should('eq', `http://localhost:8080/#/collections/posts/new`);
    exitEditor();
  });

  it('can publish a new entry', () => {
    login();
    createPostAndPublish(entry1);
    assertPublishedEntry(entry1);
  });

  it('can publish a new entry and create new', () => {
    login();
    createPostPublishAndCreateNew(entry1);
    assertPublishedEntry(entry1);
  });

  it('can publish a new entry and duplicate', () => {
    login();
    createPostPublishAndDuplicate(entry1);
    assertPublishedEntry(entry1);
  });

  it('can edit an existing entry and publish', () => {
    login();
    createPostAndPublish(entry1);
    assertPublishedEntry(entry1);

    editPostAndPublish(entry1, entry2);
  });

  it('can edit an existing entry, publish and create new', () => {
    login();
    createPostAndPublish(entry1);
    assertPublishedEntry(entry1);

    editPostPublishAndCreateNew(entry1, entry2);
  });

  it('can edit an existing entry, publish and duplicate', () => {
    login();
    createPostAndPublish(entry1);
    assertPublishedEntry(entry1);

    editPostPublishAndDuplicate(entry1, entry2);
  });

  it('can duplicate an existing entry', () => {
    login();
    createPostAndPublish(entry1);
    assertPublishedEntry(entry1);

    duplicatePostAndPublish(entry1);
  });
});


================================================
FILE: cypress/e2e/view_filters_spec.js
================================================
import { login } from '../utils/steps';

const filter = term => {
  cy.contains('span', 'Filter by').click();
  cy.contains(term).click();
  cy.contains('Contents').click();
};

const assertEntriesCount = count => {
  cy.get('[class*=ListCardLink]').should('have.length', count);
};

const assertInEntries = text => {
  cy.get('[class*=ListCardLink] h2').contains(text);
};

const assertNotInEntries = text => {
  cy.get('[class*=ListCardLink] h2').contains(text).should('not.exist');
};

describe('View Filter', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  beforeEach(() => {
    login();
  });

  it('can apply string filter', () => {
    // enable filter
    filter('Posts With Index');

    assertEntriesCount(20);
    for (let i = 1; i <= 20; i++) {
      assertInEntries(`This is post # ${i} --`);
    }
    assertNotInEntries('This is a YAML front matter post');
    assertNotInEntries('This is a JSON front matter post');
    assertNotInEntries('This is a TOML front matter post');

    // disable filter
    filter('Posts With Index');
    assertEntriesCount(23);
    for (let i = 1; i <= 20; i++) {
      assertInEntries(`This is post # ${i} --`);
    }
    assertInEntries('This is a YAML front matter post');
    assertInEntries('This is a JSON front matter post');
    assertInEntries('This is a TOML front matter post');
  });

  it('can apply boolean filter', () => {
    // enable filter
    filter('Drafts');

    assertEntriesCount(10);
    for (let i = 1; i <= 20; i++) {
      const draft = i % 2 === 0;
      if (draft) {
        assertInEntries(`This is post # ${i} --`);
      } else {
        assertNotInEntries(`This is post # ${i} --`);
      }
    }
    assertNotInEntries('This is a YAML front matter post');
    assertNotInEntries('This is a JSON front matter post');
    assertNotInEntries('This is a TOML front matter post');

    // disable filter
    filter('Drafts');
    assertEntriesCount(23);
    for (let i = 1; i <= 20; i++) {
      assertInEntries(`This is post # ${i} --`);
    }
    assertInEntries('This is a YAML front matter post');
    assertInEntries('This is a JSON front matter post');
    assertInEntries('This is a TOML front matter post');
  });

  it('can apply multiple filters', () => {
    // enable filter
    filter('Posts Without Index');

    assertEntriesCount(3);

    assertInEntries('This is a YAML front matter post');
    assertInEntries('This is a JSON front matter post');
    assertInEntries('This is a TOML front matter post');

    filter('Drafts');

    assertEntriesCount(0);

    cy.contains('div', 'No Entries');
  });
});


================================================
FILE: cypress/e2e/view_groups_spec.js
================================================
import { login } from '../utils/steps';

const group = term => {
  cy.contains('span', 'Group by').click();
  cy.contains(term).click();
  cy.contains('Contents').click();
};

const assertGroupsCount = count => {
  cy.get('[class*=GroupContainer]').should('have.length', count);
};

const assertEachGroupCount = (id, count) => {
  cy.get(`[id='${id}']`).within(() => {
    assertEntriesCount(count);
  });
};

const assertEntriesCount = count => {
  cy.get('[class*=ListCardLink]').should('have.length', count);
};

const assertInEntries = text => {
  cy.get('[class*=ListCardLink] h2').contains('h2', text);
};

describe('View Group', () => {
  before(() => {
    Cypress.config('defaultCommandTimeout', 4000);
    cy.task('setupBackend', { backend: 'test' });
  });

  after(() => {
    cy.task('teardownBackend', { backend: 'test' });
  });

  beforeEach(() => {
    login();
  });

  it('can apply string group', () => {
    // enable group
    group('Year');

    assertGroupsCount(2);
    const year = new Date().getFullYear();
    assertEachGroupCount(`Year${year}`, 20);
    assertEachGroupCount('Year2015', 3);

    //disable group
    group('Year');

    assertEntriesCount(23);
    for (let i = 1; i <= 20; i++) {
      assertInEntries(`This is post # ${i} --`);
    }
    assertInEntries('This is a YAML front matter post');
    assertInEntries('This is a JSON front matter post');
    assertInEntries('This is a TOML front matter post');

    //enable group
    group('Drafts');

    assertEntriesCount(23);
    assertGroupsCount(3);
    assertEachGroupCount('Draftstrue', 10);
    assertEachGroupCount('Draftsfalse', 10);
    assertEachGroupCount('missing_value', 3);
  });
});


================================================
FILE: cypress/fixtures/BitBucket Backend Editorial Workflow__can change status on and publish multiple entries.json
================================================
[
  {
    "method": "GET",
    "url": "/2.0/repositories/owner/repo",
    "headers": {
      "Server": "nginx",
      "Vary": "Authorization",
      "Content-Type": "application/json; charset=utf-8",
      "X-OAuth-Scopes": "pullrequest:write, repository:delete, repository:admin, account",
      "Access-Control-Expose-Headers": "Accept-Ranges, Content-Encoding, Content-Length, Content-Type, ETag, Last-Modified",
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
      "X-Served-By": "app-1107",
      "Access-Control-Allow-Origin": "*",
      "X-Static-Version": "e1ce49ad4aa3",
      "X-Content-Type-Options": "nosniff",
      "X-Accepted-OAuth-Scopes": "repository",
      "X-Credential-Type": "oauth2",
      "X-Render-Time": "0.0437190532684",
      "Connection": "Keep-Alive",
      "X-Request-Count": "82",
      "X-Frame-Options": "SAMEORIGIN",
      "X-Version": "e1ce49ad4aa3",
      "content-length": "3011"
    },
    "response": "{\"scm\": \"git\", \"website\": null, \"has_wiki\": false, \"name\": \"repo\", \"links\": {\"watchers\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/watchers\"}, \"branches\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/refs/branches\"}, \"tags\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/refs/tags\"}, \"commits\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commits\"}, \"clone\": [{\"href\": \"https://owner@bitbucket.org/owner/repo.git\", \"name\": \"https\"}, {\"href\": \"git@bitbucket.org:owner/repo.git\", \"name\": \"ssh\"}], \"self\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo\"}, \"source\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/src\"}, \"html\": {\"href\": \"https://bitbucket.org/owner/repo\"}, \"avatar\": {\"href\": \"https://bytebucket.org/ravatar/%7Bcc1da263-9f3b-41f1-95d8-4ad5402c0ffd%7D?ts=default\"}, \"hooks\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/hooks\"}, \"forks\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/forks\"}, \"downloads\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/downloads\"}, \"pullrequests\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/pullrequests\"}}, \"fork_policy\": \"allow_forks\", \"uuid\": \"{cc1da263-9f3b-41f1-95d8-4ad5402c0ffd}\", \"language\": \"\", \"created_on\": \"2020-04-12T09:43:06.141832+00:00\", \"mainbranch\": {\"type\": \"branch\", \"name\": \"master\"}, \"full_name\": \"owner/repo\", \"has_issues\": false, \"owner\": {\"display_name\": \"Erez Rokah\", \"uuid\": \"{0344b5bd-000e-47ad-80d1-b0a91b92bb53}\", \"links\": {\"self\": {\"href\": \"https://api.bitbucket.org/2.0/users/%7B0344b5bd-000e-47ad-80d1-b0a91b92bb53%7D\"}, \"html\": {\"href\": \"https://bitbucket.org/%7B0344b5bd-000e-47ad-80d1-b0a91b92bb53%7D/\"}, \"avatar\": {\"href\": \"https://secure.gravatar.com/avatar/2c95a9ee2f890f6c9ccdbf2438c88ca7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FER-2.png\"}}, \"nickname\": \"owner\", \"type\": \"user\", \"account_id\": \"557058:f3be1617-a338-488e-99a1-4ed46a10755a\"}, \"updated_on\": \"2020-04-12T09:46:45.423481+00:00\", \"size\": 4362904, \"type\": \"repository\", \"slug\": \"repo\", \"is_private\": false, \"description\": \"\"}",
    "status": 200
  },
  {
    "method": "GET",
    "url": "/2.0/user",
    "headers": {
      "Server": "nginx",
      "Vary": "Authorization",
      "Content-Type": "application/json; charset=utf-8",
      "X-OAuth-Scopes": "pullrequest:write, repository:delete, repository:admin, account",
      "Access-Control-Expose-Headers": "Accept-Ranges, Content-Encoding, Content-Length, Content-Type, ETag, Last-Modified",
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
      "X-Served-By": "app-1129",
      "Access-Control-Allow-Origin": "*",
      "X-Static-Version": "e1ce49ad4aa3",
      "X-Content-Type-Options": "nosniff",
      "X-Credential-Type": "oauth2",
      "X-Render-Time": "0.0374369621277",
      "X-Accepted-OAuth-Scopes": "account",
      "Connection": "Keep-Alive",
      "X-Request-Count": "837",
      "X-Version": "e1ce49ad4aa3",
      "X-Frame-Options": "SAMEORIGIN",
      "content-length": "1041"
    },
    "response": "{\"name\":\"owner\",\"display_name\":\"owner\",\"links\":{\"avatar\":{\"href\":\"https://avatars1.githubusercontent.com/u/7892489?v=4\"}},\"nickname\":\"owner\"}",
    "status": 200
  },
  {
    "method": "GET",
    "url": "/2.0/repositories/owner/repo/refs/branches/master",
    "headers": {
      "Server": "nginx",
      "Vary": "Authorization",
      "Content-Type": "application/json; charset=utf-8",
      "X-OAuth-Scopes": "pullrequest:write, repository:delete, repository:admin, account",
      "Access-Control-Expose-Headers": "Accept-Ranges, Content-Encoding, Content-Length, Content-Type, ETag, Last-Modified",
      "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
      "X-Served-By": "app-1129",
      "Access-Control-Allow-Origin": "*",
      "X-Static-Version": "e1ce49ad4aa3",
      "X-Content-Type-Options": "nosniff",
      "X-Accepted-OAuth-Scopes": "repository",
      "X-Credential-Type": "oauth2",
      "X-Render-Time": "0.0564169883728",
      "Connection": "Keep-Alive",
      "X-Request-Count": "1813",
      "X-Frame-Options": "SAMEORIGIN",
      "X-Version": "e1ce49ad4aa3",
      "content-length": "3625"
    },
    "response": "{\"name\": \"master\", \"links\": {\"commits\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commits/master\"}, \"self\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/refs/branches/master\"}, \"html\": {\"href\": \"https://bitbucket.org/owner/repo/branch/master\"}}, \"default_merge_strategy\": \"merge_commit\", \"merge_strategies\": [\"merge_commit\", \"squash\", \"fast_forward\"], \"type\": \"branch\", \"target\": {\"hash\": \"b782b50eefc7d6f6482ac989eb5c9142d5abfa37\", \"repository\": {\"links\": {\"self\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo\"}, \"html\": {\"href\": \"https://bitbucket.org/owner/repo\"}, \"avatar\": {\"href\": \"https://bytebucket.org/ravatar/%7Bcc1da263-9f3b-41f1-95d8-4ad5402c0ffd%7D?ts=default\"}}, \"type\": \"repository\", \"name\": \"repo\", \"full_name\": \"owner/repo\", \"uuid\": \"{cc1da263-9f3b-41f1-95d8-4ad5402c0ffd}\"}, \"links\": {\"self\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commit/b782b50eefc7d6f6482ac989eb5c9142d5abfa37\"}, \"comments\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commit/b782b50eefc7d6f6482ac989eb5c9142d5abfa37/comments\"}, \"patch\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/patch/b782b50eefc7d6f6482ac989eb5c9142d5abfa37\"}, \"html\": {\"href\": \"https://bitbucket.org/owner/repo/commits/b782b50eefc7d6f6482ac989eb5c9142d5abfa37\"}, \"diff\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/diff/b782b50eefc7d6f6482ac989eb5c9142d5abfa37\"}, \"approve\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commit/b782b50eefc7d6f6482ac989eb5c9142d5abfa37/approve\"}, \"statuses\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commit/b782b50eefc7d6f6482ac989eb5c9142d5abfa37/statuses\"}}, \"author\": {\"raw\": \"Erez Rokah <ownerkah@gmail.com>\", \"type\": \"author\", \"user\": {\"display_name\": \"Erez Rokah\", \"uuid\": \"{0344b5bd-000e-47ad-80d1-b0a91b92bb53}\", \"links\": {\"self\": {\"href\": \"https://api.bitbucket.org/2.0/users/%7B0344b5bd-000e-47ad-80d1-b0a91b92bb53%7D\"}, \"html\": {\"href\": \"https://bitbucket.org/%7B0344b5bd-000e-47ad-80d1-b0a91b92bb53%7D/\"}, \"avatar\": {\"href\": \"https://secure.gravatar.com/avatar/2c95a9ee2f890f6c9ccdbf2438c88ca7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FER-2.png\"}}, \"nickname\": \"owner\", \"type\": \"user\", \"account_id\": \"557058:f3be1617-a338-488e-99a1-4ed46a10755a\"}}, \"parents\": [{\"hash\": \"536a257d1e997553bb48535542a6a6e98612cd44\", \"type\": \"commit\", \"links\": {\"self\": {\"href\": \"https://api.bitbucket.org/2.0/repositories/owner/repo/commit/536a257d1e997553bb48535542a6a6e98612cd44\"}, \"html\": {\"href\": \"https://bitbucket.org/owner/repo/commits/536a257d1e997553bb48535542a6a6e98612cd44\"}}}], \"date\": \"2020-01-20T13:25:35+00:00\", \"message\": \".gitattributes edited online with Bitbucket\", \"type\": \"commit\"}}",
    "status": 200
  },
  {
    "method": "GET",
    "url": "/2.0/repositories/owner/repo/src/b782b50eefc7d6f6482ac989eb5c9142d5abfa37/static/media?max_depth=1&pagelen=100",
    "headers": {
      "
Download .txt
gitextract_3fklok8x/

├── .editorconfig
├── .eslintrc.js
├── .gitattributes
├── .github/
│   ├── .kodiak.toml
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── stale.yml
│   └── workflows/
│       ├── create-release.yml
│       ├── labeler.yml
│       ├── nodejs.yml
│       ├── publish.yml
│       └── sponsors.yml
├── .gitignore
├── .husky/
│   └── commit-msg
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .storybook/
│   └── main.js
├── .stylelintrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── __mocks__/
│   └── styleMock.js
├── babel.config.js
├── commitlint.config.js
├── cypress/
│   ├── Readme.md
│   ├── e2e/
│   │   ├── common/
│   │   │   ├── editorial_workflow.js
│   │   │   ├── editorial_workflow_migrations.js
│   │   │   ├── entries.js
│   │   │   ├── i18n.js
│   │   │   ├── i18n_editorial_workflow_spec.js
│   │   │   ├── media_library.js
│   │   │   ├── open_authoring.js
│   │   │   ├── simple_workflow.js
│   │   │   └── spec_utils.js
│   │   ├── editorial_workflow_spec_bitbucket_backend.js
│   │   ├── editorial_workflow_spec_git-gateway_github_backend.js
│   │   ├── editorial_workflow_spec_git-gateway_gitlab_backend.js
│   │   ├── editorial_workflow_spec_github_backend_graphql.js
│   │   ├── editorial_workflow_spec_github_backend_graphql_open_authoring.js
│   │   ├── editorial_workflow_spec_github_backend_rest.js
│   │   ├── editorial_workflow_spec_github_backend_rest_open_authoring.js
│   │   ├── editorial_workflow_spec_gitlab_backend.js
│   │   ├── editorial_workflow_spec_proxy_git_backend.js
│   │   ├── editorial_workflow_spec_test_backend.js
│   │   ├── field_validations_spec.js
│   │   ├── i18n_editorial_workflow_spec_test_backend.js
│   │   ├── i18n_simple_workflow_spec_proxy_fs_backend.js
│   │   ├── markdown_widget_backspace_spec.js
│   │   ├── markdown_widget_code_block_spec.js
│   │   ├── markdown_widget_enter_spec.js
│   │   ├── markdown_widget_hotkeys_spec.js
│   │   ├── markdown_widget_link_spec.js
│   │   ├── markdown_widget_list_spec.js
│   │   ├── markdown_widget_marks_spec.js
│   │   ├── markdown_widget_quote_spec.js
│   │   ├── media_library_spec_bitbucket_backend.js
│   │   ├── media_library_spec_bitbucket_backend_large_media.js
│   │   ├── media_library_spec_git-gateway_github_backend_large_media.js
│   │   ├── media_library_spec_git-gateway_gitlab_backend_large_media.js
│   │   ├── media_library_spec_github_backend_graphql.js
│   │   ├── media_library_spec_github_backend_rest.js
│   │   ├── media_library_spec_gitlab_backend.js
│   │   ├── media_library_spec_proxy_git_backend.js
│   │   ├── media_library_spec_test_backend.js
│   │   ├── search_suggestion_spec.js
│   │   ├── simple_workflow_spec_bitbucket_backend.js
│   │   ├── simple_workflow_spec_git-gateway_github_backend.js
│   │   ├── simple_workflow_spec_git-gateway_gitlab_backend.js
│   │   ├── simple_workflow_spec_github_backend_graphql.js
│   │   ├── simple_workflow_spec_github_backend_rest.js
│   │   ├── simple_workflow_spec_gitlab_backend.js
│   │   ├── simple_workflow_spec_proxy_fs_backend.js
│   │   ├── simple_workflow_spec_proxy_git_backend.js
│   │   ├── simple_workflow_spec_test_backend.js
│   │   ├── view_filters_spec.js
│   │   └── view_groups_spec.js
│   ├── fixtures/
│   │   ├── BitBucket Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── BitBucket Backend Editorial Workflow__can change workflow status.json
│   │   ├── BitBucket Backend Editorial Workflow__can create an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can delete an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can update an entry.json
│   │   ├── BitBucket Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── BitBucket Backend Editorial Workflow__successfully loads.json
│   │   ├── BitBucket Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── BitBucket Backend Media Library - Large Media__can save entry with image.json
│   │   ├── BitBucket Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── BitBucket Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__can publish entry with image.json
│   │   ├── BitBucket Backend Media Library - REST API__can save entry with image.json
│   │   ├── BitBucket Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── BitBucket Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── BitBucket Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── BitBucket Backend Simple Workflow__can create an entry.json
│   │   ├── BitBucket Backend Simple Workflow__successfully loads.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can change workflow status.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can create an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can delete an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can update an entry.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── Git Gateway (GitHub) Backend Editorial Workflow__successfully loads.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can save entry with image.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── Git Gateway (GitHub) Backend Simple Workflow__can create an entry.json
│   │   ├── Git Gateway (GitHub) Backend Simple Workflow__successfully loads.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can change workflow status.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can create an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can delete an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can update an entry.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── Git Gateway (GitLab) Backend Editorial Workflow__successfully loads.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can delete image from global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can publish entry with image.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can save entry with image.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from entry media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should not show draft entry image in global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in global media library.json
│   │   ├── Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in grid view.json
│   │   ├── Git Gateway (GitLab) Backend Simple Workflow__can create an entry.json
│   │   ├── Git Gateway (GitLab) Backend Simple Workflow__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can change entry status from fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can delete review entry from fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can return entry to draft and delete it.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update a draft entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully forks repository and loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can change status on and publish multiple entries.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can change workflow status.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can delete an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__can update workflow status from within the editor.json
│   │   ├── GitHub Backend Editorial Workflow - GraphQL API__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can change entry status from fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can create an entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can delete review entry from fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can return entry to draft and delete it.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can update a draft entry on fork.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__successfully forks repository and loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API - Open Authoring__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can change status on and publish multiple entries.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can change workflow status.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can create an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can delete an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can publish an editorial workflow entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can update an entry.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__can update workflow status from within the editor.json
│   │   ├── GitHub Backend Editorial Workflow - REST API__successfully loads.json
│   │   ├── GitHub Backend Editorial Workflow Migration - REST API__migrate from 2.10.24 to latest.json
│   │   ├── GitHub Backend Editorial Workflow Migration - REST API__migrate from 2.9.7 to latest.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can delete image from global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can publish entry with image.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can save entry with image.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can upload image from entry media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__can upload image from global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should not show draft entry image in global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should show published entry image in global media library.json
│   │   ├── GitHub Backend Media Library - GraphQL API__should show published entry image in grid view.json
│   │   ├── GitHub Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── GitHub Backend Media Library - REST API__can publish entry with image.json
│   │   ├── GitHub Backend Media Library - REST API__can save entry with image.json
│   │   ├── GitHub Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── GitHub Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── GitHub Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── GitHub Backend Simple Workflow - GraphQL API__can create an entry.json
│   │   ├── GitHub Backend Simple Workflow - GraphQL API__successfully loads.json
│   │   ├── GitHub Backend Simple Workflow - REST API__can create an entry.json
│   │   ├── GitHub Backend Simple Workflow - REST API__successfully loads.json
│   │   ├── GitHub Backend Simple Workflow__successfully loads.json
│   │   ├── GitLab Backend Editorial Workflow__can change status on and publish multiple entries.json
│   │   ├── GitLab Backend Editorial Workflow__can change workflow status.json
│   │   ├── GitLab Backend Editorial Workflow__can create an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can delete an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can publish an editorial workflow entry.json
│   │   ├── GitLab Backend Editorial Workflow__can update an entry.json
│   │   ├── GitLab Backend Editorial Workflow__can update workflow status from within the editor.json
│   │   ├── GitLab Backend Editorial Workflow__successfully loads.json
│   │   ├── GitLab Backend Media Library - REST API__can delete image from global media library.json
│   │   ├── GitLab Backend Media Library - REST API__can publish entry with image.json
│   │   ├── GitLab Backend Media Library - REST API__can save entry with image.json
│   │   ├── GitLab Backend Media Library - REST API__can upload image from entry media library.json
│   │   ├── GitLab Backend Media Library - REST API__can upload image from global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should not show draft entry image in global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should show published entry image in global media library.json
│   │   ├── GitLab Backend Media Library - REST API__should show published entry image in grid view.json
│   │   ├── GitLab Backend Simple Workflow__can create an entry.json
│   │   ├── GitLab Backend Simple Workflow__successfully loads.json
│   │   └── example.json
│   ├── plugins/
│   │   ├── bitbucket.js
│   │   ├── common.js
│   │   ├── gitGateway.js
│   │   ├── github.js
│   │   ├── gitlab.js
│   │   ├── index.js
│   │   ├── proxy.js
│   │   └── testBackend.js
│   ├── run.mjs
│   ├── support/
│   │   ├── commands.js
│   │   └── e2e.js
│   └── utils/
│       ├── README.md
│       ├── config.js
│       ├── constants.js
│       ├── dismiss-local-backup.js
│       ├── mock-server.js
│       ├── regexp.js
│       └── steps.js
├── cypress.config.ts
├── dev-test/
│   ├── backends/
│   │   ├── azure/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── bitbucket/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── git-gateway/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── gitea/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── github/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── gitlab/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   ├── proxy/
│   │   │   ├── config.yml
│   │   │   └── index.html
│   │   └── test/
│   │       ├── config.yml
│   │       └── index.html
│   ├── config.yml
│   ├── example.css
│   └── index.html
├── functions/
│   └── publish.js
├── jest.config.js
├── lerna.json
├── netlify.toml
├── nx.json
├── package.json
├── packages/
│   ├── decap-cms/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── shims/
│   │   │   ├── cms.css
│   │   │   └── deprecate-old-dist.js
│   │   ├── src/
│   │   │   ├── extensions.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-app/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── extensions.js
│   │   │   ├── index.js
│   │   │   └── locales.js
│   │   └── webpack.config.js
│   ├── decap-cms-backend-aws-cognito-github-proxy/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.tsx
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-azure/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-bitbucket/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   └── api.spec.js
│   │   │   ├── git-lfs-client.ts
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   └── types/
│   │   │       ├── semaphore.d.ts
│   │   │       └── what-the-diff.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-git-gateway/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── GitHubAPI.ts
│   │   │   ├── GitLabAPI.ts
│   │   │   ├── __tests__/
│   │   │   │   └── GitHubAPI.spec.js
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   ├── netlify-lfs-client.ts
│   │   │   └── types/
│   │   │       └── ini.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-gitea/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   └── implementation.spec.js
│   │   │   ├── implementation.tsx
│   │   │   ├── index.ts
│   │   │   └── types.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-github/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   └── createFragmentTypes.js
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── GraphQLAPI.ts
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   ├── GraphQLAPI.spec.js
│   │   │   │   └── implementation.spec.js
│   │   │   ├── fragmentTypes.js
│   │   │   ├── fragments.ts
│   │   │   ├── implementation.tsx
│   │   │   ├── index.ts
│   │   │   ├── mutations.ts
│   │   │   ├── queries.ts
│   │   │   └── types/
│   │   │       └── semaphore.d.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-gitlab/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── API.spec.js
│   │   │   │   └── gitlab.spec.js
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   └── queries.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-proxy/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-backend-test/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   └── implementation.spec.js
│   │   │   ├── implementation.ts
│   │   │   └── index.ts
│   │   └── webpack.config.js
│   ├── decap-cms-core/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── backend.spec.js
│   │   │   ├── actions/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── config.spec.js
│   │   │   │   │   ├── editorialWorkflow.spec.js
│   │   │   │   │   ├── entries.spec.js
│   │   │   │   │   ├── media.spec.ts
│   │   │   │   │   ├── mediaLibrary.spec.js
│   │   │   │   │   └── search.spec.js
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collections.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── deploys.ts
│   │   │   │   ├── editorialWorkflow.ts
│   │   │   │   ├── entries.ts
│   │   │   │   ├── media.ts
│   │   │   │   ├── mediaLibrary.ts
│   │   │   │   ├── notifications.ts
│   │   │   │   ├── search.ts
│   │   │   │   ├── status.ts
│   │   │   │   └── waitUntil.ts
│   │   │   ├── backend.ts
│   │   │   ├── bootstrap.js
│   │   │   ├── components/
│   │   │   │   ├── App/
│   │   │   │   │   ├── App.js
│   │   │   │   │   ├── Header.js
│   │   │   │   │   └── NotFoundPage.js
│   │   │   │   ├── Collection/
│   │   │   │   │   ├── Collection.js
│   │   │   │   │   ├── CollectionControls.js
│   │   │   │   │   ├── CollectionSearch.js
│   │   │   │   │   ├── CollectionTop.js
│   │   │   │   │   ├── ControlButton.js
│   │   │   │   │   ├── Entries/
│   │   │   │   │   │   ├── Entries.js
│   │   │   │   │   │   ├── EntriesCollection.js
│   │   │   │   │   │   ├── EntriesSearch.js
│   │   │   │   │   │   ├── EntryCard.js
│   │   │   │   │   │   ├── EntryListing.js
│   │   │   │   │   │   └── __tests__/
│   │   │   │   │   │       ├── EntriesCollection.spec.js
│   │   │   │   │   │       └── __snapshots__/
│   │   │   │   │   │           └── EntriesCollection.spec.js.snap
│   │   │   │   │   ├── FilterControl.js
│   │   │   │   │   ├── GroupControl.js
│   │   │   │   │   ├── NestedCollection.js
│   │   │   │   │   ├── Sidebar.js
│   │   │   │   │   ├── SortControl.js
│   │   │   │   │   ├── ViewStyleControl.js
│   │   │   │   │   └── __tests__/
│   │   │   │   │       ├── Collection.spec.js
│   │   │   │   │       ├── NestedCollection.spec.js
│   │   │   │   │       ├── Sidebar.spec.js
│   │   │   │   │       └── __snapshots__/
│   │   │   │   │           ├── Collection.spec.js.snap
│   │   │   │   │           ├── NestedCollection.spec.js.snap
│   │   │   │   │           └── Sidebar.spec.js.snap
│   │   │   │   ├── Editor/
│   │   │   │   │   ├── Editor.js
│   │   │   │   │   ├── EditorControlPane/
│   │   │   │   │   │   ├── EditorControl.js
│   │   │   │   │   │   ├── EditorControlPane.js
│   │   │   │   │   │   └── Widget.js
│   │   │   │   │   ├── EditorInterface.js
│   │   │   │   │   ├── EditorPreviewPane/
│   │   │   │   │   │   ├── EditorPreview.js
│   │   │   │   │   │   ├── EditorPreviewContent.js
│   │   │   │   │   │   ├── EditorPreviewPane.js
│   │   │   │   │   │   └── PreviewHOC.js
│   │   │   │   │   ├── EditorToolbar.js
│   │   │   │   │   ├── __tests__/
│   │   │   │   │   │   ├── Editor.spec.js
│   │   │   │   │   │   ├── EditorToolbar.spec.js
│   │   │   │   │   │   └── __snapshots__/
│   │   │   │   │   │       ├── Editor.spec.js.snap
│   │   │   │   │   │       └── EditorToolbar.spec.js.snap
│   │   │   │   │   └── withWorkflow.js
│   │   │   │   ├── EditorWidgets/
│   │   │   │   │   ├── Unknown/
│   │   │   │   │   │   ├── UnknownControl.js
│   │   │   │   │   │   └── UnknownPreview.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── MediaLibrary/
│   │   │   │   │   ├── EmptyMessage.js
│   │   │   │   │   ├── MediaLibrary.js
│   │   │   │   │   ├── MediaLibraryButtons.js
│   │   │   │   │   ├── MediaLibraryCard.js
│   │   │   │   │   ├── MediaLibraryCardGrid.js
│   │   │   │   │   ├── MediaLibraryHeader.js
│   │   │   │   │   ├── MediaLibraryModal.js
│   │   │   │   │   ├── MediaLibrarySearch.js
│   │   │   │   │   ├── MediaLibraryTop.js
│   │   │   │   │   └── __tests__/
│   │   │   │   │       ├── MediaLibraryButtons.spec.js
│   │   │   │   │       ├── MediaLibraryCard.spec.js
│   │   │   │   │       └── __snapshots__/
│   │   │   │   │           └── MediaLibraryCard.spec.js.snap
│   │   │   │   ├── UI/
│   │   │   │   │   ├── DragDrop.js
│   │   │   │   │   ├── ErrorBoundary.js
│   │   │   │   │   ├── FileUploadButton.js
│   │   │   │   │   ├── Modal.js
│   │   │   │   │   ├── Notifications.tsx
│   │   │   │   │   ├── SettingsDropdown.js
│   │   │   │   │   ├── __tests__/
│   │   │   │   │   │   └── ErrorBoundary.spec.js
│   │   │   │   │   └── index.js
│   │   │   │   └── Workflow/
│   │   │   │       ├── Workflow.js
│   │   │   │       ├── WorkflowCard.js
│   │   │   │       └── WorkflowList.js
│   │   │   ├── constants/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── configSchema.spec.js
│   │   │   │   ├── collectionTypes.ts
│   │   │   │   ├── collectionViews.js
│   │   │   │   ├── commitProps.ts
│   │   │   │   ├── configSchema.js
│   │   │   │   ├── fieldInference.tsx
│   │   │   │   ├── publishModes.ts
│   │   │   │   └── validationErrorTypes.js
│   │   │   ├── formats/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── formats.spec.js
│   │   │   │   │   ├── frontmatter.spec.js
│   │   │   │   │   ├── toml.spec.js
│   │   │   │   │   └── yaml.spec.js
│   │   │   │   ├── formats.ts
│   │   │   │   ├── frontmatter.ts
│   │   │   │   ├── helpers.ts
│   │   │   │   ├── json.ts
│   │   │   │   ├── toml.ts
│   │   │   │   └── yaml.ts
│   │   │   ├── index.js
│   │   │   ├── integrations/
│   │   │   │   ├── index.js
│   │   │   │   └── providers/
│   │   │   │       ├── algolia/
│   │   │   │       │   └── implementation.js
│   │   │   │       └── assetStore/
│   │   │   │           └── implementation.js
│   │   │   ├── lib/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── formatters.spec.js
│   │   │   │   │   ├── i18n.spec.js
│   │   │   │   │   ├── phrases.spec.js
│   │   │   │   │   ├── registry.spec.js
│   │   │   │   │   ├── serializeEntryValues.spec.js
│   │   │   │   │   └── urlHelper.spec.js
│   │   │   │   ├── consoleError.js
│   │   │   │   ├── formatters.ts
│   │   │   │   ├── i18n.ts
│   │   │   │   ├── phrases.js
│   │   │   │   ├── polyfill.js
│   │   │   │   ├── registry.js
│   │   │   │   ├── serializeEntryValues.js
│   │   │   │   ├── stega.ts
│   │   │   │   ├── textHelper.js
│   │   │   │   └── urlHelper.ts
│   │   │   ├── mediaLibrary.ts
│   │   │   ├── reducers/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── auth.spec.ts
│   │   │   │   │   ├── collections.spec.js
│   │   │   │   │   ├── config.spec.js
│   │   │   │   │   ├── deploys.spec.ts
│   │   │   │   │   ├── entries.spec.js
│   │   │   │   │   ├── entryDraft.spec.js
│   │   │   │   │   ├── globalUI.js
│   │   │   │   │   ├── integrations.spec.ts
│   │   │   │   │   ├── mediaLibrary.spec.js
│   │   │   │   │   └── medias.spec.ts
│   │   │   │   ├── auth.ts
│   │   │   │   ├── collections.ts
│   │   │   │   ├── combinedReducer.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── cursors.js
│   │   │   │   ├── deploys.ts
│   │   │   │   ├── editorialWorkflow.ts
│   │   │   │   ├── entries.ts
│   │   │   │   ├── entryDraft.js
│   │   │   │   ├── globalUI.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── integrations.ts
│   │   │   │   ├── mediaLibrary.ts
│   │   │   │   ├── medias.ts
│   │   │   │   ├── notifications.ts
│   │   │   │   ├── search.ts
│   │   │   │   └── status.ts
│   │   │   ├── redux/
│   │   │   │   ├── index.ts
│   │   │   │   └── middleware/
│   │   │   │       └── waitUntilAction.ts
│   │   │   ├── routing/
│   │   │   │   ├── __tests__/
│   │   │   │   │   └── history.spec.ts
│   │   │   │   └── history.ts
│   │   │   ├── types/
│   │   │   │   ├── diacritics.d.ts
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── immutable.ts
│   │   │   │   ├── redux.ts
│   │   │   │   └── tomlify-j0.4.d.ts
│   │   │   └── valueObjects/
│   │   │       ├── AssetProxy.ts
│   │   │       ├── EditorComponent.js
│   │   │       └── Entry.ts
│   │   └── webpack.config.js
│   ├── decap-cms-default-exports/
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-editor-component-image/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-auth/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── implicit-oauth.js
│   │   │   ├── index.js
│   │   │   ├── netlify-auth.js
│   │   │   ├── pkce-oauth.js
│   │   │   └── utils.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-util/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── API.ts
│   │   │   ├── APIError.ts
│   │   │   ├── APIUtils.ts
│   │   │   ├── AccessTokenError.ts
│   │   │   ├── Cursor.ts
│   │   │   ├── EditorialWorkflowError.ts
│   │   │   ├── __tests__/
│   │   │   │   ├── api.spec.js
│   │   │   │   ├── apiUtils.spec.js
│   │   │   │   ├── asyncLock.spec.js
│   │   │   │   ├── backendUtil.spec.js
│   │   │   │   ├── implementation.spec.js
│   │   │   │   ├── path.spec.js
│   │   │   │   └── unsentRequest.spec.js
│   │   │   ├── asyncLock.ts
│   │   │   ├── backendUtil.ts
│   │   │   ├── getBlobSHA.ts
│   │   │   ├── git-lfs.ts
│   │   │   ├── implementation.ts
│   │   │   ├── index.ts
│   │   │   ├── loadScript.js
│   │   │   ├── localForage.ts
│   │   │   ├── path.ts
│   │   │   ├── promise.ts
│   │   │   ├── types/
│   │   │   │   └── semaphore.d.ts
│   │   │   ├── types.ts
│   │   │   └── unsentRequest.js
│   │   └── webpack.config.js
│   ├── decap-cms-lib-widgets/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── stringTemplate.spec.js
│   │   │   ├── index.ts
│   │   │   ├── stringTemplate.ts
│   │   │   └── validations.ts
│   │   └── webpack.config.js
│   ├── decap-cms-locales/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── bg/
│   │   │   │   └── index.js
│   │   │   ├── ca/
│   │   │   │   └── index.js
│   │   │   ├── cs/
│   │   │   │   └── index.js
│   │   │   ├── da/
│   │   │   │   └── index.js
│   │   │   ├── de/
│   │   │   │   └── index.js
│   │   │   ├── en/
│   │   │   │   └── index.js
│   │   │   ├── es/
│   │   │   │   └── index.js
│   │   │   ├── fa/
│   │   │   │   └── index.js
│   │   │   ├── fr/
│   │   │   │   └── index.js
│   │   │   ├── gr/
│   │   │   │   └── index.js
│   │   │   ├── he/
│   │   │   │   └── index.js
│   │   │   ├── hr/
│   │   │   │   └── index.js
│   │   │   ├── hu/
│   │   │   │   └── index.js
│   │   │   ├── index.js
│   │   │   ├── it/
│   │   │   │   └── index.js
│   │   │   ├── ja/
│   │   │   │   └── index.js
│   │   │   ├── ko/
│   │   │   │   └── index.js
│   │   │   ├── lt/
│   │   │   │   └── index.js
│   │   │   ├── mk/
│   │   │   │   └── index.js
│   │   │   ├── nb_no/
│   │   │   │   └── index.js
│   │   │   ├── nl/
│   │   │   │   └── index.js
│   │   │   ├── nn_no/
│   │   │   │   └── index.js
│   │   │   ├── pl/
│   │   │   │   └── index.js
│   │   │   ├── pt/
│   │   │   │   └── index.js
│   │   │   ├── ro/
│   │   │   │   └── index.js
│   │   │   ├── ru/
│   │   │   │   └── index.js
│   │   │   ├── sl/
│   │   │   │   └── index.js
│   │   │   ├── sv/
│   │   │   │   └── index.js
│   │   │   ├── th/
│   │   │   │   └── index.js
│   │   │   ├── tr/
│   │   │   │   └── index.js
│   │   │   ├── ua/
│   │   │   │   └── index.js
│   │   │   ├── uk/
│   │   │   │   └── index.js
│   │   │   ├── vi/
│   │   │   │   └── index.js
│   │   │   ├── zh_Hans/
│   │   │   │   └── index.js
│   │   │   └── zh_Hant/
│   │   │       └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-media-library-cloudinary/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-media-library-uploadcare/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── __tests__/
│   │   │   │   └── index.spec.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-ui-auth/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── index.d.ts
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── NetlifyAuthenticationPage.js
│   │   │   ├── PKCEAuthenticationPage.js
│   │   │   ├── __tests__/
│   │   │   │   ├── NetlifyAuthenticationPage.spec.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── NetlifyAuthenticationPage.spec.js.snap
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-ui-default/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── AuthenticationPage.js
│   │   │   ├── Dropdown.js
│   │   │   ├── FieldLabel.js
│   │   │   ├── GoBackButton.js
│   │   │   ├── Icon/
│   │   │   │   ├── icons.js
│   │   │   │   └── images/
│   │   │   │       └── _index.js
│   │   │   ├── Icon.js
│   │   │   ├── IconButton.js
│   │   │   ├── ListItemTopBar.js
│   │   │   ├── Loader.js
│   │   │   ├── ObjectWidgetTopBar.js
│   │   │   ├── Toggle.js
│   │   │   ├── WidgetPreviewContainer.js
│   │   │   ├── index.js
│   │   │   └── styles.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-boolean/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── BooleanControl.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-code/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── data/
│   │   │   ├── languages-raw.yml
│   │   │   └── languages.json
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   └── process-languages.js
│   │   ├── src/
│   │   │   ├── CodeControl.js
│   │   │   ├── CodePreview.js
│   │   │   ├── SettingsButton.js
│   │   │   ├── SettingsPane.js
│   │   │   ├── index.js
│   │   │   ├── languageSelectStyles.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-colorstring/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ColorControl.js
│   │   │   ├── ColorPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-datetime/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── DateTimeControl.js
│   │   │   ├── DateTimePreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── DateTimeControl.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-file/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── FilePreview.js
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── withFileControl.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-image/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ImagePreview.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-list/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ListControl.js
│   │   │   ├── __tests__/
│   │   │   │   ├── ListControl.spec.js
│   │   │   │   └── __snapshots__/
│   │   │   │       └── ListControl.spec.js.snap
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── typedListHelpers.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-map/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── MapPreview.js
│   │   │   ├── index.js
│   │   │   ├── schema.js
│   │   │   └── withMapControl.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-markdown/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── MarkdownControl/
│   │   │   │   ├── RawEditor.js
│   │   │   │   ├── Toolbar.js
│   │   │   │   ├── ToolbarButton.js
│   │   │   │   ├── VisualEditor.js
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── VisualEditor.spec.js
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── parser.spec.js.snap
│   │   │   │   │   └── parser.spec.js
│   │   │   │   ├── components/
│   │   │   │   │   ├── Shortcode.js
│   │   │   │   │   └── VoidBlock.js
│   │   │   │   ├── index.js
│   │   │   │   ├── plugins/
│   │   │   │   │   ├── BreakToDefaultBlock.js
│   │   │   │   │   ├── CloseBlock.js
│   │   │   │   │   ├── CommandsAndQueries.js
│   │   │   │   │   ├── ForceInsert.js
│   │   │   │   │   ├── Hotkey.js
│   │   │   │   │   ├── LineBreak.js
│   │   │   │   │   ├── Link.js
│   │   │   │   │   ├── blocks/
│   │   │   │   │   │   ├── defaultEmptyBlock.js
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownBackspace.js
│   │   │   │   │   │   │   ├── keyDownEnter.js
│   │   │   │   │   │   │   └── toggleBlock.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   ├── areCurrentAndPreviousBlocksOfType.js
│   │   │   │   │   │   │   ├── getListTypeAtCursor.js
│   │   │   │   │   │   │   ├── isCursorAtEndOfParagraph.js
│   │   │   │   │   │   │   ├── isCursorAtStartOfBlockType.js
│   │   │   │   │   │   │   ├── isCursorAtStartOfNonEmptyHeading.js
│   │   │   │   │   │   │   ├── isCursorCollapsedAfterSoftBreak.js
│   │   │   │   │   │   │   ├── isCursorInBlockType.js
│   │   │   │   │   │   │   └── isCursorInNonDefaultBlock.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── splitIntoParagraph.js
│   │   │   │   │   │   │   ├── unwrapIfCursorAtStart.js
│   │   │   │   │   │   │   └── wrapListItemsInBlock.js
│   │   │   │   │   │   └── withBlocks.js
│   │   │   │   │   ├── html/
│   │   │   │   │   │   └── withHtml.js
│   │   │   │   │   ├── inlines/
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownShiftEnter.js
│   │   │   │   │   │   │   ├── toggleLink.js
│   │   │   │   │   │   │   └── toggleMark.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   └── isMarkActive.js
│   │   │   │   │   │   ├── selectors/
│   │   │   │   │   │   │   └── getActiveLink.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── unwrapLink.js
│   │   │   │   │   │   │   └── wrapLink.js
│   │   │   │   │   │   └── withInlines.js
│   │   │   │   │   ├── lists/
│   │   │   │   │   │   ├── events/
│   │   │   │   │   │   │   ├── keyDown.js
│   │   │   │   │   │   │   ├── keyDownBackspace.js
│   │   │   │   │   │   │   ├── keyDownEnter.js
│   │   │   │   │   │   │   ├── keyDownShiftTab.js
│   │   │   │   │   │   │   ├── keyDownTab.js
│   │   │   │   │   │   │   └── toggleListType.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   ├── isCursorAtListItemStart.js
│   │   │   │   │   │   │   ├── isCursorAtNoninitialParagraphStart.js
│   │   │   │   │   │   │   ├── isCursorInItemContainingNestedList.js
│   │   │   │   │   │   │   ├── isCursorInListItem.js
│   │   │   │   │   │   │   └── isSelectionWithinNoninitialListItem.js
│   │   │   │   │   │   ├── selectors/
│   │   │   │   │   │   │   ├── getListContainedInListItem.js
│   │   │   │   │   │   │   ├── getLowestAncestorList.js
│   │   │   │   │   │   │   └── getLowestAncestorQuote.js
│   │   │   │   │   │   ├── transforms/
│   │   │   │   │   │   │   ├── changeListType.js
│   │   │   │   │   │   │   ├── convertParagraphToListItem.js
│   │   │   │   │   │   │   ├── liftFirstMatchedParent.js
│   │   │   │   │   │   │   ├── liftListItem.js
│   │   │   │   │   │   │   ├── mergeWithPreviousListItem.js
│   │   │   │   │   │   │   ├── moveListToListItem.js
│   │   │   │   │   │   │   ├── splitListItem.js
│   │   │   │   │   │   │   ├── splitToNestedList.js
│   │   │   │   │   │   │   ├── unwrapFirstMatchedParent.js
│   │   │   │   │   │   │   ├── unwrapSelectionFromList.js
│   │   │   │   │   │   │   ├── wrapFirstMatchedParent.js
│   │   │   │   │   │   │   └── wrapSelectionInList.js
│   │   │   │   │   │   └── withLists.js
│   │   │   │   │   ├── matchers/
│   │   │   │   │   │   ├── lowestMatchedAncestor.js
│   │   │   │   │   │   ├── matchLink.js
│   │   │   │   │   │   └── matchedAncestors.js
│   │   │   │   │   ├── shortcodes/
│   │   │   │   │   │   ├── insertShortcode.js
│   │   │   │   │   │   ├── locations/
│   │   │   │   │   │   │   └── isCursorInEmptyParagraph.js
│   │   │   │   │   │   └── withShortcodes.js
│   │   │   │   │   └── util.js
│   │   │   │   └── renderers.js
│   │   │   ├── MarkdownPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── renderer.spec.js
│   │   │   ├── index.js
│   │   │   ├── regexHelper.js
│   │   │   ├── schema.js
│   │   │   ├── serializers/
│   │   │   │   ├── __tests__/
│   │   │   │   │   ├── __fixtures__/
│   │   │   │   │   │   ├── commonmarkExpected.json
│   │   │   │   │   │   └── duplicate_marks_github_issue_3280.md
│   │   │   │   │   ├── __snapshots__/
│   │   │   │   │   │   └── remarkShortcodes.spec.js.snap
│   │   │   │   │   ├── commonmark.spec.js
│   │   │   │   │   ├── index.spec.js
│   │   │   │   │   ├── remarkAllowHtmlEntities.spec.js
│   │   │   │   │   ├── remarkAssertParents.spec.js
│   │   │   │   │   ├── remarkEscapeMarkdownEntities.spec.js
│   │   │   │   │   ├── remarkPaddedLinks.spec.js
│   │   │   │   │   ├── remarkPlugins.spec.js
│   │   │   │   │   ├── remarkShortcodes.spec.js
│   │   │   │   │   ├── remarkSlate.spec.js
│   │   │   │   │   ├── remarkStripTrailingBreaks.spec.js
│   │   │   │   │   └── slate.spec.js
│   │   │   │   ├── index.js
│   │   │   │   ├── rehypePaperEmoji.js
│   │   │   │   ├── remarkAllowHtmlEntities.js
│   │   │   │   ├── remarkAssertParents.js
│   │   │   │   ├── remarkEscapeMarkdownEntities.js
│   │   │   │   ├── remarkImagesToText.js
│   │   │   │   ├── remarkPaddedLinks.js
│   │   │   │   ├── remarkRehypeShortcodes.js
│   │   │   │   ├── remarkShortcodes.js
│   │   │   │   ├── remarkSlate.js
│   │   │   │   ├── remarkSquashReferences.js
│   │   │   │   ├── remarkStripTrailingBreaks.js
│   │   │   │   ├── remarkWrapHtml.js
│   │   │   │   └── slateRemark.js
│   │   │   ├── styles.js
│   │   │   └── types.js
│   │   ├── test-helpers/
│   │   │   └── h.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-number/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── NumberControl.js
│   │   │   ├── NumberPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── number.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-object/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ObjectControl.js
│   │   │   ├── ObjectPreview.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-relation/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── RelationCache.js
│   │   │   ├── RelationControl.js
│   │   │   ├── RelationPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── relation.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-select/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── SelectControl.js
│   │   │   ├── SelectPreview.js
│   │   │   ├── __tests__/
│   │   │   │   └── select.spec.js
│   │   │   ├── index.js
│   │   │   └── schema.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-string/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── StringControl.js
│   │   │   ├── StringPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   ├── decap-cms-widget-text/
│   │   ├── CHANGELOG.md
│   │   ├── README.md
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── TextControl.js
│   │   │   ├── TextPreview.js
│   │   │   └── index.js
│   │   └── webpack.config.js
│   └── decap-server/
│       ├── CHANGELOG.md
│       ├── README.md
│       ├── jest.config.js
│       ├── package.json
│       ├── src/
│       │   ├── global.d.ts
│       │   ├── index.ts
│       │   ├── logger.ts
│       │   ├── middlewares/
│       │   │   ├── common/
│       │   │   │   └── index.ts
│       │   │   ├── joi/
│       │   │   │   ├── customValidators.ts
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── localFs/
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── localGit/
│       │   │   │   ├── index.spec.ts
│       │   │   │   └── index.ts
│       │   │   ├── types.ts
│       │   │   └── utils/
│       │   │       ├── entries.ts
│       │   │       └── fs.ts
│       │   ├── middlewares.ts
│       │   └── what-the-diff.d.ts
│       ├── tsconfig.json
│       └── webpack.config.js
├── renovate.json
├── scripts/
│   ├── cache.js
│   ├── externals.js
│   ├── pack-and-install.sh
│   ├── revert_publish.sh
│   └── webpack.js
├── setupTestFramework.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (23 symbols across 4 files)

FILE: babel.config.js
  function presets (line 32) | function presets() {
  function plugins (line 46) | function plugins() {

FILE: cypress/e2e/common/media_library.js
  function uploadMediaFile (line 16) | function uploadMediaFile() {
  function assertImagesInLibrary (line 26) | function assertImagesInLibrary() {
  function assertNoImagesInLibrary (line 30) | function assertNoImagesInLibrary() {
  function deleteImage (line 37) | function deleteImage() {
  function chooseSelectedMediaFile (line 43) | function chooseSelectedMediaFile() {
  function chooseAnImage (line 48) | function chooseAnImage() {
  function waitForEntryToLoad (line 52) | function waitForEntryToLoad() {
  function matchImageSnapshot (line 58) | function matchImageSnapshot() {
  function newPostAndUploadImage (line 62) | function newPostAndUploadImage() {
  function newPostWithImage (line 68) | function newPostWithImage(entry) {
  function publishPostWithImage (line 75) | function publishPostWithImage(entry) {
  function closeMediaLibrary (line 85) | function closeMediaLibrary() {
  function switchToGridView (line 89) | function switchToGridView() {
  function assertGridEntryImage (line 97) | function assertGridEntryImage(entry) {

FILE: cypress/e2e/common/spec_utils.js
  function before (line 1) | function before(taskResult, options, backend) {
  function after (line 12) | function after(taskResult, backend) {
  function beforeEach (line 22) | function beforeEach(taskResult, backend) {
  function afterEach (line 60) | function afterEach(taskResult, backend) {
  function seedRepo (line 86) | function seedRepo(taskResult, backend) {

FILE: cypress/e2e/markdown_widget_code_block_spec.js
  function codeBlockRaw (line 45) | function codeBlockRaw(content) {
  function codeBlock (line 61) | function codeBlock(content) {
Copy disabled (too large) Download .json
Condensed preview — 954 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (17,925K chars).
[
  {
    "path": ".editorconfig",
    "chars": 286,
    "preview": "# https://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespac"
  },
  {
    "path": ".eslintrc.js",
    "chars": 3058,
    "preview": "const fs = require('fs');\n\nconst packages = fs\n  .readdirSync(`${__dirname}/packages`, { withFileTypes: true })\n  .filte"
  },
  {
    "path": ".gitattributes",
    "chars": 19,
    "preview": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/.kodiak.toml",
    "chars": 147,
    "preview": "version = 1\n\n[merge.automerge_dependencies]\nversions = [\"minor\", \"patch\"]\nusernames = [\"renovate\"]\n\n[approve]\nauto_appro"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 40,
    "preview": "*                 @decaporg/maintainers\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 40,
    "preview": "github: decaporg\nopen_collective: decap\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1654,
    "preview": "---\nname: Bug report\nabout: Report a problem you are experiencing\ntitle: 'Please replace with a clear and descriptive ti"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 915,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: 'Please replace with a clear and descriptive ti"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 953,
    "preview": "<!--\nThanks for submitting a pull request!\n\nPlease make sure you've read and understood our contributing guidelines here"
  },
  {
    "path": ".github/stale.yml",
    "chars": 693,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
  },
  {
    "path": ".github/workflows/create-release.yml",
    "chars": 897,
    "preview": "name: Create release\n\non:\n  create\n\njobs:\n  create-release:\n    name: Create GitHub Release\n    runs-on: ubuntu-latest\n "
  },
  {
    "path": ".github/workflows/labeler.yml",
    "chars": 1073,
    "preview": "name: Label PR\non:\n  pull_request:\n    types: [opened, edited]\n\njobs:\n  label-pr:\n    if: github.event_name == 'pull_req"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "chars": 3280,
    "preview": "name: Node CI\n\nconcurrency:\n  group: ci-${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\non:\n  push:"
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 2067,
    "preview": "name: Publish Packages\n\non:\n  push:\n    tags:\n      - 'decap-*@*'\n\npermissions:\n  contents: read\n  id-token: write  # Re"
  },
  {
    "path": ".github/workflows/sponsors.yml",
    "chars": 1011,
    "preview": "# Automatically updates the README with our GitHub Sponsors.\n# Fetches sponsor data from GitHub and adds their avatars t"
  },
  {
    "path": ".gitignore",
    "chars": 241,
    "preview": "dist/\nbin/\npublic/\nnode_modules/\nnpm-debug.log\n.DS_Store\n.tern-project\nyarn-error.log\n.vscode/\n.idea/\n.claude/\nmanifest."
  },
  {
    "path": ".husky/commit-msg",
    "chars": 80,
    "preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx --no-install commitlint --edit $1\n"
  },
  {
    "path": ".npmrc",
    "chars": 22,
    "preview": "legacy-peer-deps=true\n"
  },
  {
    "path": ".nvmrc",
    "chars": 6,
    "preview": "lts/*\n"
  },
  {
    "path": ".prettierignore",
    "chars": 208,
    "preview": "dist/\nbin/\npublic/\n.cache/\npackages/decap-cms-backend-github/src/fragmentTypes.js\npackages/decap-cms-backend-gitlab/src/"
  },
  {
    "path": ".prettierrc",
    "chars": 99,
    "preview": "{\n  \"arrowParens\": \"avoid\",\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true,\n  \"printWidth\": 100\n}\n"
  },
  {
    "path": ".storybook/main.js",
    "chars": 220,
    "preview": "module.exports = {\n  stories: [\n    '../packages/decap-cms-core/src/**/*.stories.js',\n    '../packages/decap-cms-ui-defa"
  },
  {
    "path": ".stylelintrc",
    "chars": 1234,
    "preview": "{\n  \"extends\": [\"stylelint-config-standard-scss\"],\n  \"customSyntax\": \"postcss-styled-syntax\",\n  \"rules\": {\n    \"block-no"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 28496,
    "preview": "# Changelog\nDecap CMS is a collection of npm packages with their own versions and changelogs, each listed\nbelow. The leg"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3220,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 7847,
    "preview": "# CONTRIBUTING\n\nContributions are always welcome, no matter how large or small. Before contributing,\nplease read the [co"
  },
  {
    "path": "LICENSE",
    "chars": 1079,
    "preview": "Copyright (c) 2016 Netlify <decap@p-m.si>\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 4401,
    "preview": "![Decap CMS](/img/decap.svg)\n\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/"
  },
  {
    "path": "SECURITY.md",
    "chars": 2172,
    "preview": "# Security Policy\n\nDecap CMS takes security seriously. This document outlines our security policy, supported versions, a"
  },
  {
    "path": "__mocks__/styleMock.js",
    "chars": 21,
    "preview": "module.exports = {};\n"
  },
  {
    "path": "babel.config.js",
    "chars": 1875,
    "preview": "const path = require('path');\n\nconst appVersion = require('./packages/decap-cms-app/package.json').version;\nconst coreVe"
  },
  {
    "path": "commitlint.config.js",
    "chars": 67,
    "preview": "module.exports = { extends: ['@commitlint/config-conventional'] };\n"
  },
  {
    "path": "cypress/Readme.md",
    "chars": 3372,
    "preview": "# Cypress Tests Guide\n\n## Introduction\n\n[Cypress](https://www.cypress.io/) is a JavaScript End to End Testing Framework "
  },
  {
    "path": "cypress/e2e/common/editorial_workflow.js",
    "chars": 3021,
    "preview": "import '../../utils/dismiss-local-backup';\nimport {\n  login,\n  createPost,\n  createPostAndExit,\n  updateExistingPostAndE"
  },
  {
    "path": "cypress/e2e/common/editorial_workflow_migrations.js",
    "chars": 1812,
    "preview": "import '../../utils/dismiss-local-backup';\nimport {\n  login,\n  createPostAndExit,\n  goToWorkflow,\n  goToCollections,\n  u"
  },
  {
    "path": "cypress/e2e/common/entries.js",
    "chars": 469,
    "preview": "export const entry1 = {\n  title: 'first title',\n  body: 'first body',\n  description: 'first description',\n  category: 'f"
  },
  {
    "path": "cypress/e2e/common/i18n.js",
    "chars": 1415,
    "preview": "import { newPost, populateEntry, publishEntry, flushClockAndSave } from '../../utils/steps';\n\nconst enterTranslation = s"
  },
  {
    "path": "cypress/e2e/common/i18n_editorial_workflow_spec.js",
    "chars": 1730,
    "preview": "import '../../utils/dismiss-local-backup';\nimport {\n  login,\n  goToWorkflow,\n  updateWorkflowStatus,\n  exitEditor,\n  pub"
  },
  {
    "path": "cypress/e2e/common/media_library.js",
    "chars": 4710,
    "preview": "import '../../utils/dismiss-local-backup';\nimport {\n  login,\n  goToMediaLibrary,\n  newPost,\n  populateEntry,\n  exitEdito"
  },
  {
    "path": "cypress/e2e/common/open_authoring.js",
    "chars": 2124,
    "preview": "import '../../utils/dismiss-local-backup';\nimport {\n  login,\n  createPostAndExit,\n  updateExistingPostAndExit,\n  goToWor"
  },
  {
    "path": "cypress/e2e/common/simple_workflow.js",
    "chars": 385,
    "preview": "import '../../utils/dismiss-local-backup';\nimport { login, createPostAndPublish, assertPublishedEntry } from '../../util"
  },
  {
    "path": "cypress/e2e/common/spec_utils.js",
    "chars": 3103,
    "preview": "export function before(taskResult, options, backend) {\n  console.log(`[spec_utils.before] START backend=${backend}`);\n  "
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_bitbucket_backend.js",
    "chars": 704,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_git-gateway_github_backend.js",
    "chars": 754,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_git-gateway_gitlab_backend.js",
    "chars": 754,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_github_backend_graphql.js",
    "chars": 817,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_github_backend_graphql_open_authoring.js",
    "chars": 878,
    "preview": "import fixture from './common/open_authoring';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry2"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_github_backend_rest.js",
    "chars": 813,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_github_backend_rest_open_authoring.js",
    "chars": 874,
    "preview": "import fixture from './common/open_authoring';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry2"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_gitlab_backend.js",
    "chars": 905,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_proxy_git_backend.js",
    "chars": 799,
    "preview": "import fixture from './common/editorial_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, en"
  },
  {
    "path": "cypress/e2e/editorial_workflow_spec_test_backend.js",
    "chars": 9670,
    "preview": "import '../utils/dismiss-local-backup';\nimport {\n  login,\n  createPost,\n  createPostAndExit,\n  exitEditor,\n  goToWorkflo"
  },
  {
    "path": "cypress/e2e/field_validations_spec.js",
    "chars": 2720,
    "preview": "import '../utils/dismiss-local-backup';\nimport {\n  login,\n  validateObjectFieldsAndExit,\n  validateNestedObjectFieldsAnd"
  },
  {
    "path": "cypress/e2e/i18n_editorial_workflow_spec_test_backend.js",
    "chars": 833,
    "preview": "import fixture from './common/i18n_editorial_workflow_spec';\n\nconst backend = 'test';\n\ndescribe(`I18N Test Backend Edito"
  },
  {
    "path": "cypress/e2e/i18n_simple_workflow_spec_proxy_fs_backend.js",
    "chars": 3390,
    "preview": "import * as specUtils from './common/spec_utils';\nimport { login } from '../utils/steps';\nimport { createEntryTranslateA"
  },
  {
    "path": "cypress/e2e/markdown_widget_backspace_spec.js",
    "chars": 2496,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget', () => {\n\n  before(() => {\n    Cypress.config('defau"
  },
  {
    "path": "cypress/e2e/markdown_widget_code_block_spec.js",
    "chars": 3091,
    "preview": "import { oneLineTrim, stripIndent } from 'common-tags';\nimport '../utils/dismiss-local-backup';\n\ndescribe('Markdown widg"
  },
  {
    "path": "cypress/e2e/markdown_widget_enter_spec.js",
    "chars": 2808,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget breaks', () => {\n  before(() => {\n    Cypress.config("
  },
  {
    "path": "cypress/e2e/markdown_widget_hotkeys_spec.js",
    "chars": 3213,
    "preview": "import '../utils/dismiss-local-backup';\nimport {HOT_KEY_MAP} from \"../utils/constants\";\nconst headingNumberToWord = ['',"
  },
  {
    "path": "cypress/e2e/markdown_widget_link_spec.js",
    "chars": 1903,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget link', () => {\n  before(() => {\n    Cypress.config('d"
  },
  {
    "path": "cypress/e2e/markdown_widget_list_spec.js",
    "chars": 18383,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget', () => {\n  describe('list', () => {\n    before(() =>"
  },
  {
    "path": "cypress/e2e/markdown_widget_marks_spec.js",
    "chars": 848,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget', () => {\n  describe('code mark', () => {\n    before("
  },
  {
    "path": "cypress/e2e/markdown_widget_quote_spec.js",
    "chars": 11202,
    "preview": "import '../utils/dismiss-local-backup';\n\ndescribe('Markdown widget', () => {\n  describe('quote block', () => {\n    befor"
  },
  {
    "path": "cypress/e2e/media_library_spec_bitbucket_backend.js",
    "chars": 626,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_bitbucket_backend_large_media.js",
    "chars": 652,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_git-gateway_github_backend_large_media.js",
    "chars": 715,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_git-gateway_gitlab_backend_large_media.js",
    "chars": 1224,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_github_backend_graphql.js",
    "chars": 741,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_github_backend_rest.js",
    "chars": 737,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_gitlab_backend.js",
    "chars": 656,
    "preview": "import fixture from './common/media_library';\nimport { entry1 } from './common/entries';\nimport * as specUtils from './c"
  },
  {
    "path": "cypress/e2e/media_library_spec_proxy_git_backend.js",
    "chars": 741,
    "preview": "import fixture from './common/media_library';\nimport * as specUtils from './common/spec_utils';\nimport { entry1 } from '"
  },
  {
    "path": "cypress/e2e/media_library_spec_test_backend.js",
    "chars": 404,
    "preview": "import fixture from './common/media_library';\n\nconst entries = [\n  {\n    title: 'first title',\n    body: 'first body',\n "
  },
  {
    "path": "cypress/e2e/search_suggestion_spec.js",
    "chars": 2043,
    "preview": "import { login } from '../utils/steps';\n\nconst search = (term, collection) => {\n  cy.get('[class*=SearchInput]').clear({"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_bitbucket_backend.js",
    "chars": 686,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_git-gateway_github_backend.js",
    "chars": 736,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_git-gateway_gitlab_backend.js",
    "chars": 736,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_github_backend_graphql.js",
    "chars": 776,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_github_backend_rest.js",
    "chars": 772,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_gitlab_backend.js",
    "chars": 680,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_proxy_fs_backend.js",
    "chars": 777,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_proxy_git_backend.js",
    "chars": 935,
    "preview": "import fixture from './common/simple_workflow';\nimport * as specUtils from './common/spec_utils';\nimport { entry1, entry"
  },
  {
    "path": "cypress/e2e/simple_workflow_spec_test_backend.js",
    "chars": 2334,
    "preview": "import '../utils/dismiss-local-backup';\nimport {\n  login,\n  newPost,\n  populateEntry,\n  exitEditor,\n  createPostAndPubli"
  },
  {
    "path": "cypress/e2e/view_filters_spec.js",
    "chars": 2764,
    "preview": "import { login } from '../utils/steps';\n\nconst filter = term => {\n  cy.contains('span', 'Filter by').click();\n  cy.conta"
  },
  {
    "path": "cypress/e2e/view_groups_spec.js",
    "chars": 1692,
    "preview": "import { login } from '../utils/steps';\n\nconst group = term => {\n  cy.contains('span', 'Group by').click();\n  cy.contain"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can change status on and publish multiple entries.json",
    "chars": 397686,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can change workflow status.json",
    "chars": 219392,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can create an entry.json",
    "chars": 130202,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can delete an entry.json",
    "chars": 147087,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can publish an editorial workflow entry.json",
    "chars": 180773,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can update an entry.json",
    "chars": 224414,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__can update workflow status from within the editor.json",
    "chars": 174768,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Editorial Workflow__successfully loads.json",
    "chars": 24494,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__can delete image from global media library.json",
    "chars": 86131,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__can publish entry with image.json",
    "chars": 199024,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__can save entry with image.json",
    "chars": 140985,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__can upload image from entry media library.json",
    "chars": 74643,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__can upload image from global media library.json",
    "chars": 93351,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__should not show draft entry image in global media library.json",
    "chars": 140975,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__should show published entry image in global media library.json",
    "chars": 205389,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - Large Media__should show published entry image in grid view.json",
    "chars": 193794,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__can delete image from global media library.json",
    "chars": 90338,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__can publish entry with image.json",
    "chars": 273240,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__can save entry with image.json",
    "chars": 145973,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__can upload image from entry media library.json",
    "chars": 76475,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__can upload image from global media library.json",
    "chars": 88764,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__should not show draft entry image in global media library.json",
    "chars": 145974,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__should show published entry image in global media library.json",
    "chars": 207575,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Media Library - REST API__should show published entry image in grid view.json",
    "chars": 197532,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Simple Workflow__can create an entry.json",
    "chars": 94179,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/BitBucket Backend Simple Workflow__successfully loads.json",
    "chars": 44298,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/2.0/repositories/owner/repo\",\n    \"headers\": {\n      \"Server\": \"nginx\",\n      \"V"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can change status on and publish multiple entries.json",
    "chars": 801714,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can change workflow status.json",
    "chars": 413594,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can create an entry.json",
    "chars": 254817,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can delete an entry.json",
    "chars": 294272,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can publish an editorial workflow entry.json",
    "chars": 331623,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can update an entry.json",
    "chars": 576973,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__can update workflow status from within the editor.json",
    "chars": 348180,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Editorial Workflow__successfully loads.json",
    "chars": 70606,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__can delete image from global media library.json",
    "chars": 165384,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__can publish entry with image.json",
    "chars": 348080,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__can save entry with image.json",
    "chars": 272235,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from entry media library.json",
    "chars": 95546,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__can upload image from global media library.json",
    "chars": 155044,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__should not show draft entry image in global media library.json",
    "chars": 277064,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in global media library.json",
    "chars": 367764,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Media Library - Large Media__should show published entry image in grid view.json",
    "chars": 351064,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Simple Workflow__can create an entry.json",
    "chars": 160717,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitHub) Backend Simple Workflow__successfully loads.json",
    "chars": 64613,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can change status on and publish multiple entries.json",
    "chars": 254367,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can change workflow status.json",
    "chars": 150177,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can create an entry.json",
    "chars": 99672,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can delete an entry.json",
    "chars": 113735,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can publish an editorial workflow entry.json",
    "chars": 128556,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can update an entry.json",
    "chars": 160011,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__can update workflow status from within the editor.json",
    "chars": 127370,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Editorial Workflow__successfully loads.json",
    "chars": 30241,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__can delete image from global media library.json",
    "chars": 85204,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__can publish entry with image.json",
    "chars": 215366,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__can save entry with image.json",
    "chars": 121195,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from entry media library.json",
    "chars": 64395,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__can upload image from global media library.json",
    "chars": 76822,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__should not show draft entry image in global media library.json",
    "chars": 121163,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in global media library.json",
    "chars": 162153,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Media Library - Large Media__should show published entry image in grid view.json",
    "chars": 150182,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Simple Workflow__can create an entry.json",
    "chars": 77645,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/Git Gateway (GitLab) Backend Simple Workflow__successfully loads.json",
    "chars": 30241,
    "preview": "[\n  {\n    \"body\": \"grant_type=password&username=decap%40p-m.si&password=12345678\",\n    \"method\": \"POST\",\n    \"url\": \"/.n"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can change entry status from fork.json",
    "chars": 231603,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry on fork.json",
    "chars": 197804,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can create an entry.json",
    "chars": 151039,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can delete review entry from fork.json",
    "chars": 257934,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can publish an editorial workflow entry.json",
    "chars": 171756,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Server\": \"GitHub.com\",\n      \"Content-Type\": \"app"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can return entry to draft and delete it.json",
    "chars": 265459,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update a draft entry on fork.json",
    "chars": 311653,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__can update an entry.json",
    "chars": 271673,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully forks repository and loads.json",
    "chars": 121499,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API - Open Authoring__successfully loads.json",
    "chars": 84883,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API__can create an entry.json",
    "chars": 147388,
    "preview": "[\n  {\n    \"body\": \"{\\\"operationName\\\":null,\\\"variables\\\":{},\\\"query\\\":\\\"{\\\\n  viewer {\\\\n    id\\\\n    avatar_url: avatar"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API__can delete an entry.json",
    "chars": 171886,
    "preview": "[\n  {\n    \"body\": \"{\\\"operationName\\\":null,\\\"variables\\\":{},\\\"query\\\":\\\"{\\\\n  viewer {\\\\n    id\\\\n    avatar_url: avatar"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API__can publish an editorial workflow entry.json",
    "chars": 168105,
    "preview": "[\n  {\n    \"body\": \"{\\\"operationName\\\":null,\\\"variables\\\":{},\\\"query\\\":\\\"{\\\\n  viewer {\\\\n    id\\\\n    avatar_url: avatar"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API__can update an entry.json",
    "chars": 262550,
    "preview": "[\n  {\n    \"body\": \"{\\\"operationName\\\":null,\\\"variables\\\":{},\\\"query\\\":\\\"{\\\\n  viewer {\\\\n    id\\\\n    avatar_url: avatar"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - GraphQL API__successfully loads.json",
    "chars": 81232,
    "preview": "[\n  {\n    \"body\": \"{\\\"operationName\\\":null,\\\"variables\\\":{},\\\"query\\\":\\\"{\\\\n  viewer {\\\\n    id\\\\n    avatar_url: avatar"
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - REST API - Open Authoring__can change entry status from fork.json",
    "chars": 295348,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - REST API - Open Authoring__can create an entry.json",
    "chars": 219640,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - REST API - Open Authoring__can delete review entry from fork.json",
    "chars": 510411,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  },
  {
    "path": "cypress/fixtures/GitHub Backend Editorial Workflow - REST API - Open Authoring__can update an entry.json",
    "chars": 544550,
    "preview": "[\n  {\n    \"method\": \"GET\",\n    \"url\": \"/user\",\n    \"headers\": {\n      \"Content-Type\": \"application/json; charset=utf-8\","
  }
]

// ... and 789 more files (download for full content)

About this extraction

This page contains the full source code of the decaporg/decap-cms GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 954 files (43.4 MB), approximately 4.1M tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!