diff --git a/gatsby-config.mjs b/gatsby-config.mjs index 91b532ee..25aeb2df 100644 --- a/gatsby-config.mjs +++ b/gatsby-config.mjs @@ -106,6 +106,9 @@ const config = { options: { extensions: [`.mdx`], gatsbyRemarkPlugins: [ + { + resolve: `gatsby-remark-double-brackets-link`, + }, { resolve: `gatsby-remark-images`, options: { diff --git a/package-lock.json b/package-lock.json index 9c119b94..109921fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "gatsby-plugin-sharp": "5.13.1", "gatsby-plugin-styled-components": "6.13.1", "gatsby-remark-copy-linked-files": "^6.13.1", + "gatsby-remark-double-brackets-link": "^0.1.11", "gatsby-remark-images": "7.13.1", "gatsby-remark-prismjs": "7.13.1", "gatsby-source-filesystem": "^5.13.1", @@ -779,30 +780,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", @@ -815,21 +792,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -887,30 +849,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", @@ -926,18 +864,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", @@ -974,18 +900,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", @@ -998,36 +912,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", @@ -2356,12 +2240,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "peer": true - }, "node_modules/@builder.io/partytown": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/@builder.io/partytown/-/partytown-0.7.6.tgz", @@ -3443,979 +3321,272 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "peer": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "peer": true, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", "dependencies": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^16.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/console/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "peer": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=6.0.0" } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jest/core/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "peer": true, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "license": "Apache-2.0" + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "license": "MIT" + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "license": "MIT", "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@lezer/common": "^1.0.0" } }, - "node_modules/@jest/core/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.3.tgz", + "integrity": "sha512-RXwGZ/0eCqtCY8FLTM/koR60w+MXyvBUpToXiIyjOcBnC81tAlTUHrRUavCEWPI9zc9VgvpK3+cbumPyR8BSuA==", + "cpu": [ + "arm64" ], - "peer": true, - "engines": { - "node": ">=8" - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.3.tgz", + "integrity": "sha512-337dNzh5yCdNCTk8kPfoU7jR3otibSlPDGW0vKZT97rKnQMb9tNdto3RtWoGPsQ8hKmlRZpojOJtmwjncq1MoA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@jest/core/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.3.tgz", + "integrity": "sha512-mU2HFJDGwECkoD9dHQEfeTG5mp8hNS2BCfwoiOpVPMeapjYpQz9Uw3FkUjRZ4dGHWKbin40oWHuL0bk2bCx+Sg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "peer": true, - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.3.tgz", + "integrity": "sha512-VJw60Mdgb4n+L0fO1PqfB0C7TyEQolJAC8qpqvG3JoQwvyOv6LH7Ib/WE3wxEW9nuHmVz9jkK7lk5HfWWgoO1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.3.tgz", + "integrity": "sha512-qaReO5aV8griBDsBr8uBF/faO3ieGjY1RY4p8JvTL6Mu1ylLrTVvOONqKFlNaCwrmUjWw5jnf7VafxDAeQHTow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.3.tgz", + "integrity": "sha512-cK+Elf3RjEzrm3SerAhrFWL5oQAsZSJ/LmjL1joIpTfEP1etJJ9CTRvdaV6XLYAxaEkfdhk/9hOvHLbR9yIhCA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "peer": true, + "node_modules/@mdx-js/mdx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", + "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "@types/mdx": "^2.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/fake-timers/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" } }, - "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "peer": true, + "node_modules/@mischnic/json-sourcemap": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", + "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", + "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" + "@lezer/common": "^1.0.0", + "@lezer/lr": "^1.0.0", + "json5": "^2.2.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=12.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "peer": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/@jest/reporters/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "peer": true, - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "peer": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "peer": true, - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "peer": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/transform/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "peer": true - }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "license": "Apache-2.0" - }, - "node_modules/@lezer/common": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", - "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", - "license": "MIT" - }, - "node_modules/@lezer/lr": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", - "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.3.tgz", - "integrity": "sha512-RXwGZ/0eCqtCY8FLTM/koR60w+MXyvBUpToXiIyjOcBnC81tAlTUHrRUavCEWPI9zc9VgvpK3+cbumPyR8BSuA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.3.tgz", - "integrity": "sha512-337dNzh5yCdNCTk8kPfoU7jR3otibSlPDGW0vKZT97rKnQMb9tNdto3RtWoGPsQ8hKmlRZpojOJtmwjncq1MoA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@lmdb/lmdb-linux-arm": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.3.tgz", - "integrity": "sha512-mU2HFJDGwECkoD9dHQEfeTG5mp8hNS2BCfwoiOpVPMeapjYpQz9Uw3FkUjRZ4dGHWKbin40oWHuL0bk2bCx+Sg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.3.tgz", - "integrity": "sha512-VJw60Mdgb4n+L0fO1PqfB0C7TyEQolJAC8qpqvG3JoQwvyOv6LH7Ib/WE3wxEW9nuHmVz9jkK7lk5HfWWgoO1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@lmdb/lmdb-linux-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.3.tgz", - "integrity": "sha512-qaReO5aV8griBDsBr8uBF/faO3ieGjY1RY4p8JvTL6Mu1ylLrTVvOONqKFlNaCwrmUjWw5jnf7VafxDAeQHTow==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@lmdb/lmdb-win32-x64": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.3.tgz", - "integrity": "sha512-cK+Elf3RjEzrm3SerAhrFWL5oQAsZSJ/LmjL1joIpTfEP1etJJ9CTRvdaV6XLYAxaEkfdhk/9hOvHLbR9yIhCA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@mdx-js/mdx": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz", - "integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdx": "^2.0.0", - "collapse-white-space": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "estree-util-scope": "^1.0.0", - "estree-walker": "^3.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "markdown-extensions": "^2.0.0", - "recma-build-jsx": "^1.0.0", - "recma-jsx": "^1.0.0", - "recma-stringify": "^1.0.0", - "rehype-recma": "^1.0.0", - "remark-mdx": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "source-map": "^0.7.0", - "unified": "^11.0.0", - "unist-util-position-from-estree": "^2.0.0", - "unist-util-stringify-position": "^4.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=16", - "react": ">=16" - } - }, - "node_modules/@mischnic/json-sourcemap": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", - "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0", - "@lezer/lr": "^1.0.0", - "json5": "^2.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "license": "MIT", - "dependencies": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - }, - "engines": { - "node": ">=4" + "node": ">=4" } }, "node_modules/@mrmlnc/readdir-enhanced/node_modules/glob-to-regexp": { @@ -5835,24 +5006,6 @@ "node": ">=8" } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "peer": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "peer": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -5943,47 +5096,6 @@ "@types/estree": "*" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "peer": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "peer": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "peer": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -6086,15 +5198,6 @@ "@types/node": "*" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "peer": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -6243,12 +5346,6 @@ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==", "license": "MIT" }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "peer": true - }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -6310,12 +5407,6 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "license": "MIT" }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "peer": true - }, "node_modules/@types/stylis": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", @@ -6374,190 +5465,6 @@ "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "peer": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "peer": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", - "peer": true, - "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", - "peer": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", - "peer": true, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", - "peer": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", - "peer": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", @@ -6731,13 +5638,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "license": "Apache-2.0" }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "peer": true - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -6790,25 +5690,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "peer": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -7605,36 +6486,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "eslint": ">= 4.12.1" - } - }, - "node_modules/babel-eslint/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/babel-extract-comments": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz", @@ -7647,53 +6498,6 @@ "node": ">=4" } }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "peer": true, - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-jest/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, "node_modules/babel-jsx-utils": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/babel-jsx-utils/-/babel-jsx-utils-1.1.0.tgz", @@ -7783,37 +6587,6 @@ "object.assign": "^4.1.0" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "peer": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, "node_modules/babel-plugin-lodash": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz", @@ -7952,32 +6725,6 @@ "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", "license": "MIT" }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "peer": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/babel-preset-fbjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", @@ -8072,22 +6819,6 @@ "@babel/core": "^7.11.6" } }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "peer": true, - "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -8445,12 +7176,6 @@ "node": ">=8" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "peer": true - }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -8938,15 +7663,6 @@ "upper-case-first": "^2.0.2" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -9080,12 +7796,6 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "license": "MIT" }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "peer": true - }, "node_modules/class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -9400,16 +8110,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "peer": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, "node_modules/coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -9515,12 +8215,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "peer": true - }, "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -10394,30 +9088,6 @@ "node": ">=8.0.0" } }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "peer": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "peer": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "peer": true - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -10866,61 +9536,6 @@ "node": ">= 6" } }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "peer": true, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/data-urls/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "peer": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/data-urls/node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "peer": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/data-urls/node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "peer": true - }, - "node_modules/data-urls/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "peer": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -11020,12 +9635,6 @@ "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "peer": true - }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", @@ -11268,12 +9877,6 @@ "node": ">=0.10.0" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "peer": true - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -11465,15 +10068,6 @@ "node": ">=0.10" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/detect-port": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", @@ -11723,28 +10317,6 @@ ], "license": "BSD-2-Clause" }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "deprecated": "Use your platform's native DOMException instead", - "peer": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", @@ -12115,18 +10687,6 @@ "integrity": "sha512-Zq16uk1hfQhyGx5GpwPAYDwddJuSGhtRhgOA2mCxANYaDT79nAeGnaXogMGng4KqLaJUVnOnuL0+TDop9nLOiA==", "license": "ISC" }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -13497,15 +12057,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -13573,46 +12124,6 @@ "node": ">=6" } }, - "node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/expect/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, "node_modules/express": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", @@ -16765,6 +15276,34 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/gatsby-remark-double-brackets-link": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/gatsby-remark-double-brackets-link/-/gatsby-remark-double-brackets-link-0.1.11.tgz", + "integrity": "sha512-fUUt7jywCrYAvjgkPnmkMJuVclJxOH1ibJm89HglbqpcSpwBYGzNEw7pDrXsxBM3QnFQxhQ58RACuLOpxKzGaA==", + "dependencies": { + "slugify": "^1.5.3", + "unist-util-visit": "^2.0.0" + } + }, + "node_modules/gatsby-remark-double-brackets-link/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, + "node_modules/gatsby-remark-double-brackets-link/node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/gatsby-remark-images": { "version": "7.13.1", "resolved": "https://registry.npmjs.org/gatsby-remark-images/-/gatsby-remark-images-7.13.1.tgz", @@ -17919,15 +16458,6 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "license": "ISC" }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "peer": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -20737,39 +19267,6 @@ "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", "license": "MIT" }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "peer": true, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-encoding-sniffer/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/html-encoding-sniffer/node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "peer": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, "node_modules/html-entities": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", @@ -20786,12 +19283,6 @@ ], "license": "MIT" }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "peer": true - }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -21019,25 +19510,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "peer": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -21719,15 +20191,6 @@ "node": ">=8" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-generator-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", @@ -21910,12 +20373,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "peer": true - }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -22260,105 +20717,6 @@ "license": "MIT", "optional": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "peer": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "peer": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "peer": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "peer": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "peer": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", @@ -22417,249 +20775,53 @@ "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "license": "MIT" }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "peer": true, - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus": { + "node_modules/jest-diff": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "peer": true, + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { + "node_modules/jest-get-type": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "license": "MIT", "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-cli": { + "node_modules/jest-matcher-utils": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "peer": true, + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "license": "MIT", "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -22667,1343 +20829,25 @@ "picomatch": "^2.2.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "peer": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "peer": true, - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/ci-info": { + "node_modules/jest-util/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "peer": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-each/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "peer": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-jsdom/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "peer": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "license": "MIT", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-haste-map/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-haste-map/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "peer": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-jasmine2/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, "engines": { "node": ">=8" } }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "peer": true, - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-message-util/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "peer": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "peer": true, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "peer": true, - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "peer": true, - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runtime/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "peer": true, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "peer": true, - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "peer": true, - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "peer": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "peer": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "peer": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", @@ -24271,232 +21115,6 @@ "signal-exit": "^3.0.2" } }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "peer": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/jsdom/node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "peer": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/jsdom/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "peer": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz", - "integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==", - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "peer": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "peer": true - }, - "node_modules/jsdom/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "peer": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsdom/node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "peer": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdom/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "peer": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/jsdom/node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "peer": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/jsdom/node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "peer": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "peer": true - }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "peer": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsdom/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "peer": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -24714,15 +21332,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -25122,15 +21731,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "license": "ISC" }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "peer": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -30360,12 +26960,6 @@ "node": ">=0.10.0" } }, - "node_modules/nwsapi": { - "version": "2.2.18", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.18.tgz", - "integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==", - "peer": true - }, "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -32845,6 +29439,7 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", "license": "MIT", + "optional": true, "dependencies": { "punycode": "^2.3.1" }, @@ -32941,12 +29536,6 @@ "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "peer": true - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -36498,12 +33087,6 @@ "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==", "license": "MIT" }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "peer": true - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -36558,15 +33141,6 @@ "deprecated": "https://github.com/lydell/resolve-url#deprecated", "license": "MIT" }, - "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "peer": true, - "engines": { - "node": ">=10" - } - }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -36926,18 +33500,6 @@ "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", "license": "ISC" }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -38056,27 +34618,6 @@ "node": "*" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "peer": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -38238,19 +34779,6 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "peer": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/string-natural-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", @@ -38908,19 +35436,6 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -39040,12 +35555,6 @@ "node": ">=0.10.0" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "peer": true - }, "node_modules/sync-fetch": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.3.1.tgz", @@ -39262,49 +35771,6 @@ "rimraf": "bin.js" } }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "peer": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "peer": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/terser": { "version": "5.37.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", @@ -39413,20 +35879,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "peer": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/text-decoder": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", @@ -39465,12 +35917,6 @@ "node": ">=0.8" } }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "peer": true - }, "node_modules/throttleit": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", @@ -39596,12 +36042,6 @@ "node": ">=14.14" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "peer": true - }, "node_modules/to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", @@ -40034,15 +36474,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -40163,19 +36594,6 @@ "is-typedarray": "^1.0.0" } }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/ua-parser-js": { "version": "1.0.40", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", @@ -40902,16 +37320,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "peer": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", @@ -41061,26 +37469,6 @@ "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "license": "MIT" }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "peer": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "peer": true - }, "node_modules/valid-url": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", @@ -41174,37 +37562,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "peer": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "peer": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "peer": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -41888,12 +38245,6 @@ "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "license": "MIT" }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "peer": true - }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -41916,12 +38267,6 @@ "node": ">=4.0" } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "peer": true - }, "node_modules/xmlhttprequest-ssl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", diff --git a/package.json b/package.json index d9b6a800..842431f3 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "gatsby-plugin-sharp": "5.13.1", "gatsby-plugin-styled-components": "6.13.1", "gatsby-remark-copy-linked-files": "^6.13.1", + "gatsby-remark-double-brackets-link": "^0.1.11", "gatsby-remark-images": "7.13.1", "gatsby-remark-prismjs": "7.13.1", "gatsby-source-filesystem": "^5.13.1", diff --git a/src/blog/2019-07-13-shell-architecture.mdx b/src/blog/2019-07-13-shell-architecture.mdx index 8ed1ba13..865130e2 100644 --- a/src/blog/2019-07-13-shell-architecture.mdx +++ b/src/blog/2019-07-13-shell-architecture.mdx @@ -50,6 +50,6 @@ We are following an MVC pattern for developing this server. Broadly speaking eve We use the config module to handle different app configuration. This includes sensitive credentials like database parameters. config expects a config directory with default.json file in it. We don't check that into the git repository for security reasons. You will have to contact us via email or our slack channel to get access to it. -# Footnotes +## Footnotes 1. Even though currently Shell Server uses amazon’s hosted MySQL service for our Database needs, for all practical purposes, it is ok to consider it part of Shell. diff --git a/src/blog/2019-07-26-tattle-data-science-finding-similar-videos-efficiently.mdx b/src/blog/2019-07-26-tattle-data-science-finding-similar-videos-efficiently.mdx index ae4c191b..17e5cead 100644 --- a/src/blog/2019-07-26-tattle-data-science-finding-similar-videos-efficiently.mdx +++ b/src/blog/2019-07-26-tattle-data-science-finding-similar-videos-efficiently.mdx @@ -6,6 +6,7 @@ author: Swair Shah project: Kosh tags: machine-learning, devlog --- +import { Heading } from 'grommet' One of the challenges we face at Tattle is to efficiently check if a given post or message has been encountered earlier by our system. @@ -18,7 +19,7 @@ In this post we will describe our ideas for video and GIF content representation We want to come up with an approach which not only works for finding duplicate or near duplicate videos but we also want to extract some useful information from the video. For example, we may want to generate a description of this video with tags such as `speech`, `president`, `nixon` etc. Processing a video can be very processor intensive. The Nixon video in our example is 36 seconds long. We can extract the frames from the video using OpenCV. There are 1109 frames in the video for just 36 seconds. Even if we are using an efficient deep Convolutional Neural Network to classify labels or generate representation, it would be inefficient to use all the frames of the video. -### Anchor Frames +## Anchor Frames We want to find what we call "Anchor Frames" for a given video. Anchor Frames are a small set of frames which are a good representation of the video. For our video let us look at a sample from the extracted frames. ![](../images/Screen-Shot-2019-07-19-at-3.32.55-PM.png)A sample of frames from the video @@ -66,27 +67,27 @@ We can see that after the first two frame selection the error reduction slows do ![](../images/frame0-3.jpg)![](/content/images/2019/07/frame700.jpg) Using these these two frames as anchor frames we achieve $80.48\%$ reduction in reconstruction error.  Now that we have found a set of representative anchor frames for a video, we can use the same technique we use for image duplicate detection and label extraction. To generate a fingerprint for the video we can take individual image fingerprint (pHash or pre-trained Convolutional Neural Network features) for all the anchor frames  and either append them or take an average. -## Semantic Labels for Videos +Semantic Labels for Videos To classify videos we use Google vision API on the set of anchor frames. Passing the second anchor frame to Google Vision API gives the following labels. ![](../images/Screen-Shot-2019-07-19-at-5.37.05-PM.png) We can pass all the anchor frames to the API and take a union of the labels. -## Advantages and Limitations +Advantages and Limitations One of the advantages of this anchor frame approach is that it is robust to minor changes in the video. In case the video is slightly edited or if a few frames are missing, the anchor frames are not affected in most cases as they are still a good representation of the remaining video frames. Though we definitely need more testing to verify this on a broad class of videos. One could use an average frame as a representation of the video but this average frame is sensitive to the video editing. The anchor frames are also much "cleaner" than the average frame which tends to be blurry passing it to the image classifier may not give useful results. The result of the Google Vision API on the average frame is shown below. ![](../images/Screen-Shot-2019-07-19-at-11.41.18-PM.png) We can see that the score for the labels relating to Richard Nixon has dropped drastically. This only gets worse as the video size increases and the average frame becomes more noisy. -## Some Optimizations and Future Work +Some Optimizations and Future Work In our example the matrix formed after vectorizing each frames was $7500 \times 1000$. Operating on such a matrix is computationally expensive (the complexity of QR is $O(n^3)$). We can try reducing the row dimensions and the column dimensions. Here the number of rows is due to the size of the each frame which is $50 \times 50 \times 3$, even after resizing. We could resize further but after a certain point we start to lose useful information in the image. One possible way which we experimented with is using a pre-trained Convolutional Neural Network to get embeddings of each frame and then using these as a representation for the image.  These embeddings are known to capture semantic information in the image [3]. The size of the embedding depends on the CNN architecture we use. Using a ResNet-18 architecture gives us $512$ dimensional embeddings. This is a significant improvement on $7500$. Another optimization we tried is sampling one frame in every 10 frames. There is a trade-off involved between speed and accuracy and one needs to tune these parameters for a given use case. Using these optimizations the matrix $X$ that we compute the QR-decomposition of is of size $512 \times 100$. -## Limitations and Future work +Limitations and Future work The approach taken is by no means a silver bullet. There are videos where the base frame changes drastically and the number of anchor frames required to achieve a small error would be very high. So far our approach works well for typical videos shared frequently on WhatsApp and other messaging platforms. If you have suggestions on other approaches do get in touch with us! -# References +## References - [1] G. H. Golub and C. F. Van-Loan.Matrix computations. The Johns Hopkins University Press, third edition,1996. - [2] Maung, Crystal, and Haim Schweitzer. "Pass-efficient unsupervised feature selection." *Advances in Neural Information Processing Systems*. 2013. diff --git a/src/blog/2020-05-22-topic-modelling-on-fact-checked-stories.mdx b/src/blog/2020-05-22-topic-modelling-on-fact-checked-stories.mdx index 06e63889..14a23fbd 100644 --- a/src/blog/2020-05-22-topic-modelling-on-fact-checked-stories.mdx +++ b/src/blog/2020-05-22-topic-modelling-on-fact-checked-stories.mdx @@ -6,19 +6,21 @@ tags: devlog author: Kruttika Nadig --- +import { Heading } from 'grommet' + This Python notebook builds a topic model on the headlines of 13,000+ fact-checking stories in the Tattle archive, and identifies 7 broad "topics" within India's misinformation landscape. Steps 1 to 7 cover the text processing and topic modelling process, while steps 8 and 9 interpret and visualise the resulting topics with an interactive pyLDAvis plot. Step 10 is a standalone n-gram analysis exercise that highlights the top words and word-pairs in the headlines. -#### Why headlines? +### Why headlines? The body text of fact-checking stories sometimes contains noisy strings which are not related to the misinformation being fact-checked. The headlines, however, are focused on the misinformation. (We repeated this analysis with the body text and found no significant difference apart from the noise) -#### Brief description of the model +### Brief description of the model Latent Dirichlet Allocation is an unsupervised, probabilistic model that generates topic-document and word-topic probability distributions from a collection of text documents. Topics are themes that occur in documents. Here, document = story headline. -#### Process +### Process 1. Getting the data from the Tattle database 2. Text cleaning (removing noise, English / non-English separation using regex) @@ -34,7 +36,8 @@ Latent Dirichlet Allocation is an unsupervised, probabilistic model that generat ## 1. Getting the data - # Importing libraries + ### Importing libraries +

import os import requests import time @@ -76,10 +79,12 @@ Latent Dirichlet Allocation is an unsupervised, probabilistic model that generat import pyLDAvis import pyLDAvis.gensim from gensim import similarities +

- # Get data from factchecking sites MongoDB + ### Get data from factchecking sites MongoDB +

def initialize_mongo(): mongo_url = "mongodb+srv://os.environ.get("FACTCHECK_DB_USERNAME"):os.environ.get("FACTCHECK_DB_PASSWORD")@tattle-data-fkpmg.mongodb.net/test?retryWrites=true&w=majority&ssl=true&ssl_cert_reqs=CERT_NONE" cli = MongoClient(mongo_url) @@ -100,7 +105,7 @@ Latent Dirichlet Allocation is an unsupervised, probabilistic model that generat df = df.drop_duplicates() - # Snapshot of headlines + ### Snapshot of headlines df["headline"][0:3] @@ -108,21 +113,22 @@ Latent Dirichlet Allocation is an unsupervised, probabilistic model that generat 1 Photoshopped promo of ABP News over Chandrayaa... 2 Video of scuffle between men in khaki uniform ... Name: headline, dtype: object +

## 2. Text cleaning The headlines contain some special accents which the regex pattern for non-English text is unable to catch, so we remove these before proceeding to separate the English and non-English headlines. - # Save headlines in a variable + ### Save headlines in a variable raw_data = df["headline"].values.tolist() - # Defining a function to remove accented characters in the headlines + ### Defining a function to remove accented characters in the headlines def data_dict(sentences): return dict((sentence, ", ".join(simple_preprocess(str(sentence), deacc=True, max_len=100))) for sentence in sentences) result = data_dict(raw_data) - # Separating non-English headlines using regex + ### Separating non-English headlines using regex pat = re.compile("[^\x00-\x7F]") # matches non-English characters non_eng = [k for k,v in result.items() if pat.search(v)] eng = [k for k,v in result.items() if not pat.search(v)] @@ -134,23 +140,25 @@ The headlines contain some special accents which the regex pattern for non-Engli 8298 5241 -## 3. Translating non-English headlines +3. Translating non-English headlines Googletrans is a free library that sends translation requests to the Google Translate API. Random time delays between requests are advised, else Google may (and probably will) block your ip address. Translating the 5200+ non-English headlines took approximately 6.5 hours. - # Translating non-English headlines using googletrans library - + ### Translating non-English headlines using googletrans library +

translator = Translator() translations = [] for doc in non_eng: translations.append(translator.translate(doc).text) time.sleep(uniform(3,5)) +

- # Saving the original and translated headlines for future reference + ### Saving the original and translated headlines for future reference +

translated_headlines = dict(zip(non_eng, translations)) translations_df = pd.DataFrame(translated_headlines.items(), columns = ["headline", "translation"]) translations_df["original_english"] = 0 @@ -158,6 +166,7 @@ Translating the 5200+ non-English headlines took approximately 6.5 hours. translations_df["original_english"].fillna(value=1, inplace = True) translations_df["original_english"] = translations_df["original_english"].astype(int) translations_df.to_csv("headlines_with_translations.csv") +

## 4. Text preprocessing @@ -165,29 +174,29 @@ The text needs to be prepared for topic modelling by tokenizing it (splitting in After some experimentation, we found that excluding all parts of speech except nouns and proper nouns during text preprocessing resulted in a more well-defined topics later. - # Combining the headlines + ### Combining the headlines all_headlines = eng + translations - # Tokenizing the headlines + ### Tokenizing the headlines all_tokens = list(sent_to_words(all_headlines)) - # Creating stop words list + ### Creating stop words list stop_words = stopwords.words("english") - # Adding domain words + ### Adding domain words stop_words.extend(["fake", "fact", "check", "checked", "factcheck", "news", "false", "falsely", "true", "truth", "viral", "video", "image", "picture", "photo", "claim", "claiming", "share", "clip", "misleading"]) - # Stop word removal + ### Stop word removal data_stopped = [[word for word in doc if word not in stop_words] for doc in all_tokens] - # Creating bigrams + ### Creating bigrams bigram = gensim.models.Phrases(data_stopped, min_count=10) for idx in range(len(data_stopped)): for token in bigram[data_stopped[idx]]: if '_' in token: - # If token is bigram, add it to document + ### If token is bigram, add it to document data_stopped[idx].append(token) data_with_bigrams = data_stopped - # Lemmatizing i.e. reducing words to their root form - # Including only nouns as this improves both topic interpretability and coherence scores + ### Lemmatizing i.e. reducing words to their root form + ### Including only nouns as this improves both topic interpretability and coherence scores def lemmatization(docs, allowed_postags=["NOUN", "PROPN"]): nlp = spacy.load("en_core_web_sm") docs_out = [] @@ -198,25 +207,34 @@ After some experimentation, we found that excluding all parts of speech except n data_lemmatized = lemmatization(data_with_bigrams, allowed_postags=["NOUN", "PROPN"]) - # Removing any stopwords created because of lemmatization + ### Removing any stopwords created because of lemmatization data_cleaned = [[word for word in doc if word not in stop_words] for doc in data_lemmatized] -## 5. Text transformation: creating a corpus of vectors +5. Text transformation: creating a corpus of vectors -Topic modelling with the Gensim library involves certain concepts like documents, corpus, vectors and bag of words. These are explained here - [https://radimrehurek.com/gensim/auto_examples/core/run_core_concepts.html](https://radimrehurek.com/gensim/auto_examples/core/run_core_concepts.html) +Topic modelling with the Gensim library involves certain concepts like documents, corpus, vectors and bag of words. These are explained here - +
+ +https://radimrehurek.com/gensim/auto_examples/core/run_core_concepts.html + +
- # Creating a dictionary + ### Creating a dictionary id2word = corpora.Dictionary(data_cleaned) - # Creating a document-term matrix + ### Creating a document-term matrix print('Number of unique tokens: %d' % len(id2word)) id2word.filter_extremes(no_below = 20) - # Creating a document-term matrix + ### Creating a document-term matrix corpus = [id2word.doc2bow(doc) for doc in data_cleaned] Number of unique tokens: 7585 -## 6. Selecting the number of topics +6. Selecting the number of topics Topic coherence is an evaluation metric for topic models that measures the degree of semantic similarity between high scoring words in the topic. The graph below helps find an appropriate 'k' number of topics to model. @@ -232,6 +250,7 @@ Topic coherence is an evaluation metric for topic models that measures the degre limit : Max num of topics Returns: +

------- model_list : List of LDA topic models coherence_values : Coherence values corresponding to the LDA model with respective number of topics @@ -246,10 +265,11 @@ Topic coherence is an evaluation metric for topic models that measures the degre coherence_values.append(coherencemodel.get_coherence()) return model_list, coherence_values + model_list, coherence_values = compute_coherence_values(dictionary=id2word, corpus=corpus, texts=data_cleaned, start=2, limit=20, step=1) - # Show graph + ### Show graph limit=20; start=2; step=1; x = range(start, limit, step) plt.plot(x, coherence_values) @@ -257,17 +277,18 @@ Topic coherence is an evaluation metric for topic models that measures the degre plt.ylabel("Coherence score") plt.legend(("coherence_values"), loc='best') plt.show() +

![png](../images/output_18_1.png) -## 7. Building the topic model +7. Building the topic model We choose k=7 as k=11 produces the same number of meaningful topics (and four not-so-meaningful ones).  This 'k' is supplied as a hyperparameter to the model. The model returns topics (clusters of words) along with their probabilities of belonging to that topic / cluster. lda_model = ldamodel.LdaModel(corpus = corpus, num_topics = 7, id2word = id2word, chunksize=100, alpha='auto', eta='auto', passes = 5, random_state = 0) - # Printing the topic-word probabilities + ### Printing the topic-word probabilities pprint(lda_model.print_topics()) @@ -312,11 +333,21 @@ The topic-word probability distributions above can be interpreted as 7 topics or - Law enforcement & crime - Citizenship act protests & different states -## 9. Interactive model visualisation +9. Interactive model visualisation The pyLDAvis plot below presents the results of the topic model in a more interpretable form. It provides a global view of the topics (and how they differ from each other), while at the same time allowing for a deep inspection of the terms most highly associated with each individual topic. Hovering over a topic "bubble" highlights the associated terms in red on the right. A bubble's size is proportional to the relative prevalence of that topic in the corpus. -To read more about the plot, check out this paper by the creators - [https://nlp.stanford.edu/events/illvi2014/papers/sievert-illvi2014.pdf](https://nlp.stanford.edu/events/illvi2014/papers/sievert-illvi2014.pdf) +To read more about the plot, check out this paper by the creators - +
+ + https://nlp.stanford.edu/events/illvi2014/papers/sievert-illvi2014.pdf + +
+ ``` pyLDAvis.enable_notebook() @@ -385,7 +416,7 @@ new LDAvis("#" + "ldavis_el202961124743444486412651638", ldavis_el20296112474344 The n-gram analysis involves a simple word frequency count (after removing stop words and domain words) to reveal the top words and word-pairs (bigrams) in the corpus. Bigrams are insightful because they capture more information than unigrams. - # Redoing text processing with adjectives, verbs and adverbs included + ### Redoing text processing with adjectives, verbs and adverbs included def lemmatization(docs, allowed_postags=["NOUN", "PROPN", "VERB", "ADJ", "ADV"]): nlp = spacy.load("en_core_web_sm") docs_out = [] @@ -396,11 +427,11 @@ The n-gram analysis involves a simple word frequency count (after removing stop data_lemmatized = lemmatization(data_with_bigrams, allowed_postags=["NOUN", "PROPN", "VERB", "ADJ", "ADV"]) - # Removing any stopwords created because of lemmatization + ### Removing any stopwords created because of lemmatization data_cleaned = [[word for word in doc if word not in stop_words] for doc in data_lemmatized] - # Plot frequency distribution of most common words in the headlines + ### Plot frequency distribution of most common words in the headlines all_sents = [" ".join(doc) for doc in data_cleaned] tokenizer = regextoken("[a-zA-Z0-9]+") all_words = tokenizer.tokenize(str(all_sents)) @@ -408,10 +439,10 @@ The n-gram analysis involves a simple word frequency count (after removing stop for word in all_words: fd[word] += 1 - # Plotting the top 50 most frequent words - # plt.figure(figsize = (15, 10)) - # fd.plot(50) - # plt.show() + #### Plotting the top 50 most frequent words + #### plt.figure(figsize = (15, 10)) + #### fd.plot(50) + #### plt.show() fd_sorted={k: v for k, v in sorted(fd.items(), key=lambda item: item[1], reverse=True)} fd_sorted=dict(list(fd_sorted.items())[0: 100]) diff --git a/src/blog/2020-05-25-analysing-the-katna-library-for-video-key-frame-extraction.mdx b/src/blog/2020-05-25-analysing-the-katna-library-for-video-key-frame-extraction.mdx index 489427f1..e35f8f8d 100644 --- a/src/blog/2020-05-25-analysing-the-katna-library-for-video-key-frame-extraction.mdx +++ b/src/blog/2020-05-25-analysing-the-katna-library-for-video-key-frame-extraction.mdx @@ -25,25 +25,23 @@ In this notebook we are trying to analyse how the library works for the purpose import matplotlib.pyplot as plt import os - # We will be analysing each video by generating 15, 10 and 5 keyframes each + ### We will be analysing each video by generating 15, 10 and 5 keyframes each frame_counts = [15,10,5] - LINE_SEPARATOR = "__________________________________________________" - # Creating a dataframe to store video duration, size and time taken for extracting 15, 10 and 5 frames from each video + LINE_SEPARATOR = "____________________________" + + ### Creating a dataframe to store video duration, size and time taken for extracting 15, 10 and 5 frames from each video df = pd.DataFrame(columns=("Duration", "Size", "Time for 15 frames", "Time for 10 frames", "Time for 5 frames")) - # Function to create a new folder to store extracted key frames + ### Function to create a new folder to store extracted key frames def create_folder_for_video (path): os.makedirs(path) - # Function to extract key frames from given video + ### Function to extract key frames from given video - def get_key_frames_from_video (path_to_video, no_of_frames): - video = Video() - images = video.extract_frames_as_images(no_of_frames = no_of_frames, file_path= path_to_video) - return images +
 ```python def get_key_frames_from_video(path_to_video, no_of_frames): video = Video() images = video.extract_frames_as_images(no_of_frames=no_of_frames, file_path=path_to_video) return images ``` 
- # Function to write extracted key frames into disc + ### Function to write extracted key frames into disc def write_images_into_folder (images, no_of_frames, path): video = Video() @@ -55,7 +53,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration: 26 seconds - Size: 59,88,173 bytes (7 MB on disk) - # Extracting keyframes for video 1 and appending data to the dataframe + ### Extracting keyframes for video 1 and appending data to the dataframe ``` data = {'Duration':26, 'Size':7} @@ -90,7 +88,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration: 57 seconds - Size: 30,95,424 bytes (3.1 MB on disk) - # Extracting keyframes for video 2 and appending data to the dataframe + ### Extracting keyframes for video 2 and appending data to the dataframe ``` data = {'Duration':57, 'Size': 3.1} @@ -125,7 +123,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration - 2min - Size - 57,92,806 bytes (5.8 MB on disk) - # Extracting keyframes for video 3 and appending data to the dataframe + ### Extracting keyframes for video 3 and appending data to the dataframe ``` data = {'Duration':120, 'Size': 5.8} @@ -160,7 +158,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration 4 min 51 sec - Size - 1,61,58,835 bytes (16.8 MB on disk) - # Extracting keyframes for video 5 and appending data to the dataframe + ### Extracting keyframes for video 5 and appending data to the dataframe ``` data = {'Duration':291, 'Size': 16.8} @@ -197,7 +195,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration - 8min 14sec - Size - 4,53,46,631 bytes (46.4 MB on disk) - # Extracting keyframes for video 6 and appending data to the dataframe + ### Extracting keyframes for video 6 and appending data to the dataframe ``` data = {'Duration':494, 'Size': 46.4} @@ -233,7 +231,7 @@ In this notebook we are trying to analyse how the library works for the purpose - Duration - 10min 50sec - Size - 5,15,84,587 bytes (52.4 MB on disk) - # Extracting keyframes for video 7 and appending data to the dataframe + ### Extracting keyframes for video 7 and appending data to the dataframe ``` data = {'Duration':650, 'Size': 52.4} @@ -293,7 +291,7 @@ In this notebook we are trying to analyse how the library works for the purpose plt.show() ![](../images/output_26_0.png) - # plotting size vs time + ## plotting size vs time plt.plot(df["Size"],df["Time for 15 frames"]) plt.plot(df["Size"],df["Time for 10 frames"]) diff --git a/src/blog/2020-07-08-fake-news-amplification.mdx b/src/blog/2020-07-08-fake-news-amplification.mdx index fdb9fb1c..0a4fad03 100644 --- a/src/blog/2020-07-08-fake-news-amplification.mdx +++ b/src/blog/2020-07-08-fake-news-amplification.mdx @@ -5,7 +5,7 @@ tags: online-harms author: Zumi Udaipuri --- -## जब से प्रिंटिंग, तब से फ़ेक न्यूज़ +### जब से प्रिंटिंग, तब से फ़ेक न्यूज़ माना जाता है कि फ़ेक न्यूज़ का इतिहास, कम से कम प्रिंटिंग प्रेस के इतिहास जितना पुराना तो है ही। जब साल 1439 में, गूटेन्बर्ग ने प्रिंटिंग प्रेस का आविष्कार किया था तभी से फ़ेक न्यूज़ का प्रचार और प्रसार शुरू हो गया था। 16वीं और 17वीं सदी के दौरान प्रकाशक ऐसे पैम्फ़्लेट्स और ख़बरों के पर्चे छापा करते थे जिनमें विशालकाय दैत्यों और अप्रत्याशित घटनाओं का विस्तार से उल्लेख हुआ करता था। कैटेलोनिया(स्पेन का एक हिस्सा) में 1654 में एक रिपोर्ट प्रकाशित हुई थी जिसके अनुसार एक ऐसा राक्षस मिला है जिसके बकरी जैसे पैर हैं, इंसान जैसा शरीर है, सात हाथ हैं और साथ सिर है। @@ -13,38 +13,58 @@ author: Zumi Udaipuri ये बातें झूठ थी, लेकिन जब प्रकाशकों से इसके बारें में पूछा गया तो उन्होंने ये कह कर इस मसले से अपने हाथ धो लिए कि उनका काम केवल एक ऐसा मंच देने का था जिससे सूचना के प्रचार और प्रसार में सुविधा हो। सूचनाओं की वैधता या वास्तविकता के पैमानों को तय करना  उनका काम नहीं था। -## प्रिटिंग प्रेस के आने से पहले आई फ़ेक न्यूज़ +### प्रिटिंग प्रेस के आने से पहले आई फ़ेक न्यूज़ +
हालाँकि प्रेस के आगमन से पहले भी इतिहास और हमारी कहानियों में फ़ेक न्यूज़ के कई क़िस्से दर्ज हैं। शायद फ़र्क़ सिर्फ़ ये है कि तब उन्हें फ़ेक न्यूज़ नहीं कहते थे। इसापूर्व पहली सदी के दौरान, रोमन सेना के लीडर अगस्तस ने अपने प्रतिद्वंदी मार्क ऐंटॉनी के ख़िलाफ़ अफ़वाहों और दुष्प्रचार का एक अभियान चलाया था। इस अभियान के चलते, मार्क ऐंटॉनी पर तरह तरह के इल्ज़ाम लगाए गए थे। उन्हें शराबी, व्याभिचारी और रानी क्लीओपेट्रा के हाथों की कठपुतली भी कहा गया। भारतीय पुराणों में भी अफ़वाहों का ज़िक्र होता है। महाभारतकेयुद्धकेसमयकुरुक्षेत्रकीरणभूमिपर*, *कृष्णनेगुरुद्रोणकोपरास्तकरनेकेलिएपांडवोंकोसुझावदियाकिहमेंद्रोणाचार्यऔरकौरवोंकेखेमेकामनोबलतोड़नाहोगा।तबकृष्णनेयुधिष्ठिरसेकहलवाया*, “*अश्वत्थामाहतोनरोवाकुंजरो*” *अर्थातपतानहींकिअश्वत्थामानामकहाथीयामनुष्यकौनमारागयाहै।गुरु द्रोणाचार्य के पुत्र का नाम ‘अश्वत्थामा‘ था। इस संदेश के फैलते ही गुरु द्रोणाचार्य का मनोबल टूट गया और वह युद्ध भूमि में कमज़ोर पड़ गए।  अन्यथा महाभारत के युद्ध में गुरु द्रोण की मृत्यु लगभग असम्भव थी। उनकी मृत्यु को आसान बनाने के लिए कृष्ण ने यह छल किया था।शायदआजकेसंदर्भमेंकोईफ़ैक्टचेकिंगसंस्थान*, *इसबातको फ़ेकन्यूज़भीकहसकतीहै। साल* 1475 *में*, *इटलीकेट्रेंटशहरमें*, *सिमोनियोनामकाएकढाईसालकाबच्चालापताहोजाताहै।एकफ़्रांसीसीउपदेशक*, *बर्नार्डिनोदावाकरतेहैंकीयहूदीसमुदायनेबच्चेकीहत्याकर*, *उसकाख़ूननिचोड़करऔरअपनेपासओवर* (Passover) *पर्वकेदौरानउसख़ूनकोपीलियाथा। इसके बाद उन्होंने इस बात का भी प्रचार किया कि बच्चे की लाश, एक यहूदी के घर के तहख़ाने में मिली थी। इन अफ़वाहों का नतीजा यह हुआ कि, आदेश जारी हुए की पूरे यहूदी समुदाय को बंदी बनाकर कड़ी से कड़ी सज़ा दी जाए। अंत में 15 यहूदियों को जला दिया जाता है। -![](../images/image-7.png)सिमोन ऑफ ट्रेंट की एक मूर्ति + +![](../images/image-7.png) + +सिमोन ऑफ ट्रेंट की एक मूर्ति आजभीसिमोनकोईसाईधर्मकीकईमान्यताओंऔरकहानियोंमेंसिमोनऑफट्रेंटकेतौरपरजानाजाताहै*, *उसकीमूर्तियांबनतीहैंऔरइसकोलेकरयहूदियोंकोनिशानाबनायाजाताहै।इतिहासकारों का मानना है कि नाज़ी जर्मनी में जो यहूदियों के ख़िलाफ़ जो माहौल था, उसकी जड़ें इन कहानियों में छुपी हुई हैं। यहां तक कि इस कहानी को नाज़ियों ने भी अपने दुष्प्रचार में इस्तेमाल किया। मतलबफ़ेकन्यूज़कोफैलानेवालेऔरइसकेपीड़ितोंकीमृत्युकेसदियोंबादभीफ़ेकन्यूज़काजीवनकालव्याप्तरहसकताहै। +
-## लेकिन अब तो पूरा फ़ेक न्यूज़ युग है.. +### लेकिन अब तो पूरा फ़ेक न्यूज़ युग है.. +
वर्तमान की फ़ेक न्यूज़ और अफ़वाहों ने एक अलग आयाम और रूप ले लिया है। ये अपने आप में एक इतनी बड़ी समस्या बन गयी है कि साल* 2016 *मेंऑक्सफ़ोर्डडिक्शनरीने* “Post-Truth” *कोअपनेशब्दकोशमेंशामिलकियाथा।ऑक्सफ़ोर्डडिक्शनरीकेमुताबिक़पोस्टट्रुथ* (Post-Truth) *काअर्थथाएकऐसावक़्तयायुग*, “*जिसकासम्बन्धऐसीपरिस्थितियोंसेहोजहाँलोक*-*मतयाजनताकीरायबनानेमेंतथ्योंऔरवास्तविकताकाबहुतकमयोगदानऔरप्रभावरहताहै।*”*लोगों की भावनाएँ और निजी विचार, जनता की सम्मति को प्रेरित करते हैं।“*Post-Truth” * का तात्पर्य ये नहीं है कि अब सच के कोई मायने नहीं हैं या कोई सच मानता नहीं है या कोई सच मानना नहीं चाहता या सच का कोई अस्तित्व नहीं है।ना तो हमें यह मानना चाहिए कि ये दौर सच के परे है और ना ही यह कि ये सच के बाद का दौर है। बल्कि, ये एक ऐसा दौर है जहाँ सच मुश्किल में है, जहाँ सच को ना देख पाने या सच के ना सामने आने की सम्भावना बहुत ज़्यादा है। मुमकिन है कि, यह एक ऐसा वक़्त हैं जहाँ वास्तविकता, बहुसांख्यिक सत्ता पक्ष  और  भावनात्मक विचारों के अधीन है। आख़िर क्यों है सच जोखिम में? क्या होती है फ़ेक न्यूज़ और क्यों ये फैल पाती है? क्यों हम लोग कुछ बातों को बिना सबूत के सच मान लेते हैं?क्यों हम हमेशा एक ही तरह की ख़बरें या एक ही प्रकार के प्रसंगों को सच मान लेते हैं? +
-## झूठ, आधा-सच या तथ्यों का विकल्प +### झूठ, आधा-सच या तथ्यों का विकल्प +
ठीकसाल* 1600 *ईसवींमेंरोमकेएकभीड़भरेबाज़ारमें*, *एकशख़्सकोउल्टालटकाकर* – *उसकोगालियांदीजारहीथी।भीड़खड़ेहोकरइसविधर्मीकेअंतकातमाशादेखनेकोउत्सुकथी।उसकीज़ुबान*, *कैथोलिकचर्च*, *ईसाऔरमहानवेटिकनकेख़िलाफ़बोलनेकेआरोपमेंसिलदीगईथी।कुछहीदेरमेंउसकेशरीरकोआगलगादीगई।आगकीलपटोंमेंगिरे*, *जलतेऔरअपनेनिर्ममअंतकीओरजाते* – *ब्रूनो* – *गिआर्डिनोब्रूनोकाकुसूरयेथाकिउसकेदावाकियाथाकिधरती*, *सूरजकेचारोंओरपरिक्रमाकरतीहै* – *जबकिकैथोलिकचर्चकामाननाइसकेठीकउलटथा। -![](../images/image-8.png)ब्रूनो को सज़ा + +![](../images/image-8.png) + +ब्रूनो को सज़ा प्रसिद्धवैज्ञानिकगैलीलियोनेजबयहीदावाकियाकि*, *पृथ्वीसूरजकेचक्करकाटतीहै*, *औरपृथ्वीनहींसूरजहमारेसौरमंडलकाकेंद्रहै*, *तबउनपरभीयहीइल्ज़ामलगायागयाथा।उनकीबातोंकोविधर्म* , *झूठऔरपाखंडकहागयाथा।फ़र्कबसयेथाकिउनकोज़िंदाछोड़दियागयाऔरता*-*उम्रघरमेंक़ैदकरदियागया। उनके द्वारा लिखी गयी चीज़ों के प्रकाशन पर रोक लगा दी गयी थी। उनकी बातें अंततः सच साबित हुई पर क्योंकि उनकी बातों ने समाज की प्रबल आवाज़ यानी कि चर्च को चुनौती दी थी – उनकी बातों को झूठ क़रार दे दिया गया। लेकिन एक और बात हुई, गैलीलियो की घटना के बाद ,तथ्यों और उनके सूत्रों पर एक बड़े स्तर पर विचार विमर्श होने लगा। तो क्या धर्म या प्रबल या प्रबुद्ध विचारों के ख़िलाफ़ बोलना, झूठ होता है? आख़िर सच, झूठ, आधा सच, पूरा झूठ होते क्या हैं? आख़िर सूत्रों, तथ्यों को कैसे समझा जाए? इन सवालों पर आने से पहले, वर्तमान समय की थोड़ी बात कर लेते हैं। -![](../images/image-9.png)गैलीलियो की सुनवाई + +![](../images/image-9.png) + +गैलीलियो की सुनवाई साल* 2015 *मेंतथ्योंकीजाँचपड़तालकेलिएएकअंतराष्ट्रीयसंगठनकीस्थापनाहुई।इससंगठनको* IFCN (International Fact Checking Network) *भीकहतेहैं।यहसंगठन* fact checking *संस्थानोंकोमान्यतादेताहैऔरउनकेलिएनियम*, *क़ानूनयामापदंडतयकरताहै।इस दौर में फ़ेक न्यूज़ या अफ़वाहों की जाँच करने वाले कई संस्थानो का गठन हुआ है। भारत में भी लगभग 14 ऐसी कम्पनियाँ हैं जो फ़ेक न्यूज़ से लड़ने का काम कर रही हैं। ऑल्ट न्यूज़, बूम, विश्वास न्यूज़ उनमें से प्रमुख संस्थाएँ हैं। इन सबके बावजूद आज फ़ेक न्यूज़ एक बहुत बड़ी समस्या है। क्यों झूठ पकड़े जाने की सम्भावना के बावजूद नेता, देशों के प्रमुख लगातार झूठ या तथ्यों को तोड़ मरोड़ कर पेश करते रहते हैं? उदाहरण के तौर पर जब राष्ट्रपति ट्रम्प का चुनाव हुआ था तो उन्होंने कहा कि, राष्ट्रपति रीगन के बाद ये सबसे बड़ी चुनावी जीत थी। जबकि ऐसा नहीं था। उन्होंने कहा कि, उनके अभिषेक के उत्सव में इतिहास की सबसे बड़ी भीड़ इकट्ठी हुई थी। जबकि तस्वीरों में ऐसा कुछ नज़र नहीं आता है। प्रधानमंत्री मोदी ने अपनी चुनाव रेलियों में दावा किया की DBT (Direct Benefit Transfer) की योजना उनकी सरकार ने शुरू करी थी किंतु इस योजना की शुरुआत तो साल 2013 में ही हो गयी थी। इन सबको हम झूठ की कैटेगरी में डाल सकते हैं। पर आधे सच तो और भी दिलचस्प होते हैं जैसे कि ट्रम्प ने कहा कि उनके CIA के भाषण के दौरान, सब लोगों ने खड़े होकर उनको तालियाँ और सराहना दी, किंतु सच तो ये था की उस मीटिंग के दौरान ट्रम्प ने CIA के स्टाफ़ को बैठने की अनुमति ही नहीं दी थी। पर फिर क्यों ये नेता इतने मशहूर हैं – ट्रम्प के समर्थकों को जब ट्रम्प के झूठ बताए जाते हैं तो वो ये कहकर पल्ला झाड़ लेते हैं की ट्रम्प की बातों को गम्भीरता से लेना चाहिए ना की वस्तुतः तौर से।मतलब उनके हर शब्द पर नहीं बल्कि उनकी समस्त बात पर ध्यान देना चाहिए। +
-## (अ)विश्वास से (अंध)विश्वास तक +### (अ)विश्वास से (अंध)विश्वास तक +
आख़िरसबूतों*, *तथ्यों*, Fact Check *केबावजूदझूठयाआधेपौनेसचोंपरक्योंभरोसाकियाजाताहै*? *[शोधकर्ताओं](https://pdfs.semanticscholar.org/9a0d/42b018cc283579b39ba133bbd1a4a87825e6.pdf)केअनुसारइसकेमुख्यतौरपरचारकारणहोतेहैं* – *उत्तेजितयाप्रेरिततर्कशक्ति* (Motivated Reasoning), *पुष्टीकरणकापूर्वाग्रह* (Confirmation Bias),*आसानीसेमिलनेवालीबातेंऔरअनुमान* (Available Heuristics) *औरमुख्यधाराकीबातें* (Salience)*। उत्तेजितयाप्रेरिततर्कशक्ति की वजह से जब हमें हमारी धाराणाओं  के विपरीत या उनसे अलग कोई तर्क या डेटा दिया जाता है तो हमको मानसिक असहजता का अनुभव होता है। उस नए डेटा को मानने से ज़्यादा हमें उसको ख़ारिज करने में आसानी होती है। जैसे कि मान लीजिए हमको बार बार कहा जाता है कि हिन्दू बहुत लिबरल और सहनशील है क्योंकि हमारे देश की मजॉरिटी ने मुस्लिम राष्ट्रपति, मुस्लिम अभिनेताओं, मुस्लिम लोगों को अपनाया है।चलिएमानलेतेहैंयेसचहै*, *तोफिरतोजबमुग़लशासकोंनेराजपूतमहिलाओंसेशादीकरीयाआमेरकेकच्छवाहाराजपूतोंकोअपनीसेनामेंसबसेबड़ेपददिए* – *इसहिसाबसेतोवोभीबहुतसहनशीलहुए*? *दूसराहमेंलगातारबतायाजाताहैकीमुग़लोंनेभारतकोलूटा*-*पर* 1600 *और*  1700 *सदीमेंजबभारतमेंमुग़लोंकाशासनथातबतोभारतकी* GDP , *विश्वमेंक्रमानुसारपहलेऔरदूसरेपायदानपरथी।तोफिरलूटकरउन्होंनेशायदनिवेशवापिसभारतमेंहीकरदियाहोगा*? *इस डेटा के बावजूद हम अपने विचार नहीं बदल पाते क्योंकि उस प्रक्रिया में हमें अपने पुराने विचारों और भावनाओं से दूर होना होगा, जो की बहुत कष्टदायी अनुभव होता है और जिसमें बहुत मेहनत लगती है। पुष्टीकरणकापूर्वाग्रह की वजह से हम वो ही किताबें, ख़बरें, कहानियाँ ढूँढते हैं जो हमारी वर्तमान धाराणाओं से सहमति रखते हों।ना सिर्फ़ सूचना खोजने में हम अपने पूर्वाग्रहों पर निर्भर होते हैं, पर जो सूचना हमारे सामने आती है उसको हम अपने हिसाब से समझ भी लेते हैं। जैसे कि नाज़ीजर्मनीमेंयहूदियोंकेख़िलाफ़रेडियोपरलगातारदुष्प्रचारकियाजाताथा*, *परउसकादुष्प्रभावसिर्फ़उनइलाक़ोंमेंदेखनेकोमिलाजोपहलेसेहीयहूदियोंकेख़िलाफ़थे। -![](../images/image-10.png)नाज़ी जर्मनी में यहूदियों के ख़िलाफ़ कैम्पेन + +![](../images/image-10.png) + +नाज़ी जर्मनी में यहूदियों के ख़िलाफ़ कैम्पेन **मुख्यधारा की बातें(salience**) – वैज्ञानिकोंकेअनुसारहमारेपाससीमितमानसिकक्षमताएँऔरयादाश्तहोतीहै।तोहमारेसामनेअगरदसतरहकीबातेंहैं*, *हमसिर्फ़कुछबातोंपरध्यानदेतेहैं।इसलिएनेताकभीकभारउन्हीमुद्दोंकेबारेंमेंबातकरतेहैंजिनसेलोगउत्तेजितहोजाएँ।फिरफ़र्क़नहींपड़ताकीउनमुद्दोंपरकिसीनेतानेसचकहाकीझूठ। क्योंकि मुद्दा ज़रूरी हो जाता है, मुद्दे पर क्या कहा और किया गया वो नहीं। जैसे नोटबंदी के दौरान काला धन का मुद्दा महत्वपूर्ण हो गया, पर काला धन आया की नहीं उस पर बहुत कम लोगों ने ध्यान दिया। साल 2019 में जब 4 मुख्याधाराओं के टीवी चैनलों की [202](https://www.nationalheraldindia.com/india/14-debates-on-ram-mandir-66-attacking-opposition-but-none-on-the-economy-four-hindi-news-channels) डिबेट्स का एक विश्लेषण किया गया तब पता चला की *79 *डिबेट्सपाकिस्तानकेऊपरथी*,  66 *विपक्षपरप्रहारकरतेहुएथी*,  14 *डिबेट्सराममंदिरकेमुद्देपरथीऔर* 36 *डिबेट्स* RSS/*मोदीजीकीप्रशंसाकरतेहुएथी।मतलब इससे कोई फ़र्क़ नहीं पड़ता की ये मुद्दे ज़रूरी हैं की नहीं, लेकिन क्योंकि सिर्फ़ इनही मुद्दों पर बात हो रही है तो ये मुद्दे अपने आप ज़रूरी लगने लग जाते हैं। **आसानी से मिलने वाली बातें और अनुमान (*available heuristics) ***– सोशल मीडिया के अलगोरीथमस हमें लगातार उसी तरह की बातें दिखाते हैं जिनसे हम सहमत हो। तो हमें अपनी बातों को सही सिद्ध करने के लिए आसानी से वही तर्क मिल जाते हैं जिनसे हम पहले से ही सहमत हो। +
-## फ़ेक न्यूज़ का दोषी कौन? +### फ़ेक न्यूज़ का दोषी कौन? तो ना केवल फ़ेक न्यूज़ फैलाने वाले लोग इस झूठ के कारोबार के ज़िम्मेदार हैं, लेकिन जिस तरह हम चीज़ें ढूँढते हैं, याद करते हैं, भूल जाते हैं, पढ़ते हैं, सुनते हैं – हम फ़ेक न्यूज़ से सिर्फ़ पीड़ित ही नहीं उसके दोषी भी है। बचपन में एक खेल खेलते थे चायनीज़ विस्पर जिसमें एक आदमी दूसरे के कान में कुछ फुसफुसा देता था, ऐसे करते करते जब वो बात फिर से पहले आदमी तक आती थी, तो वो कुछ और ही स्वरूप ले लेती थी।हो सकता है आपने ग़लत सुना हो, हो सकता है आपने ग़लत समझा हो, हो सकता है आपने आधा सुना हो, हो सकता है आप सुनकर भूल गए हो-फ़ेक न्यूज़ भी कुछ इसी तरह फैलती है।दूसरे के ग़लत होने के साथ साथ आपके ग़लत होने की भी पूरी सम्भावना है। diff --git a/src/blog/2020-08-03-a-brief-intro-to-gpt-3.mdx b/src/blog/2020-08-03-a-brief-intro-to-gpt-3.mdx index 25bc6d1d..c53cf7f1 100644 --- a/src/blog/2020-08-03-a-brief-intro-to-gpt-3.mdx +++ b/src/blog/2020-08-03-a-brief-intro-to-gpt-3.mdx @@ -7,12 +7,19 @@ tags: machine-learning --- ![](../images/Screen-Shot-2020-08-02-at-4.37.37-PM-3.png) + On July 11, 2020 [Open AI released its GPT-3 API](https://openai.com/blog/openai-api/) to a few individuals.What developers were able to build with the API with fairly few lines of code dazzled them. + ![](../images/dazzled-2.jpg) + Developers tweeted about it. Other technologists (who didn’t have access to the API) were also dazzled by it. The chorus got bigger and the technology came to broader attention. + ![](../images/shockedCrowdMemes.gif) + But the technology is hardly new! + ![](../images/elrond-gif.gif) + Which raises the important question: ### What then is different? @@ -21,45 +28,61 @@ GPT-3 is a language model that is trained on a LOT more data. GPT-2, the predecessor to GPT-3, is trained on [40 GB of Internet Text](https://openai.com/blog/better-language-models/). GPT-3 is trained on [45 TB](https://arxiv.org/abs/2005.14165) of Internet text. To give a sense of scale- if the black dot is what GPT-2 trained on, the orange sphere is what GPT-3 trained on (actually slightly more). -![](../images/Screen-Shot-2020-08-02-at-9.50.51-PM-1.png)Visualization by [RedCentric](https://www.redcentricplc.com/resources/byte-size-infographic/) -### -Did We Also Say Something about a Language Model? + +![](../images/Screen-Shot-2020-08-02-at-9.50.51-PM-1.png) + +Visualization by [RedCentric](https://www.redcentricplc.com/resources/byte-size-infographic/) + +### Did We Also Say Something about a Language Model? + ![](../images/confused_2.png) + Language modeling is the task of assigning a probability to sentences in a language. There is some technical complexity to how language models are implemented but we can leave that out for this introduction. + ![](../images/what-if-i-told-you-its-just-a-black-box-2.jpg) + In effect this is what happens: You throw a lot of text at the model, and from that text the model infers how probable a word or sequence of words are given a sequence of words preceding it. ![](https://lh6.googleusercontent.com/5Lxw6i74ee6i1NO9u1FXO2g8tGJFJKSP4xYOh-Yer64gLGTAy8r3W2PoDCGiAyA5YqKY5A8GWL85TlbC6MbWlxp66l195N6ugeki7IidYA33kOR09FKxdH-ztBxNwx1EXQpTnLQQ) So this should explain all [those essays](https://maraoz.com/2020/07/18/openai-gpt3/) and [poems ](https://www.gwern.net/GPT-3#poetry)generated by GPT-3. Users give GPT-3 a prompt text. Based on this input, GPT-3 throws out text that it predicts as the most probable sequence of words for that prompt. You can set the [response length on GPT-3](https://towardsdatascience.com/gpt-3-creative-potential-of-nlp-d5ccae16c1ab). So the model can complete a sentence, or write a long essay. -![](../images/GPT-3-essay.png)[Source](https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.reddit.com%2Fr%2Fmachinelearningmemes%2Fcomments%2Fh8oud6%2Fgpt_to_write_my_essays%2F&psig=AOvVaw3NGniwH1dC6Gd1coEnPb-h&ust=1596529736959000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCPjD9PHO_uoCFQAAAAAdAAAAABAD) + +![](../images/GPT-3-essay.png) + +![Source](https://www.google.com/url?sa=i&url=https%3A%2F%2Fwww.reddit.com%2Fr%2Fmachinelearningmemes%2Fcomments%2Fh8oud6%2Fgpt_to_write_my_essays%2F&psig=AOvVaw3NGniwH1dC6Gd1coEnPb-h&ust=1596529736959000&source=images&cd=vfe&ved=0CAIQjRxqFwoTCPjD9PHO_uoCFQAAAAAdAAAAABAD) + ### What is This Whole Talk of Priming? GPT-3 is trained on public conversations on the world wide web. People have all sorts of conversations online- we write about wide ranging issues, we debate, discuss product reviews, post questions on fora like Stack Overflow and Quora. The range of conversation online mirrors the range of our daily conversations. A sentence or phrase only gets meaning from its context. For example, the word 'Date' would be used very differently in a group of botanists (as a fruit), in a Microsoft Excel debugging forum and in a relationship advice forum. + ![](../images/contextMatters.jpg) + GPT-3 adopts "[in-context learning”](https://www.kdnuggets.com/2020/06/gpt-3-deep-learning-nlp.html). The developers feed the model a prompt or a few demonstrations of the task. This narrows down the 'subspace' from which the model predicts its outputs.  This ensures that the outputs are tweaked to be relevant to the context (of the prompt). ### But What Is All This Talk of Generating Code??? Take [this use case](https://twitter.com/sharifshameem/status/1282676454690451457?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1282676454690451457%7Ctwgr%5E&ref_url=https%3A%2F%2Fanalyticsindiamag.com%2Fopen-ai-gpt-3-code-generator-app-building%2F) where the person was able to generate elements used on a website in plain English. An extreme extension of this application would be a person describing the kind of website they want in English and an application built on GPT-3 generating the website for you. You could have a customized website without needing to code. + ![](../images/your-wish-is-my-command-1.jpg) + To implement this feature, the developer primed the model with two example, where they provided English statements and a corresponding code equivalent to it. One thing to remember is that there are a lot of code snippets online (on forum such as Stack Overflow, coding blog posts, and Github repositories). So GPT-3 possibly had a lot of coding example to learn from. Once the model was 'primed' on code snippets, it responded to input English sentences with coding statements as output. ![](../images/adventure-time-mind-blown-GIF-downsized_large.gif) + ### So is GPT-3 Going to Change The World As We Know It? At this stage it is hard to predict all the ways in which GPT-3 and its offsprings will be used. Like all machine learning models, it fails in unpredictable ways. It is also displaying known issues of [bias in ML](https://twitter.com/an_open_mind/status/1284487376312709120). We will definitely see a lot more generated text from GPT-3, which doesn't bode well for people grading student essays or for people tracing misinformation. But as for other creative use cases,  "it is too early to say" if they will meet consumer product standards. -### Thats All For Now. +### Thats All For Now. + ![](../images/image.png) This blog doesn't have a comments section, so if you'd like to discuss more find us [on Twitter.](https://twitter.com/tattlemade)[Or email](mailto:admin@tattle.co.in). Or [Slack](https://admin417477.typeform.com/to/nVuNyG). -### -Attribution +### Attribution All the images and GIFs, except for the first one, were found on Google Images. diff --git a/src/blog/2020-08-08-deepfakes-cheap-fakes-and-misinformation-in-india.mdx b/src/blog/2020-08-08-deepfakes-cheap-fakes-and-misinformation-in-india.mdx index 9d11d9a0..688593ea 100644 --- a/src/blog/2020-08-08-deepfakes-cheap-fakes-and-misinformation-in-india.mdx +++ b/src/blog/2020-08-08-deepfakes-cheap-fakes-and-misinformation-in-india.mdx @@ -3,7 +3,7 @@ name: Deepfakes, Cheap Fakes and Misinformation in India slug: deepfakes-cheap-fakes-and-misinformation-in-india date: 2020-08-07 tags: machine-learning, synthetic-media -author: Tarunima +author: Tarunima Prabhakar --- Tattle's archive service captured a 19 second video clip from a chat app. The video showed 9 notable figures singing a song. These are the last 5 seconds of the video: @@ -14,7 +14,9 @@ Tattle's archive service captured a 19 second video clip from a chat app. The vi > — tattle (@tattlemade) [August 7, 2020](https://twitter.com/tattlemade/status/1291744030586355712?ref_src=twsrc%5Etfw) On the [spectrum of 'Cheap Fakes' to Deepfakes](https://t.co/vnL3RwdoCL?amp=1), proposed by Joan Donovan and Britt Paris, this falls somewhere in between. It is hard to tell exactly what digital manipulation techniques were used to create this effect: + ![](../images/Ee0zo9MUYAAySjq.png) + The video is not misinformation. But it shows that techniques for convincing digital manipulation, that can be used to generate misinformation, are easily accessible. The figures' mouth movements as well as head movements change with intonation of words of the song. Any technology for audio-visual manipulation will find many applications. It will be used for everyday entertainment as in social media apps or in advertisement. It might even herald a new form of film making. Simultaneously, these sophisticated digital manipulation techniques will have serious implications for misinformation. diff --git a/src/blog/2020-09-11-the-use-of-archives-in-misinformation.mdx b/src/blog/2020-09-11-the-use-of-archives-in-misinformation.mdx index 451f86b4..261f49e9 100644 --- a/src/blog/2020-09-11-the-use-of-archives-in-misinformation.mdx +++ b/src/blog/2020-09-11-the-use-of-archives-in-misinformation.mdx @@ -3,7 +3,7 @@ name: The Use of Archives in Misinformation excerpt: Content moderation on social media has been in public scrutiny recently. Takedowns often have unintended consequences which archival projects like Tattle can address. But archival projects are also susceptible to the kind of abuse that necessitates takedowns. date: 2020-09-11 tags: archives, web-scraping, online-harms -author: Tarunima +author: Tarunima Prabhakar --- Content moderation on social media has been in public scrutiny recently. Takedowns often have unintended consequences which archival projects like Tattle can address. But archival projects are also susceptible to the kind of abuse that necessitates takedowns. diff --git a/src/blog/2020-09-18-anek-aayaam-vibhin-paigaam-ek-vaairl-photto-kii-khaanii.mdx b/src/blog/2020-09-18-anek-aayaam-vibhin-paigaam-ek-vaairl-photto-kii-khaanii.mdx index 99aca851..86886859 100644 --- a/src/blog/2020-09-18-anek-aayaam-vibhin-paigaam-ek-vaairl-photto-kii-khaanii.mdx +++ b/src/blog/2020-09-18-anek-aayaam-vibhin-paigaam-ek-vaairl-photto-kii-khaanii.mdx @@ -9,7 +9,9 @@ author: Zumi Udaipuri एक बार ऑनलाइन शेयर होने के बाद, बिना किसी संदर्भ/स्रोत वाला कंटेंट/तस्वीरें/विडीओज़ अपना जीवन ख़ुद ही ढूँढ लेते हैं। धीरे-धीरे,बिना ध्यान में आए,थोड़ा-थोड़ा कर ऐसी चीज़ें कब फ़ेक न्यूज़ बन जाती है पता नहीं चलता।इसका बड़ा कारण यह है कि किसी भी कंटेंट को अपने मूल प्रसंग से दूर बिलकुल भिन्न या ग़लत संदर्भ में भी शेयर किया जा सकता है। उदाहरण के लिए, पिछले हफ़्ते भारतीय चैटिंग प्लेटफ़ॉर्म पर वाइरल हुआ यह पोस्ट  ~600 बार शेयर हुआ और  ~2400 बार लाइक किया गया। इसको साझा करते वक़्त यह भी लिखा गया कि  “कोरोना बहुत छोटी सजा है, अरे इंसान अपने गुनाह देख और सोच तेरी सजा क्या है!” + ![](../images/image_2.jpg) + परंतु गूगल की एक बुनियादी सर्च करे तो इस फोटो का एक उल्लेख नाइजीरिया की Rapid Gist साइट पर मार्च 2020 में भी दिखाई देता है, जिसके मुताबिक़ 15 मार्च को यह बच्चा नाइजीरिया के एक शहर में लावारिस पाया गया था।लेकिन इससे पहले भी भारत में यह पोस्ट लगातार अलग-अलग संदेशों के साथ भी साझा किया गया है। जैसे की इसे फ़ेस्बुक पर ऐसे यूज़र्ज़ ने शेयर किया जिनके लाखों फ़ालोअर्ज़ थे। जहाँ एक चैटिंग प्लैट्फ़ॉर्म पर इस पर अधिकांश टिप्पणियाँ मराठी में करी गयी थी वहीं फ़ेस्बुक पर इसे पंजाबी संदेश के साथ साझा किया गया जिसमें लिखा था "भगवान से डरो,ईश्वर के तोहफ़ों को इस तरह क्यों फेंक रहे हो।पैदा ही क्यों करते हो अगर फेंकना है तो"। diff --git a/src/blog/2020-09-24-the-role-of-affect-in-misinformation-circulation.mdx b/src/blog/2020-09-24-the-role-of-affect-in-misinformation-circulation.mdx index 9f1239fa..2b2b76d7 100644 --- a/src/blog/2020-09-24-the-role-of-affect-in-misinformation-circulation.mdx +++ b/src/blog/2020-09-24-the-role-of-affect-in-misinformation-circulation.mdx @@ -4,7 +4,7 @@ excerpt: A lot has been said about the intention of misinformation peddlers, the slug: the-role-of-affect-in-misinformation-circulation date: 2020-09-24 tags: online-harms -author: Tarunima +author: Tarunima Prabhakar --- Early in the pandemic, a claim about a 'breath test' for self-diagnosis of Covid-19 was circulating on social media. Such messages were shared in multiple countries and were debunked by [fact checking groups](https://bit.ly/3i0SUXW). diff --git a/src/blog/2020-10-09-the-unexplained-motivation-behind-whatsapp.mdx b/src/blog/2020-10-09-the-unexplained-motivation-behind-whatsapp.mdx index 0d0641de..cc894ec6 100644 --- a/src/blog/2020-10-09-the-unexplained-motivation-behind-whatsapp.mdx +++ b/src/blog/2020-10-09-the-unexplained-motivation-behind-whatsapp.mdx @@ -3,7 +3,7 @@ name: The Unexplained Motivation Behind WhatsApp Forwards excerpt: The documentary The Social Dilemma describes why content goes viral on social media but doesn't account for virality on WhatsApp date: 2020-10-09 tags: web-scraping, online-harms -author: Tarunima +author: Tarunima Prabhakar --- The documentary [TheSocialDilemma](https://twitter.com/hashtag/TheSocialDilemma?src=hashtag_click) emphasizes that financial incentives led 'fake news' and hate speech to proliferate on platforms such as Facebook and Twitter. But it falls short of explaining why the same are found on chat apps such as WhatsApp. @@ -11,7 +11,9 @@ The documentary [TheSocialDilemma](https://twitter.com/hashtag/TheSocialDilemma? WhatsApp doesn't have an algorithmically modified feed, and no selective amplification by the platform. Yet, fake news goes viral on the platform. In this social media post for example in which a person claims that they will receive Rs. 1 from WhatsApp every time this message is forwarded: + ![](../images/whatsApp_tweet.png) + The claim had its effect- it is clearly untrue. Yet the message was shared over 5000 times. It was easy to pin financial motivations on Macedonian teens publishing [fake news on Facebook](https://nbcnews.to/3ltg4Zj). But what motivates people to create such content for chat apps such as WhatsApp? Virality is not directly profitable. diff --git a/src/blog/2020-10-23-learning-from-the-user-usage-model.mdx b/src/blog/2020-10-23-learning-from-the-user-usage-model.mdx index 228a37cc..c3e0fcdf 100644 --- a/src/blog/2020-10-23-learning-from-the-user-usage-model.mdx +++ b/src/blog/2020-10-23-learning-from-the-user-usage-model.mdx @@ -25,7 +25,9 @@ We started looking at this literature ## User - Usage Model The User - Usage model is a 2 dimension model of a User’s Usage of an ICT artefact. + ![](../images/Screen-Shot-2020-10-23-at-3.12.37-PM.png) + The User dimension (Y-axis) categorises a user into archetypes based on factors like personal attributes such as attitudes, capabilities, culture etc. Some factors that the first paper identified were purchasing power, literacy, attitude towards ICT, proactivity, time availability, utility, age, aides, prevalence of ICT, trust on internet etc The Usage dimension (X-axis) captures the different stages of usage of an ICT artifact by a user. The paper identifies the stages of usage as the unexposed, the novice, the rote learner, the fluent user and the competent user. @@ -37,7 +39,9 @@ The blocks between each usage stage represents a barrier that prevents the user The researchers found that users who were on the same stage of Usage with an ICT artifact moved to the next stage in different time duration. The User dimension helps attribute the difference in progress of a user to their personal attributes. Which is what led the writers of the second paper to propose that since Users don’t get younger with time nor are they likely to become more educated, your design has to accommodate to help those users. It is our understanding that what is being proposed is that it is hard for people to move up the Y axis and the features you design should meet the user where they are to assist them in moving across the barriers. ## Relevance to our Pilot + ![](../images/user-usage-diagram.png) + 1. Given the uniformity of our Pilot test demographic wrt age, economical status etc, we must identify which attributes about them matter when it comes to their ability to cross Usage barriers. 2. It will be easy to disregard our demographic’s ability to cross usage barriers using traditional features of a QnA app. It will be a challenge for us to leverage the skills that our users already have to design features that help them become critical consumers of information. One hypothesis that comes to mind from this paper is that given our focus on the demographic of 40-60 year old, some factors like frequency of usage might not change over time(partly because of the nature of khoj's purpose), in that case what features should we support to enable users to progress in their usage of our service. 3. The age demographic in the whatsapp adoption study is 18-50 years, which is only partially suitable for our purpose. diff --git a/src/blog/2021-01-10-the-need-for-archiving-social-media-data-and-the-conundrums-of-opening-it.mdx b/src/blog/2021-01-10-the-need-for-archiving-social-media-data-and-the-conundrums-of-opening-it.mdx index 962a0f97..9d60aa8f 100644 --- a/src/blog/2021-01-10-the-need-for-archiving-social-media-data-and-the-conundrums-of-opening-it.mdx +++ b/src/blog/2021-01-10-the-need-for-archiving-social-media-data-and-the-conundrums-of-opening-it.mdx @@ -3,7 +3,7 @@ name: The Need for Archiving Social Media Data and The Conundrums in Opening It excerpt: Informed Interventions on misinformation require greater contextual story telling. Social media archives are an important resource in the process. date: 2021-01-10 tags: web-scraping, online-harms -author: Tarunima +author: Tarunima Prabhakar --- Within a decade, social media went from a tool of organizing and democratization to a tool for surveillance and control. Even as hypothesis are generated and theories are conceived about the dangers of personalized feeds, we encounter messaging platforms that continue to be vectors of misinformation in absence of personalization. diff --git a/src/blog/2021-08-01-thinking-about-feminist-approaches-to-technology.mdx b/src/blog/2021-08-01-thinking-about-feminist-approaches-to-technology.mdx index c7b6ff75..612f542e 100644 --- a/src/blog/2021-08-01-thinking-about-feminist-approaches-to-technology.mdx +++ b/src/blog/2021-08-01-thinking-about-feminist-approaches-to-technology.mdx @@ -7,7 +7,7 @@ date: 2021-08-01 tags: responsible-ai --- -** This is one of the earliest articulations of the project vision and approach, written in the July/August 2021 ** +**This is one of the earliest articulations of the project vision and approach, written in the July/August 2021** The graphic narrative titled ‘Personal (Cyber) Space’ published in 2016 by Parthasarthy and Malhotra narrates an experience of a young internet user. The animated short comic hosted by Kadak, a South Asian women collective, asks: ‘If one says something, there’s the fear of hateful response. But if one doesn’t say something, isn’t that silence counterproductive?’ only to end with the question, ‘so what does one say?’ diff --git a/src/blog/2021-12-15-making-of-uli-december-2021-newsletter.mdx b/src/blog/2021-12-15-making-of-uli-december-2021-newsletter.mdx index bdb7af7e..3a251475 100644 --- a/src/blog/2021-12-15-making-of-uli-december-2021-newsletter.mdx +++ b/src/blog/2021-12-15-making-of-uli-december-2021-newsletter.mdx @@ -7,28 +7,28 @@ date: 2021-12-15 tags: responsible-ai --- -## The making of the ML tool to mitigate online gender based violence in three Indian languages +**The making of the ML tool to mitigate online gender based violence in three Indian languages** -**This post is adapted from the Uli newsletter update sent in December 2021** +This post is adapted from the Uli newsletter update sent in December 2021 -## Work from June 2021 to Decemeber 2021 +### Work from June 2021 to Decemeber 2021 During the first six months, we reached out to activists, community influencers, members of community based organizations and individuals who have been at the receiving end of violence. We wanted to learn how oGBV is experienced by different users; what strategies users deploy to access social media and how this tool can help towards that effort. We had the privilege to speak to more than 50 individuals who have been either at the receiving end of violence or have been involved in the struggle to make social media more accessible. We were able to map different responses to online gender-based violence, harassment and hate speech that can feed into our strategies to deal with oGBV. We share with you some of these responses here. -## How do people react to oGBV? +### How do people react to oGBV? ![image](../images/qr_word_cloud.png) This is a word map built from our conversations. Fatigue resulting from hate speech was the most prominent affective response that our respondents noted. This was followed by humour as the most prominent strategy to reclaim the space of social media. We learnt of many other responses that come up when one is face to face with hate speech, harassment, and violence targeting gender and sexual minorities. Fatigue, as an affective response to posts that you can neither ignore nor engage with, gives us some directions on how our model should be designed and the kind of content we should be focussing our energies on. Simultaneously, predominance of humour, in the form of memes, counter speech, or posts extending support to one’s friends, warns us of the content that the tool shouldn’t be acting upon at all. Other responses such as seeking a support group, blocking, deleting etc. allows us to arrive at other features that will be as useful as the proposed ML model. -## Features that emerged during our conversations +### Features that emerged during our conversations To make sure that our model doesn’t turn into a killing machine, we asked our participants to share with us the most desirable features that will keep the decision making power in the hands of the users. Thanks to our respondents, we learnt the most exciting features for a tool. The list is available here: https://tinyurl.com/2p9bnefk While we might not be able to incorporate all of these design suggestions in our proposed ML oGBV Tool 1.0 (this is what we are calling it until we have a name), we want to share this as a list that can help others who might be interested in building community facing tools. -## The annotation process +### The annotation process To build the model on detection of abuse, the data collected was annotated by 6 ‘expert’ annotators who have been dealing with (online) violence as activists, journalists, community influencers or members of community-based organizations. The effort is part of an undercurrent within the ML community to ensure that automated content moderation models are built around a notion of harm that reflects the everyday realities of the people for whom the model is being built. To create guidelines for the annotators, we had long discussions internally over what oGBV is and we all agreed that it is notoriously hard to define. As was expected, the definitions were contested through the course of the project. For instance, all four of us agreed that we do not have a consensus on whether following posts should be tagged as oGBV or not: diff --git a/src/blog/2022-01-15-making-of-uli-january-2022-newsletter.mdx b/src/blog/2022-01-15-making-of-uli-january-2022-newsletter.mdx index 324dddd5..bf535e14 100644 --- a/src/blog/2022-01-15-making-of-uli-january-2022-newsletter.mdx +++ b/src/blog/2022-01-15-making-of-uli-january-2022-newsletter.mdx @@ -7,7 +7,7 @@ date: 2022-01-15 tags: responsible-ai --- -## The making of the ML tool to mitigate online gender based violence in three Indian languages +### The making of the ML tool to mitigate online gender based violence in three Indian languages This post is adapted from the Uli newsletter update sent in January 2022 diff --git a/src/blog/2022-04-16-responsible-way-of-showcasing-the-countering-of-ogbv.mdx b/src/blog/2022-04-16-responsible-way-of-showcasing-the-countering-of-ogbv.mdx index 4af15ea5..616e3ef3 100644 --- a/src/blog/2022-04-16-responsible-way-of-showcasing-the-countering-of-ogbv.mdx +++ b/src/blog/2022-04-16-responsible-way-of-showcasing-the-countering-of-ogbv.mdx @@ -19,7 +19,7 @@ Given the goals of Uli (accessibility, transparency of process), explaining the It is available in one other language, is contained in one webpage only and has no links to anywhere else. The content is put across in a survivor-centric manner and is empowering such as through messaging like “how you can help.” It takes a very personal experience and topic (political advertisements) and handles it very deftly while still being sensitive. - This [community-centric tool](https://iheartmob.org/) focuses on feelings of belonging and safety. It highlights user experiences and features of the tool as the main representations on the website. Another potentiality for Uli could be quotes from users of the plugin. -Overall Discussion points:: +### Overall Discussion points: - Focus on educating the Indian audience (most of whom might be unaware of disinformation and GBV very broadly, and come from nuanced and specific social milieu and contexts). - Blur out, anonymise, resituate/re-contextualise the hateful content, if at all it is being referred to. diff --git a/src/blog/2022-04-26-making-of-uli-march-2022-newsletter.mdx b/src/blog/2022-04-26-making-of-uli-march-2022-newsletter.mdx index 57423722..30c80880 100644 --- a/src/blog/2022-04-26-making-of-uli-march-2022-newsletter.mdx +++ b/src/blog/2022-04-26-making-of-uli-march-2022-newsletter.mdx @@ -42,7 +42,7 @@ These patches have been stitched by all of us to reclaim online courtyards. A Kantha takes months or even years to be finished, handed down through generations, from grandmother, to mother, to daughter. It is ever-expanding, just like the database of our tool. A completed Kantha provides strength, as this tool aims to do. In the spirit of co-weaving this tool, we need your perspectives and insights on what a good name and face for this tool could be. -## Converging on Uli +### Converging on Uli Uli (“Uli” உளி) was a name suggested by one of our teammates. diff --git a/src/blog/2022-07-15-privacy-preserving-features-of-uli.mdx b/src/blog/2022-07-15-privacy-preserving-features-of-uli.mdx index 4a52c6f9..f76a8ef5 100644 --- a/src/blog/2022-07-15-privacy-preserving-features-of-uli.mdx +++ b/src/blog/2022-07-15-privacy-preserving-features-of-uli.mdx @@ -9,12 +9,12 @@ tags: responsible-ai We believe we've built Uli thoughtfully in a way that users can get value out of it without giving any of their personal data to us. For all the features, whenever possible, we have built a version of the feature that allows it to be used locally. What we mean by ‘locally’ is that unless users opt in, the processing required for a feature, happens on the user’s machine and no data is sent to us. For features where data is sent to us, we’ve tried to minimize any storage of PII. PII like email when stored is stored encrypted. The server source code is open source and can be audited here. -## Slur Detection and Blurring +### Slur Detection and Blurring The code for detecting exact and approximate matches for the slurs present in our slur list runs completely on the client side. Exact matching of slurs use standard RegEx while for approximate matches we use Levenshtein distances[1]. When the plugin is running it has access to the text within the tweets on your screen but this data is not sent to our servers. There is an opt-in improvement to this feature which detects whether a tweet would qualify as an instance of Online Gender Based Violence. Currently this feature does not work locally[2] and requires data to be sent to our server where we use Machine Learning to classify the tweet. In this instance as well, we don’t store the tweet text[3]. -## Archive Tweets +### Archive Tweets Unless users opt in by providing an email id, this feature allows you to store screenshots of problematic tweets on your machine itself. In this case no data - tweet screenshot or tweet metadata is sent to our servers. @@ -22,13 +22,13 @@ Even when users opt in to remote archiving by providing their email ids, their p We’ve taken measures to ensure the tweets you archive are stored securely, protected from others. We would still advise that the tool be used with discretion . Uli is a tool meant to foster a community to fight online abuse, you must consider the feature as being intended to create a community repository of problematic content and not to be used for personal data archiving. At any point if you feel like you would like to remove some content you archived remotely you can reach out to us on uli-support@tattle.co.in. The plugin also provides a “Reset Account” option that will delete your account from our servers along with any data you might have archived allowing you to be “forgotten” from our system. -## Invoke Network +### Invoke Network This feature is meant to find support against problematic content directed towards you by reaching out to your network or community organizations. By its very nature this feature can’t run locally. This is intended as a fully opt-in feature however and the same privacy preserving actions mentioned in the section above apply here. Given the potential of abuse we have not included it in the current iteration. We are exploring strategies to minimize the room for abuse. If you find any flaws in our approach, have suggestions for improvements or spot any vulnerabilities, please disclose them at uli-support@tattle.co.in -## Footnotes : +### Footnotes : [1] This code was prototyped in python and then ported to run on web browsers using the pyodide project. Code can be studied [here](https://github.com/tattle-made/OGBV/commit/35ce9e15e230e7a2aa247b7fc448bd733a831a85). [2] There is currently an [open issue](https://github.com/tattle-made/OGBV/issues/141) on our github repository tracking progress on this. Feel free to participate there if you wish to get further clarification or have any input on how we could achieve this functionality locally. diff --git a/src/blog/2022-08-28-getting-started-with-aws-copilot.mdx b/src/blog/2022-08-28-getting-started-with-aws-copilot.mdx index 9130382a..8bed2b16 100644 --- a/src/blog/2022-08-28-getting-started-with-aws-copilot.mdx +++ b/src/blog/2022-08-28-getting-started-with-aws-copilot.mdx @@ -9,7 +9,7 @@ tags: responsible-ai Around a few days ago, I was introduced to, what is, from the time of writing a relatively new framework for hosting containers in the cloud, known as AWS co-pilot. This blog post serves as a collection of all that I've learnt and more regarding the usage of co-pilot, a potential way to deploy and host Uli, using copilot and relevant resources which were useful to me in learning about co-pilot and its various use cases. -## Configuring AWS: +### Configuring AWS: The first thing you need to do before running co-pilot is install the AWS command line interface locally as well as creating an account on the AWS console. The necessary guide required in order to install AWS and configure it can be found [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html). @@ -24,7 +24,7 @@ $ aws configure Running this command will allow you to edit four fields, two of which would have the access key and the secret access key respectively to determine which user is accessing resources when the commands are run, the third field refers to the region where you can enter "ap-south-1". This is the region code for Mumbai, which not only determines your VPC, but also the region covered by your internet facing services. In the last field, you may either leave it blank or just enter the file format most suited to you, by default it would be JSON. -## Getting started with Copilot: +### Getting started with Copilot: Once you are done with the configuration mentioned above, you can proceed to install AWS Copilot. The basic steps for which are given in the documentation: @@ -38,7 +38,7 @@ The first one was wishing to host a basic server on the web, for which I had to [](https://www.youtube.com/watch?v=drYy_V7X1BU) -## Service Discovery and deploying on the Web: +### Service Discovery and deploying on the Web: The next thing I wanted to try out is deploying two services in the same environment that can talk to each other. Turns out, co-pilot offers a much more elegant solution to it rather than the traditional way of them sending each other communication on the web. AWS has a feature known as service discovery, which gives a unique identity to each service allowing them to communicate within the VPC without ever having to expose themselves to the web. A basic explanation of this is given under the "Developing" section of the documentation bunched up with a lot of other services you may wish to use: @@ -56,7 +56,7 @@ The entire website is full of a bunch of tutorials, all of which explaining some The most useful thing however, is the website taking you through what is essentially the core loop of building environments and services which follows the commands "init - show - deploy - delete", usually in that order, where init initialized the application, show and other such commands show the key details about the service, the deploy command deploys it in a particular environment that you can specify using the "-e" flag and the delete command which cleans up the service and allocated resources. -## Running specific tasks and debugging: +### Running specific tasks and debugging: Next, I want to talk about a topic that I personally briefly touched upon in my journey through learning co-pilot is running specific commands in the running instance of a container, which can be done by the "exec" command which allows for further optimization where you can run specific tasks in a container only when the need arises for it. The flexibility is vast since it allows you to open a bash session as a part of the service. The reference to the same can be found here: @@ -70,7 +70,7 @@ $ copilot job logs The final component of an application is a pipeline, which, as the name suggests is a CI/CD framework that is completely optional and which deploys changes into select environments as soon as they are pushed in your repository, similar to Github actions by creating a manifest file for the pipeline itself. It is suggested in various forums to have one setup for large applications since the distribution of resources in AWS under the hood is often very complicated and it is better to run the "pipeline deploy" command atleast in order to deploy the changes in the test environment before they are pushed into the build. A more complicated workflow would be required however to achieve the same through purely github actions and this seems to be the most straightforward way to implement CI/CD. However, more verification on the previous statement is needed -## About VPCs, Storage and other services: +### About VPCs, Storage and other services: Next, I tackled VPCs and how deployment into specific ones work. How this is tackled via AWS is that a default VPC is assigned to your account whenever you create it along with a few VPCs for each region, all fulfilled with routing tables and such. This is when setting a default region while using the "aws configure" command comes into play, where just doing that configures your machine to deploy all of your applications and related services in the EC2 instances and the VPC assigned to you for that region. Thus by default, most environments that you initialize for your application are assigned subnets specific to the VPC of the region that you are working with, making all deployments in that environment the part of the same VPC. By rule of thumb, it is essential to ensure two services are in the same environment in order to ensure they are in the same VPC. More information that I found useful on VPCs and what happens under the hood can be found here : @@ -87,11 +87,11 @@ A service that I could not practically look into is the storage feature that is It talks about integrating with your application, among other things, a persistent file system that persists till the environment it is deployed in gets deleted, a [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html), an [S3 Bucket ](https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-buckets-s3.html)and so on in order to store and organize data related to the application. -## For Uli: +### For Uli: Finally, I wish to talk about a way to deploy Uli using co-pilot and what I see its advantages would be. Firstly, the major difference here is that we would be able to divide the API and the model into two different serviced within the application where the API can be a Load Balanced Web Service that listens for requests and passes them on to the model which would be a Backend service. This can be very useful since now we can not only add more and more services to which the API can redirect various queries to but we can also make changes to both of them without having to build either one, saving relevant time and resources. Another bit of optimization that is possible is allocating the resources to the ML server, which while already being optimized by AWS could be fine tuned by us using the **manifest.yml** file of the ML server service adding required scalability. Lastly, we can also use co-pilot in order to attempt a system where the ML model runs only when the API calls upon it in order to further cut down on costs, this is something I have not tested yet and will hopefully test and update on the blog in the foreseeable future. Thanks for reading! -## Cheat sheet: +### Cheat sheet: In this section, I'll be quickly going over various commands you might want to refer to while running co-pilot on your machine. For reasons pertaining to brevity, this section might not be all encompassing and you _may_ run into errors occasionally due to you local configuration. With that out of the way, let's get started: diff --git a/src/blog/2023-07-09-making-of-uli-august-2022-newsletter.mdx b/src/blog/2023-07-09-making-of-uli-august-2022-newsletter.mdx index 4a5cb23f..efadf2ef 100644 --- a/src/blog/2023-07-09-making-of-uli-august-2022-newsletter.mdx +++ b/src/blog/2023-07-09-making-of-uli-august-2022-newsletter.mdx @@ -7,7 +7,7 @@ date: 2023-07-09 tags: responsible-ai --- -## The Evolution of Uli +### The Evolution of Uli It has been nearly a year since the last email update from the Uli team! After the launch in August last year we had a six month period of demoing, planning for the next iteration and fundraising. Uli has been moving full steam since March of this year. But in the busyness of getting the next phase launched, we forgot the lovely ritual of these email updates! We are remedying the situation and will send you monthly(ish) updates for the next year of development. diff --git a/src/blog/2023-09-24-opensourceAI_scraping.mdx b/src/blog/2023-09-24-opensourceAI_scraping.mdx index 7ed0a37e..fad2c3ab 100644 --- a/src/blog/2023-09-24-opensourceAI_scraping.mdx +++ b/src/blog/2023-09-24-opensourceAI_scraping.mdx @@ -12,7 +12,7 @@ In late July, Medianama published an opinion piece titled: [The Gap Between Resp We published a response piece suggesting that the connections made in the original piece were simplified and each of the issues such as copyright, open source AI merited unique attention. The piece can be read here: [Open Source AI- A Nebulous Concept bearting a Heavy Weight](https://www.medianama.com/2023/09/223-open-source-ai-nebulous-concept-bearing-heavy-weight/) -The key arguments we made were: +### The key arguments we made were: 1. What open source means in AI is undefined and still evolving. All AI and not just open source AI is dual use. The claim in the article is that open-source AI leads to more applications and a possible increase in negative applications of a dual-use technology. This claim is very difficult to analyze because what open source means in the world of machine learning and AI is unclear to everybody involved in developing these systems. diff --git a/src/blog/2023-12-17-sprint-dmm.mdx b/src/blog/2023-12-17-sprint-dmm.mdx index fc8e538e..99c82e60 100644 --- a/src/blog/2023-12-17-sprint-dmm.mdx +++ b/src/blog/2023-12-17-sprint-dmm.mdx @@ -19,12 +19,14 @@ The 4 days were roughly split under the theme IDEATE, PROTOTYPE, BUILD, DEMO Two days before the sprint began we shared a non-intense reading list with the team. Since this is a topic that would have required expertise in multiple domains and we have a multi-disciplinary team, we hoped it would help providing a shared understanding of terminology and lanscape. -A subset of them are : +**A subset of them are :** +

* https://www.nytimes.com/2023/03/12/technology/deepfakes-cheapfakes-videos-ai.html * https://datasociety.net/library/deepfakes-and-cheap-fakes/ * https://arxiv.org/abs/2304.01328 * https://www.boomlive.in/decode/x-is-full-of-deepfake-porn-videos-of-indian-actresses-kajol-deepika-padukone-23533 * https://www.boomlive.in/law/elections-pornography-laws-on-technology-deepfake-artificial-intelligence-23656 +

## Day 1 : Ideation @@ -36,7 +38,7 @@ By the end of the day we had clusters of similar ideas: The major clusters that we identified where - Metadata, Taxonomy, Repository, Detection, Blockers, Platform, Law, Social Awareness, Helpline, Storytelling. Explanation about each can be read on our [post sprint documentation](https://github.com/tattle-made/Uli/wiki/Mitigating-Harms-of-Digitally-Manipulated-Media) -We had two goals for the end of the day : +**We had two goals for the end of the day :** 1. Think through the ideas to have enough clarity to formulate a multi month strategy @@ -48,7 +50,7 @@ To address (2) we had everyone vote on the three clusters they'd like to work on We set aside this day to rule out all the unknowns: What datasets are available relevant to our use case, what kind of datasets would we have to make, and if we were going to make it what would be the possible ready made data sources etc. -Some of the work that happened on day 2 included: +**Some of the work that happened on day 2 included:** 1. Finding news reports about DMMs in India to see what kind of information is available about techniques used, the nature of forgery etc 2. Finding forums dedicated to Indian deepfakes diff --git a/src/blog/2023-12-22-making-of-uli-dec-2023-newsletter.mdx b/src/blog/2023-12-22-making-of-uli-dec-2023-newsletter.mdx index 16aad808..9c96d0fd 100644 --- a/src/blog/2023-12-22-making-of-uli-dec-2023-newsletter.mdx +++ b/src/blog/2023-12-22-making-of-uli-dec-2023-newsletter.mdx @@ -7,7 +7,7 @@ date: 2023-12-22 tags: responsible-ai --- -## Updates from Uli: November/December 2023 +### Updates from Uli: November/December 2023 Here we are on the other side of the sixteen days of activism. In the newsletter in November we mentioned that we were starting our second round of crowdsourcing for the slur list during the [16 days of activism](https://github.com/tattle-made/Uli/wiki/16-Days-of-Activism-with-Uli). During the two weeks we added over fifty news words in Malayalam and 300+ annotations for the different slurs. A big shout out to Akash SS at the [Citizen Digital Foundation](https://citizendigitalfoundation.org/) who led most of the work in Malayalam. You can see the updated slur list and annotations [here](https://github.com/tattle-made/Uli/blob/main/browser-extension/plugin/scripts/2023-12-21-slur-metadata/data.csv): diff --git a/src/blog/2024-03-16-cross-platform-pt1.mdx b/src/blog/2024-03-16-cross-platform-pt1.mdx index 6772ec66..e7c8cbee 100644 --- a/src/blog/2024-03-16-cross-platform-pt1.mdx +++ b/src/blog/2024-03-16-cross-platform-pt1.mdx @@ -44,7 +44,7 @@ Implementation of cross-platform protocols that enable expedient redressal of on While it is currently implemented for more widely accepted and extreme instances of abuse such as dissemination of CSAM and terrorism-related material, extending such protocols to combat instances of OGBV and broader online harms faced by the community should be explored. It would be useful to evaluate similar technical response tools and their effectiveness at various stages of redressal to understand the landscape and develop broader protocols which could handle this issue of persistant abuse. -[^1]: https://onlineharassmentfieldmanual.pen.org/reporting-online-harassment-to-platforms/ +[^1]: https://onlineharassmentfieldmanual.pen.org/reporting-online-harassment-to-platforms/ [^2]: Julie Posetti, Kalina Bontcheva and Nabeelah Shabbir, ‘The Chilling: Assessing Big Tech’s Response to Online Violence Against Women Journalists’ (May 2022, UNESCO) @@ -52,10 +52,10 @@ It would be useful to evaluate similar technical response tools and their effect [^4]: Julie Posetti, Kalina Bontcheva and Nabeelah Shabbir, ‘The Chilling: Assessing Big Tech’s Response to Online Violence Against Women Journalists’ (May 2022, UNESCO) -[^5]: https://rebootingsocialmedia.org/2022/12/01/from-emergency-to-prevention-protecting-journalists-from-online-abuse/ +[^5]: https://rebootingsocialmedia.org/2022/12/01/from-emergency-to-prevention-protecting-journalists-from-online-abuse/ -[^6]: https://www.institute.global/insights/tech-and-digitalisation/social-media-futures-what-brigading +[^6]: https://www.institute.global/insights/tech-and-digitalisation/social-media-futures-what-brigading -[^7]: https://rebootingsocialmedia.org/2022/12/01/from-emergency-to-prevention-protecting-journalists-from-online-abuse/ +[^7]: https://rebootingsocialmedia.org/2022/12/01/from-emergency-to-prevention-protecting-journalists-from-online-abuse/ -[^8]: https://www.orfonline.org/expert-speak/identifying-and-removing-terrorist-content-online +[^8]: https://www.orfonline.org/expert-speak/identifying-and-removing-terrorist-content-online diff --git a/src/blog/2024-04-03-securing-feluda-pt1.mdx b/src/blog/2024-04-03-securing-feluda-pt1.mdx index c9df6741..00247aec 100644 --- a/src/blog/2024-04-03-securing-feluda-pt1.mdx +++ b/src/blog/2024-04-03-securing-feluda-pt1.mdx @@ -19,7 +19,7 @@ It is good practice to have all tests running in the CI pipeline as part of pre- List of [checks](https://www.youtube.com/watch?v=cmWQF2FDlG8) for a security-focused software project -1. A basic check to ensure submitted code is not buggy is to ensure that the existing features still work after we merge new code. For this, it is essential to have [unit tests](https://en.wikipedia.org/wiki/Unit_testing) and [integration tests](https://en.wikipedia.org/wiki/Integration_testing) with sufficient [code coverage]([https://en.wikipedia.org/wiki/Code_coverage). Our team continued adding tests to every new and existing feature so that we could test that we do not break features during development. Further, we setup a Github workflow that [runs tests](https://github.com/tattle-made/feluda/blob/main/.github/workflows/pr-tests.yml) for every PR created. +1. A basic check to ensure submitted code is not buggy is to ensure that the existing features still work after we merge new code. For this, it is essential to have [unit tests](https://en.wikipedia.org/wiki/Unit_testing) and [integration tests](https://en.wikipedia.org/wiki/Integration_testing) with sufficient [code coverage](https://en.wikipedia.org/wiki/Code_coverage). Our team continued adding tests to every new and existing feature so that we could test that we do not break features during development. Further, we setup a Github workflow that [runs tests](https://github.com/tattle-made/feluda/blob/main/.github/workflows/pr-tests.yml) for every PR created. 2. The next step is to add linting code as part of the CI pipeline. A language-specific [linter](https://en.wikipedia.org/wiki/Lint_(software)) checks for basic syntax and formatting issues. While linting does not indicate security issues, it does indicate poorly written code, which may be buggy right now or will eventually become buggy. Hence, it is essential to fix these issues early than accumulate technical debt that leads to potential future security and stability issues. diff --git a/src/blog/2024-04-17-crossplatform-pt2.mdx b/src/blog/2024-04-17-crossplatform-pt2.mdx index 3a15e6a8..e4afae8f 100644 --- a/src/blog/2024-04-17-crossplatform-pt2.mdx +++ b/src/blog/2024-04-17-crossplatform-pt2.mdx @@ -12,7 +12,7 @@ The most popular decentralised/federated social media platform is Mastodon; Thre The platforms, built on the Activity Pub Protocol and the AT Protocol, do not have a centralised authority that oversees activity on the platform. Instead, users join different 'instances', and each instance has its own set of rules, block lists and moderators. The instances interact with one another through 'federation'. -## Federated Moderation +### Federated Moderation On federated platforms mentioned above, it has been noted that hateful material can rapidly disseminate from one instance to [another](https://arxiv.org/pdf/2302.05915.pdf). Federation policies help administrators of instances create rules that ban or modify content from [instances](https://arxiv.org/pdf/2302.05915.pdf). @@ -25,7 +25,7 @@ Pleroma mentions the option to report a user's post to the administrator if it i Centralised social media platforms on the other hand have more extensive documentation on the process for [redressal](https://docs-develop.pleroma.social/frontend/user_guide/posting_reading_basic_functions/). On both federated and centralised platforms, the user goes through different reporting mechanisms for recourse. -## Responses +### Responses As we discussed earlier, centralised responses to tackle cross-platform abuse focus on prima-facie illegal content such as CSAM and terrorism. Amongst research on the decentralised web, there have been suggestions on tools that could be used to tackle issues that come with moderation on federated platforms: (i)WatchGen, a tool which proposes instances that moderators should focus on and thus reduce the burden of moderation on [administrators](https://arxiv.org/pdf/2302.05915.pdf); @@ -37,7 +37,7 @@ By automating and attempting to improve the detection of toxic content and flag Both categories of platforms must test out tools that engage in collaborative moderation for a more effective and thorough action on content. Given the gravity of instances such as online abuse, platforms must extend signal-sharing protocols and similar tech responses to such offences as well, beyond straightforward offences such as CSAM and terrorism. -## In sum +### In sum Corporate accountability may limit the extent of responsibility a platform has towards a user (i.e. a platform entity is only responsible for what goes on in the platform), and considers an issue resolved when flagged content is acted on by moderators/administrators, as the case may be. Within federated platforms, an administrator's responsbility is limited to acting upon content in their instance, and the issue is considered 'resolved', just as in centralised platforms. diff --git a/src/blog/2024-04-24-silencing-impact.mdx b/src/blog/2024-04-24-silencing-impact.mdx index 9ca0f169..7bef84b2 100644 --- a/src/blog/2024-04-24-silencing-impact.mdx +++ b/src/blog/2024-04-24-silencing-impact.mdx @@ -26,4 +26,4 @@ As IT for Change and InternetLab stated in their [June 2021 report](https://itfo The severity and embedded nature of this problem served as the impetus for the Uli project, whose beginnings we detail in Blog 2 of this series. -Illustration Credits: Mitali Panganti +### Illustration Credits: Mitali Panganti diff --git a/src/blog/2024-05-02-chisel-online-abuse.mdx b/src/blog/2024-05-02-chisel-online-abuse.mdx index 67a86230..13132da6 100644 --- a/src/blog/2024-05-02-chisel-online-abuse.mdx +++ b/src/blog/2024-05-02-chisel-online-abuse.mdx @@ -7,7 +7,7 @@ date: 2024-05-02 tags: machine-learning, responsible-ai --- -
+
![HeroIllustration](../images/products/HeroIllustration.gif)
diff --git a/src/blog/2024-05-16-context-matters.mdx b/src/blog/2024-05-16-context-matters.mdx index a23d9f5a..2906614e 100644 --- a/src/blog/2024-05-16-context-matters.mdx +++ b/src/blog/2024-05-16-context-matters.mdx @@ -5,10 +5,11 @@ author: "Tattle" project: Uli date: 2024-05-16 tags: machine-learning -cover: ../images/products/Illustration-context-matters.png ---- -
+ +--- +
+
![Context Matters Illustration](../images/products/Illustration-context-matters.png)
@@ -26,5 +27,5 @@ Dharini Priscilla, one of the annotators for the Tamil list, also reflected on h Uli’s plug-in also incorporates ways to contextualise. Users can create their own list of words that they want to see redacted in their feeds. Additionally, since the slur lists are open source, they can be used and contextualised depending on the need. Tattle has also kept an open line of communication, allowing users to write in if they feel a word needs to be removed from the list, and have a compelling reason for the request. -Illustration: Mitali Panganti +### Illustration: Mitali Panganti diff --git a/src/blog/2024-12-05-tattle-mlcommons.mdx b/src/blog/2024-12-05-tattle-mlcommons.mdx index a47078ed..6cd6ba55 100644 --- a/src/blog/2024-12-05-tattle-mlcommons.mdx +++ b/src/blog/2024-12-05-tattle-mlcommons.mdx @@ -8,7 +8,7 @@ tags: responsible-ai cover: ../images/ml-commons-report-blog-hero.png --- -## Launch of V1.0 AI Luminate, and how Tattle is involved +### Launch of V1.0 AI Luminate, and how Tattle is involved Earlier this year, ML Commons, a global organisation which works to improve AI systems issued an expression of interest for creating prompts in non-English languages. Tattle was selected as a pilot project to contribute to the benchmark in Hindi, using the participatory approach we followed with Uli [^1], and we commenced working on this project. diff --git a/src/blog/2025-02-03-hover-feature.mdx b/src/blog/2025-02-03-hover-feature.mdx index 1896a4e1..24169653 100644 --- a/src/blog/2025-02-03-hover-feature.mdx +++ b/src/blog/2025-02-03-hover-feature.mdx @@ -7,8 +7,10 @@ date: 2025-02-03 tags: online-harms cover: ../images/uli-slur-redaction-image.jpg --- - -## Building Uli: How the hover-for-metadata feature came about. + +import { Heading } from "grommet"; + + Building Uli: How the hover-for-metadata feature came about. In October of 2022, we presented an early version of the Uli browser extension to many people. By this time we had created a crowdsourced list of slurs in Indian languages. The Uli browser extension looked for these slurs on any webpage that you visited and if it found a match, it would replace that word with a black patch. We called this feature slur redaction. diff --git a/src/blog/2025-03-07-techdev-uli.mdx b/src/blog/2025-03-07-techdev-uli.mdx index 7ede2f1a..c22678f8 100644 --- a/src/blog/2025-03-07-techdev-uli.mdx +++ b/src/blog/2025-03-07-techdev-uli.mdx @@ -30,22 +30,24 @@ In the process of updating Uli, we also created the following options on the plu Ahead of workshops we were conducting with college faculty and students, we worked on easing the process of demonstrating the value of Uli to our audience, such as the process of crowdsourcing online, and the metadata display on slur words. There were constraints posed in terms of limited access to laptops, which meant we needed to work out a way to enable the Uli experience on a mobile device as well. - 1. **Uli Community Page** +1. **Uli Community Page** We created a page that would allow users to look at words they added to the crowdsourced list, as well as the contextual information added about it, as below. It was developed using the Phoenix web framework in the Elixir programming language and we use PostgreSQL for our database. -
+
Uli input 1
- 2. **Interactive Input Page** - For our second workshop, we wanted to have an interactive exercise where we set forth examples of daily life with uncomfortable interactions and problematic words mixed in during conversations. - Presented with the limitation that students only had access to their mobiles and not laptops, we needed an interface for the students to test it against our database of slurs and our hover feature which would display the metadata behind a slur while highlighting them. - Therefore we created a separate web page on the Uli website, where students could input the illustrations we had written, and receive an output with the hover feature’s functionality displayed. - This feature processes the input text, checking it against predefined metadata. Matched words are wrapped with styled HTML elements for highlighting and hover effects, and the result is dynamically rendered. - React components manage state and lifecycle events for interactions. + +2. **Interactive Input Page** + +For our second workshop, we wanted to have an interactive exercise where we set forth examples of daily life with uncomfortable interactions and problematic words mixed in during conversations. +Presented with the limitation that students only had access to their mobiles and not laptops, we needed an interface for the students to test it against our database of slurs and our hover feature which would display the metadata behind a slur while highlighting them. +Therefore we created a separate web page on the Uli website, where students could input the illustrations we had written, and receive an output with the hover feature’s functionality displayed. +This feature processes the input text, checking it against predefined metadata. Matched words are wrapped with styled HTML elements for highlighting and hover effects, and the result is dynamically rendered. +React components manage state and lifecycle events for interactions. -
+
Uli input 1 Uli input 2
diff --git a/src/blog/2025-03-12-deepfake-o-meter.mdx b/src/blog/2025-03-12-deepfake-o-meter.mdx index 0b3a970f..6e3d3a4f 100644 --- a/src/blog/2025-03-12-deepfake-o-meter.mdx +++ b/src/blog/2025-03-12-deepfake-o-meter.mdx @@ -21,6 +21,7 @@ Alongside this, we also looked at the different types of deepfakes across variou As we wanted to assess feasibility, in terms of training/inference and the potential for self-hosting. We did a high level categorisation of all the models in deepfake-o-meter based on attributes such as framework/library used, programming language, and ease of use (easy \- instructions to run the model are given and weights are open, medium \- model needs to be trained, hard \- model needs to be trained or/and training instruction are vague/not available). +
| Model Name | Framework/Library | Language | Ease of use | Modality | | :---- | :---- | :---- | :---- | :---- | | [Nodown](https://github.com/grip-unina/GANimageDetection) | Torch | Python | Easy | Image | @@ -33,6 +34,7 @@ As we wanted to assess feasibility, in terms of training/inference and the poten | [RawNet2](https://github.com/eurecom-asp/rawnet2-antispoofing) | Torch | Python | Hard | Audio | | [RawNet2-Vocoder](https://github.com/csun22/Synthetic-Voice-Detection-Vocoder-Artifacts) | Torch | Python | Easy | Audio | | [Whisper](https://github.com/piotrkawa/deepfake-whisper-features) | Torch, openai-whisper | Python | Easy | Audio | +
(A more detailed extension of this table can be [found here](https://github.com/tattle-made/deepfake-marker/issues/2#issuecomment-2601652610)) diff --git a/src/blog/2025-04-30-community-notes.mdx b/src/blog/2025-04-30-community-notes.mdx index 13dfb44a..c074c98c 100644 --- a/src/blog/2025-04-30-community-notes.mdx +++ b/src/blog/2025-04-30-community-notes.mdx @@ -34,6 +34,7 @@ In our analysis, we focused only on currently active India-based IFCN signatorie Here is a distribution with counts of India based IFCN websites. +
| Domain | Type of links | Count | | :---- | :---- | :---- | | [factly.in](http://factly.in) | Fact Checks and Stories | 328 | @@ -55,6 +56,7 @@ Here is a distribution with counts of India based IFCN websites. | [thelallantop.com/factcheck](http://thelallantop.com/factcheck) | Only Fact Checks | 2 | | [manoramaonline.com/fact-check](http://manoramaonline.com/fact-check) | Only Fact Checks | 2 | | [medicaldialogues.in/fact-check](http://medicaldialogues.in/fact-check) | Only Fact Checks | 1 | +
### **Possible Future Directions:** diff --git a/src/blog/2025-11-01-tech4dev-evals.mdx b/src/blog/2025-11-01-tech4dev-evals.mdx new file mode 100644 index 00000000..4e3598f3 --- /dev/null +++ b/src/blog/2025-11-01-tech4dev-evals.mdx @@ -0,0 +1,25 @@ +--- +name: Reflections on Building Safety Guardrails for Tech4Dev +excerpt: What we've learnt in three months of working with non-profits working with Tech4Dev +author: Tarunima Prabhakar +project: AI Safety +date: 2025-11-01 +tags: responsible-ai +--- + +For the last three months we’ve been working with [Project Tech4Dev](https://projecttech4dev.org/) to advise the nonprofits in their AI cohort on safety guardrails. Just as nonprofits are figuring out where and how to build AI for their use cases, so are a bunch of us figuring out how to build guardrails. This is evolving territory and we are very much in the research- develop - iterate mode. + +In our first workshop with the nonprofits we gave them a set of prompts and asked them to write what they believed were acceptable and unsafe responses for an LLM to provide for each. It was easy for the nonprofits to answer the acceptable bit- anything that fell within the knowledge base that was provided in prompt engineering or RAG based customization. But outside of this small territory is the vast landscape of banal, unhelpful, nonsensical, biased and harmful responses. The nonprofits answered the question on unsafe responses in the negative- anything that fell outside the knowledge base. With a deterministic system, this hard line of a knowledge base could have been helpful. But, with large language models a non-trivial percentage of responses are going to fall outside the knowledge base. In ‘clinical’ settings, RAG based systems can give over 95% accuracy. But based on the implementation and task, the hallucination rates [can be higher than 20%](https://www.docket.io/glossary/hallucination-rate#:~:text=8.,pull%20from%20external%20knowledge%20sources.). And while all technologies fail, machine learning models fail unpredictably. Given that there is no cause and effect reasoning for why a model gives non-ideal outputs, how do you identify and deal with the failures? + +While we entered this work armed with AI risks and hazard frameworks, it strikes me that that is not how product teams at nonprofits look at it. They’re equally concerned with banal and unhelpful outputs as unsafe ones. Banal outputs, which are more commonly observed, reduce the utility of nonprofits’ service. Outright unsafe outputs might be less common but have more severe consequences. + +The essential step missing from existing product development cycles is evaluating the quality of outputs for a set of inputs. Tech4Dev underscored this in the in-person sprint in October. Quality is multi-dimensional. It can be about accuracy but also about tone, bias and cross-language consistency. In the context of this blog, and our work with Tech4Dev, we are using evaluation specifically to refer to [model evaluation](https://eval.playbook.org.ai/framework)\- does the application produce the desired response to a question or statement from the user of the service? Tatle’s work is one step beyond evaluations. We need to figure out how to categorize the non-ideal outcomes, understand the risk and consequence of each category and build failsafes accordingly. Failsafes also affect user experience. So, product teams are right in pushing back on proposals that slow down the output time or add to their development costs. + +Our work so far has made me remember the quote from Anna Karenina- “all happy families are alike but all unhappy families are unhappy in their own way.” There is general agreement on the happy/ good responses, but each bad response is bad in its own way, needing deeper diagnosis to figure out how we manage and fix it. A response can be bad because it adds noise when people come to it for information. It can be bad because it is different for men vs. women. It can be bad because it encourages self harm. It can be bad because it sounds overconfident… + +In the past few years we’ve seen a bunch of risk frameworks and benchmarks to test for some of these categories of harms. For examples, there is [Bias Benchmark](https://arxiv.org/abs/2110.08193) for bias and fairness and [HaluEval](https://arxiv.org/abs/2305.11747) for hallucinations. +In the evaluation of ‘bad outcomes’ we have multiple taxonomies and frameworks spanning bias, security, safety. Many of these, however, are directed towards foundation model developers rather than application developers. Even if application developers detect bias in the outputs, they have limited control in mitigation if they aren’t fine tuning the models (and most are not). Second, as our [work with MLCommons](https://tattle.co.in/products/ml-commons-safety-benchmark/) made clear, the lines between these different categories are blurred. Social biases when taken to an extreme and acted upon become illegal activities. How should an application developer use these frameworks productively? + +This is something that we are hoping to work through the next few months. We are starting by working with a few nonprofits and analyzing their past data, and conducting some small-scale red-teaming. We’re starting by analyzing the inputs and outputs separately. Categorizing the input helps us understand if there is a way to filter out inputs that may result in unsafe or irrelevant outputs, or triage inputs that should be sent to the LLM. The goal of categorizing the outputs is to map deviations from the ideal in a way that is actionable. Not all deviations are equally serious and some outputs might be useful enough, despite issues in, say, tone. By working with nonprofits across different disciplines, we should also be able to understand which categories are generalizable across use cases and which ones are not. For example, inputs that are straight up abusive- and there are such questions/comments in real world interactions- should be ignored across all domains. But incorrect advice on career options has different consequences from incorrect advice on maternal health and perhaps action on hallucinations in a career chatbot should be different from that on a maternal health chatbot. The existing AI risk frameworks could serve as a starting point for categorization. The other way to categorize is to think of the on-ground consequences of ‘bad’ outputs. The second approach requires deeper engagement with the nonprofits since they have the best understanding of these consequences. + +While we hope to be able to build some plug and play solutions for the nonprofits to make their AI applications safer, my hunch is that the more impactful work will be in codifying the process of safety evaluations so that it is easily replicable for nonprofits. GenAI is too early to have stable and established guardrails. We don’t even have ballparks for the probability and magnitude of contextual risks. The balance to strike is building adequate observability of user interactions, without over-imposing on non profits’ time as they try to find problem-solution fit. diff --git a/src/blog/2025-12-11-denormalising-slurs-workshops.mdx b/src/blog/2025-12-11-denormalising-slurs-workshops.mdx new file mode 100644 index 00000000..7e839971 --- /dev/null +++ b/src/blog/2025-12-11-denormalising-slurs-workshops.mdx @@ -0,0 +1,32 @@ +--- +name: Digital Safety Workshops to Denormalise Online Abuse +excerpt: We are introducing an interactive workshop with people over the age of 14 in schools and colleges to denormalise online abuse and harm through hate speech and slur words. These workshops aim to push back against the harm caused by offensive language on the internet through activities that sensitize, explain and engage students in an empathetic and open manner. +author: Poorvi Gupta +project: Uli +date: 2025-12-11 +tags: workshop +--- + +When we launched the Uli plugin in 2022, a tool that redacted abusive words from one's browser, we received feedback from multiple academics that they +saw it as being valuable as a pedagogical tool. As a tool that would help their students understand how pervasive online trolling is, and help +denormalize ways of speech and engaging online that they had gotten used to. Acting on the feedback, we introduced an interactive workshop with people +over the age of 14 in schools and colleges to denormalise online abuse and harm through hate speech and slur words. These workshops aim to push back +against the harm caused by offensive language on the internet through activities that sensitize, explain and engage students in an empathetic and open +manner. + +These workshops help students understand both visible and invisible forms of online harm, explore the different ways abuse manifests on social media +platforms, and recognize who is most often targeted. Through the few iterations of the two-hour long workshop that we’ve had in colleges of Delhi +University and online with NGO Sajhe Sapne, we’ve come to the understanding that there is an urgent need for such workshops with young adults who are +incessantly online and vulnerable to a wide range of abuse that impacts their social, mental and physical health. + +We focus on interactive learning because when students feel heard and included, they are more likely to share real experiences from their online lives +and understand that harmful behaviour they’ve normalised may actually be abuse that they can report. + +One of the most normalised forms of abuse is hate speech and slur words which largely go unreported on social media. Through conversations and +activities specifically and sensitively designed to ignite conversations around slur words, we explore what makes some words slurs, what makes them +problematic and whom do these slur words end up targeting? We believe it is important to address how problematic terms are weaponised against +marginalised and historically oppressed communities and gender minorities. + +Through India-centric data and research on cases of cyber crime and AI-enabled online abuse, the workshop delves into tools like the Uli plug-in, +resources and tools that students can use to prevent and safeguard themselves from online abuse. While we do believe actively that continuous and +consistent work is required to deal with online abuse, these workshops with young adults act as a step forward in that direction. diff --git a/src/blog/2025-12-12-what-is-ai-safety.mdx b/src/blog/2025-12-12-what-is-ai-safety.mdx new file mode 100644 index 00000000..01f30eea --- /dev/null +++ b/src/blog/2025-12-12-what-is-ai-safety.mdx @@ -0,0 +1,126 @@ +--- +name: What is AI Safety? +excerpt: This blog provides a brief overview of the current state of AI safety as a discipline from the perspective of research and practice. It covers common trends in safety evaluations of AI and raises important questions about the feasibility of a one-size-fits-all approach to AI trust and safety. +author: Aditi Aggarwal (aka Baarish) +project: responsible-ai +date: 2025-12-12 +tags: responsible-ai +--- + +An air of celebration mixed with apprehensive bracing seems to hang around the development of AI systems. People want AI to solve all of humanity’s +problems while nervously eyeing the risks that seem to keep piling up in the nascent evolution of this rendition of new tech. We are teetering on the +brink of something - chaos or liberation is yet to be revealed. Wherever you might lie on this spectrum of celebration to deep cynicism regarding AI, +I can guarantee that you have interacted with it and/or thought about its impacts on your life. As an organization that concerns itself with digital +safety, Tattle has inevitably found itself working to develop safety guardrails for AI. In our research into this emerging field, we are coming to +recognize that there continues to be critical limitations to existing AI safety discourse. + +1. The majority of AI Trust and Safety literature and practice is emerging from the Global North and is primarily focused on English-speaking Large Language Models and its English-speaking users. +2. An assumption that trust and safety manifest in the same way across histories and geographies. In other words, that there is a uniform definition of trust and safety that is shared across languages, cultural and political contexts, and use cases. + +With understanding any new concept it is important to first define the terms and know the parameters of what is being discussed. Firstly, what is AI? A simple definition provided by IBM says: +>Artificial intelligence (AI) is technology that enables computers and machines to simulate human learning, comprehension, problem solving, decision making, creativity and autonomy.[^1] + +In other words AI refers to computer systems that are learning to think like humans or developing machine learning capabilities. + +Next, what is safety? Simply put safety is a condition of being protected from harm. With reference to AI, safety is about preventing or reducing harms +posed by AI to humans and society at large. Given this definition, it is important to understand the parameters within which current research on and practice of AI safety is being conducted. To begin with, what are the primary risks and/or harms that AI safety seeks to mitigate or prevent. + +As we understand them, AI systems are known to have produced several risks, some of which are listed below: : + +1. Hallucinations - the wide range of fabricated, fantastical, and incorrect responses generated by AI models in response to queries. +2. Impersonation and generating false images of real people. +3. Misinformation and hate speech - AI models have been recorded generating and reproducing hate speech and malicious false information. +4. Sensitive information disclosure - revealing private information from training data or the internet to users. +5. Severe environmental impact - water scarcity, loss of flora and fauna around AI data centers. + +How are public and private actors within the field of AI addressing these risks? Currently, there are three main trends in AI safety.[^2] + +1. Safety engineering to ensure alignment with human values and predictable control over AI. +2. Safety evaluations and testing to detect bias, misrepresentation, exclusion, and other social harms. +3. Mitigating and managing catastrophic AI risk. + +Cumulatively, these three approaches to AI safety aim to improve trustworthiness in AI to make it more safe in its uses by focusing on specific aspects +including security, accountability, monitoring, effectiveness, reliability, robustness, transparency and explainability, and fairness. +What differentiates these approaches is their objectives, methodology and audience. + +Safety engineering is the field of engineering that developed to ensure safety from harms within all the diverse fields of engineering. Some examples +include the safety policies deployed in a chemical producing factory, research into the safety mechanics of automobiles, the research of safety practices +appropriate for keeping nuclear plants from leaking harmful radiations. In a similar fashion, safety engineering for AI has focused its efforts in +engineering AI to be more aligned with human values to ensure that it does not become autonomous or take actions that go against human interest. +This premise assumes that the audience for AI is homogenous to an extent and resembles the creators of AI systems in that it assumes shared values. + +Arriving at consensus on what human values are shared by all humankind for AI alignment is a monumental task rife with contradictions and power +inequalities. It involves deliberating over which humans get to control AI. Is it merely those who pay for the development and deployment of machine +learning systems? Should it be governments within whose jurisdiction AI systems are deployed? Or should it be the users who ultimately experience the +effects of the use of an AI system? Is there an argument for considering the natural flora and fauna around the server farms for AI systems to be +stakeholders as we redirect potable water for cooling AI data centres? The field of safety engineering has yet to propose solutions for building +regulatory and monitoring institutions that are accountable to a broad array of stakeholders for overseeing AI safety in such a way that it ensures +the greatest reduction of harm and highest level of protection of all these stakeholders. + +This brings us to the second set of AI safety practices that has evolved as a response to the narrow safety engineering focus. There is a growing number +of civil society, research and non-profit organizations around the world that are developing toolkits, processes, and policy recommendations for +embedding principles of social justice and harm repair within AI systems. This avenue of AI safety concerns itself with identifying the existing biases, [^3] +prejudices, inequalities, and misrepresentation in the data [^4] used to train AI systems and the institutional structures in which AI is designed and +deployed. + +This set of approaches tends to operate by conducting safety evaluations of AI systems before or during their pilot tests to identify issues arising +at the point of consumer contact. Safety evaluations focus on accuracy of responses, appropriateness of tone, and the degree of deviation from the +correct response (also known as hallucinations) that an AI system shows. Teams that run this specific kind of safety evaluation aim to produce +context-specific suggestions for safety, recognizing that there is a diversity of interests, values, and risks experienced by the users of any AI system. +Therefore, a one-size-fits-all safety solution is likely to be inadequate in building more reliable and trustworthy systems. + +The third approach to safety is planning for the possibility of catastrophic risks from AI in the future. A large part of this research focuses on how +to mitigate risks arising from AI operating autonomously and resisting human control[^5]. In tandem, an environmental movement against the use of AI due to +its devastating impacts on water scarcity [^6] and destruction of local ecosystems also operates within this framework and argues against the deployment of +AI altogether as the safest approach forward. They suggest that AI should be reserved for only a few use cases that are critical for human survival and +not be used ubiquitously to accomplish daily tasks[^7]. + +AI is not separate from humans. We designed it to replicate human thinking and eventually think by itself. But it's designed on human data and biases +and therefore it is reflecting human biases and violences. So to address AI safety we need to address the very human institutions that create it. +We have to go several steps back before a model even exists to think of what are the intentions and motives with which it was created. To make AI safe +it is necessary to monitor and regulate the institutions[^8] within which it is developed and where decision-making about its design is conducted. + +The current way to address this is to analyze training data used to train a model and identify the biases, misrepresentations, and lacunae present in it. +Most commonly, researchers are trying to feed more diverse training data in order to improve model performance. However, adding data does not address +the initial biases that have become part of the model’s knowledge system. To address that requires starting from scratch and redeveloping models with +multi-stakeholder inputs, an approach that most corporates will not pursue due to costs and loss of profits. As public pressure against the large-scale +adoption of AI grows, researchers, activists, and policy makers are grappling with putting safety guardrails into place to mitigate current harmful +effects of AI systems. + +All of this brings us back to the beginning of this piece where I set out with some definitions of AI, safety, and risk. Surveying the field reveals +that one of the underlying assumptions across much existing work is that there is a shared understanding and experience of risk and safety across the +globe. Therefore, delving into AI safety in a meaningful way in the Global South requires developing taxonomies of risks and safety measures that are +rooted in the specific cultural, historical, linguistic, and geographical contexts where AI will be deployed. In order to address this big omission it +is necessary for AI safety practitioners to seriously consider: + +1. What are the non-negotiables when it comes to ensuring safety for their communities - is it security, data privacy, alignment with cultural norms? +2. Whether LLMs developed in the Global North can be tweaked to make them safe for contexts which did not inform their design? + +[^1]: Cole Strykery and Eda Kavlakoglu, What Is Artificial Intelligence (AI)?, IBM, 9 August 2024, + https://www.ibm.com/think/topics/artificial-intelligence + +[^2]: Jacqueline Harding and Cameron Domenico Kirk-Giannini, What Is AI Safety? What Do We Want It to Be?, arXiv:2505.02313, version 1, preprint, arXiv, 6 May 2025, + https://doi.org/10.48550/arXiv.2505.02313 + +[^3]: Shelton Fitch, Revisiting AI Red-Teaming, Center for Security and Emerging Technology, 26 September 2024, + https://cset.georgetown.edu/article/revisiting-ai-red-teaming/; + Markov Grey and Charbel-Raphaël Segerie, Safety by Measurement: A Systematic Literature Review of AI Safety Evaluation Methods, arXiv:2505.05541, preprint, arXiv, 8 May 2025, + https://doi.org/10.48550/arXiv.2505.05541; + Laura Weidinger et al., Sociotechnical Safety Evaluation of Generative AI Systems, arXiv:2310.11986, preprint, arXiv, 31 October 2023, + https://doi.org/10.48550/arXiv.2310.11986 + +[^4]: Shaina Raza et al., MBIAS: Mitigating Bias in Large Language Models While Retaining Context, Proceedings of the 14th Workshop on Computational Approaches to Subjectivity, Sentiment, & Social Media Analysis, Association for Computational Linguistics, 2024, 97–111, + https://doi.org/10.18653/v1/2024.wassa-1.9 + + +[^5]: Miles Brundage et al., The Malicious Use of Artificial Intelligence: Forecasting, Prevention, and Mitigation, arXiv:1802.07228, preprint, arXiv, 1 December 2024, + https://doi.org/10.48550/arXiv.1802.07228 + +[^6]: UNEA, AI Has an Environmental Problem. Here’s What the World Can Do about That, UN Environment Programme, 13 November 2025, + https://www.unep.org/news-and-stories/story/ai-has-environmental-problem-heres-what-world-can-do-about + +[^7]: Beyond Fossil Fuels et al., WITHIN BOUNDS: Limiting AI’s Environmental Impact — Joint Statement from Civil Society for the AI Action Summit, 7 February 2025, + https://beyondfossilfuels.org/2025/02/07/within-bounds-limiting-ais-environmental-impact/ + +[^8]: Tim O’Reilly, You Can’t Regulate What You Don’t Understand, 14 April 2023, + https://web.archive.org/web/20230414162057/https://www.oreilly.com/content/you-cant-regulate-what-you-dont-understand-2/ diff --git a/src/blog/2025-12-30-comments-draft-IT-rules-amendment-2025.mdx b/src/blog/2025-12-30-comments-draft-IT-rules-amendment-2025.mdx new file mode 100644 index 00000000..9732617f --- /dev/null +++ b/src/blog/2025-12-30-comments-draft-IT-rules-amendment-2025.mdx @@ -0,0 +1,77 @@ +--- +name: Comments on the Draft IT Rules’ amendment (2025) +excerpt: We outline Tattle’s submissions to the proposed rules regulating deepfakes on platforms in this post, and our key recommendations. +author: Kaustubha Kalidindi +project: DAU +date: 2025-12-30 +tags: synthetic-media +--- + +In October this year, the Ministry of Electronics and Information Technology (MEITY) had published draft amended rules to cover the scope of harms +emanating from AI generated/synthetically generated information. The rules in their current iteration govern obligations of intermediaries with respect +to content hosted on their platform, and in the proposed amendment, additional obligations are to be incurred by intermediaries for synthetic content. +In the background note, it is stated that the aim of the proposed amendment is to curb harms such as deepfakes and to prevent their weaponization to +‘spread misinformation, damage reputations, manipulate or influence elections, or commit financial fraud’. + +In sum, the proposed rules require synthetically generated content to be labelled. The label must cover 10% of the content, and intermediaries are +required to ensure any synthetic content hosted on their platform contains such a label. We submitted a response to the call for comments on the +proposed amendment. Our key concerns were with respect to the following: + +**1. Broad Definition of synthetic content** +The definition of ‘synthetically generated information’ in the proposed rules is broad, and current interpretation would include any modification made, +including simple editing tools such as increasing/decreasing contrasts or brightness on an image, or adding filters to images posted on social media, +since they fall under the ambit of the word ‘artificiality’. It could also include use cases such as text edited using an AI powered spell-checker, +and remixes or modified samples of existing music using software tools. The scope of the proposed definition would result in most content online needing +to be labelled and verified including content that falls below the threshold of what would be considered harmful. + +**2. The Challenge with Labels** +The proposed rules require any computer resources which enable the creation of synthetically generated information to label or permanently embed such +content, covering at least 10% of the content area. The rules state that the media item must be embedded with metadata or labels that are visible or +audible. The labels, as per the proposed amendments, are supposed to serve both a technical and social function. While the technology to establish provenance +with metadata is evolving through initiatives like C2PA, the adoption is slow. Furthermore, watermarks are easily removed. +The technology for detection is also not foolproof- image and video models only support detection of manipulation of facial attributes [^1]. Models encounter +challenges in detecting manipulations which are of low quality or where they consist of real-world images previously unseen [^2]. Audio controlled models +failed to perform well on real samples [^3]. With these limitations in detection and labeling of content, the guidelines stipulate responsibilities that +cannot be met by significant social media intermediaries on which this content is hosted. + +**3. Addressing Harms from AI generated Content** +When examining the harms from AI generated content, we have found that these are addressed in existing regulations [^4]. For example, AI generated nudes can +be actioned under the Information Technology Act, and the Bharatiya Nyay Sanhita, through provisions on impersonation, sexual harassment, and +transmission of explicit content [^5]. Use of AI for creating misleading content during elections can be addressed under the code of conduct released by ECI [^6]. +The RBI has issued directions to strengthen the digital lending ecosystem and protect against incidents such as loan app scams [^7]. +The biggest challenge is in the enforcement of existing laws. Bad actors, seeking to do harm, are wilful defaulters of existing laws and will also not +comply with any stipulations for labeling. Instead of adding more regulations, strengthening the enforcement of existing regulations will address harms +from AI generated content. Some of the concerns faced in enforcement are: + >1. An absence of clear protocols at police stations to handle digital evidence in a manner that maintains its integrity and respects the privacy and dignity of the survivor. + >2. Delays at the Forensic Science Library (FSL) which hinder investigations, and difficulties in translating report conclusions to judicial authorities. + >3. Challenges in validating digital evidence in court and explaining chain of custody. + +**Our Key Recommendations** + +1. The current legal framework is sufficient in tackling harms pertaining to synthetic content, it is recommended that regulation instead be focused on + expanding the applicability of existing laws to such content. +2. Outside of the narrow category of AI generated videos and audio, a definition of AI modified content will inevitably rely on arbitrary thresholds. + Any prescriptions for labeling on provenance must only be stipulated for the narrow category of content that is entirely AI generated. This however, + should come with the understanding that given the state of technology today, bad actors will easily remove such identifiers. +3. Community guidelines must be legally compliant, and transparency is vital for effective redressal of platform violations. A techno-legal approach to + the issue of harms from AI generated content could also be stipulations for data sharing by SSMIs that enable independent evaluations of content + addressed under existing categories of harm. This can help address violations that include synthetic content. +4. Detection measures such as watermarking and labeling are reacting to a fast evolving generative AI landscape resulting in an endless cat and mouse + chase [^8]. Instead, we must focus on the needs of survivors who have been detrimentally impacted by synthetic content. This requires capacity building and + sensitisation of support networks, law enforcement and judiciary so that survivors feel confident in reporting incidents. + +[^1]: https://tattle.co.in/blog/2025-03-12-deepfake-o-meter/ + +[^2]: Ibid. + +[^3]: Ibid. + +[^4]: https://tattle.co.in/make-it-real-report.pdf + +[^5]: Ibid. + +[^6]: https://www.pib.gov.in/PressReleseDetailm.aspx?PRID=2019760 + +[^7]: https://rbidocs.rbi.org.in/rdocs/notification/PDFs/36NT8C402BE7C2A349E0BFFF3C526668CD7A.PDF + +[^8]: https://tattle.co.in/blog/mitigating-harms-of-synthetic-media/ \ No newline at end of file diff --git a/src/blog/2026-01-20-functional-ai-safety.mdx b/src/blog/2026-01-20-functional-ai-safety.mdx new file mode 100644 index 00000000..8cc19671 --- /dev/null +++ b/src/blog/2026-01-20-functional-ai-safety.mdx @@ -0,0 +1,35 @@ +--- +name: Towards Functional Safety in AI in the Social Sector +excerpt: Reflections from the discussions pertaining to AI Safety at the Expert Group Convening on AI evaluations in the social sector, convened by Agency Fund and C4GD +date: 2026-01-22 +project: +tags: responsible-ai, ai-safety +author: Tarunima Prabhakar +--- + +In December Agency Fund and C4GD convened an expert group to discuss evaluation of Generative AI in the social sector. These are reflections from the breakout discussion on safety evaluations where we also tried to conceptualize a set of minimal viable safety evaluations. + +Just as aspirations for AI range from mundane efficiencies to superintelligence, AI safety considerations also range from micro-fixes to the catastrophic. AI is a loose term that includes a range of applications- from foundational model services handed out at schools and workplaces, to image recognition and natural language processing for quiz assessments, to personalized advice for primary care givers. The [Agency Fund evaluation playbook](https://www.playbooks.org.ai/framework) has a challenging mandate of identifying best practices for design and evaluations common to a wide set of applications. It is similarly challenging to speak of safety evaluations in abstraction. Yet, that is the task that the breakout room had at hand. + +What struck me through the numerous threads of the conversation was that the narratives around AI, writ large, were also affecting our perceptions of what AI safety work means. I am very much in the [‘AI as normal technology](https://www.anildash.com/2025/10/17/the-majority-ai-view/)’ camp. This means that building AI safely today, is more of fixing the nuts and bolts in the here and now. That isn’t to say that we shouldn’t have a sight of the emergent long-term risks, but conflating the two time scales of risks can be counterproductive. The long-term risks feel ambiguous and hard to measure and many social sector teams can’t devote resources to that task. In the process, it can feel challenging to do any safety work. One of the issues we touched on in the discussion was the use of AI for the generation of synthetic media. It is a vexing issue, and one that Tattle devotes a fair bit of time on in the context of [misinformation](https://dau.tattle.co.in/) and [gendered abuse](https://tattle.co.in/blog/make-it-real/). But it isn’t something that any of the non-profits we’ve engaged with have encountered in the context of their services. We have, however, seen instances of AI bots disclosing caste information, or being used for questions on sex determination. Those are the risks that impact the utility and perception of a service provided by the social sector. Fixing these, even if with imperfect band-aids, is possible. + +#### Safety is Within, not Outside of Design: + +Something I have heard now, more than once, is that too much emphasis on safety or responsibility is taking away from the actual work of building applications or innovation. I also attribute this to narratives and noise around AI that is making it difficult to have useful or actionable conversations. On one hand you have the safety vs. innovation debate within the ‘AI as a superintelligence’ community. On the other, you have years of policy chatter on responsible AI that hasn’t felt actionable to product teams. The gap between principles and action is [slowly being bridged](https://arxiv.org/pdf/2401.15229) but there is perhaps some exhaustion in builders with conversations around responsibility and safety. +The corollary to AI safety that I find more useful is cybersecurity. People don’t argue that investing in cybersecurity is a threat to building the product. Good engineering practices will take care of baseline security considerations. And then there is some more that a team can do in terms of cybersecurity audits. That additional effort will vary for each product depending on the sensitivity of their application. Most teams work with some kind of a Pareto principle- they do things that take care of 80% of the cybersecurity risks. + +I think we will also get to a similar pareto principle with safety work in AI applications. After all, hallucinations are a user experience and a safety issue. Good design will also result in safer applications. We’ve seen a couple of design practices in the more mature AI chatbots, motivated not by safety, but that also help it: + +* Restriction on the length of the conversation: + * Many use cases rely on a strict knowledge base and don’t require lengthy back and forth. Restriction on the number of turns of the conversations helps keep costs in check. But it also helps with safety since guardrails get looser with lengthier conversations. + +* An LLM as a judge that reviews the output of the primary foundational model, against the user input: + * This helps in preventing conversation from drift away from the main purpose of the bot. This keeps costs in check. It also prevents malicious use cases. + +* Observability stack\! + * This seems basic but many applications don’t have one. An application developer needs to know the answers for specific inputs, to understand if the application is working as intended. Observability is necessary for any model or product level evaluation, but also safety assessment. + +The specific design details vary across organizations but all this to say that we can get a decent distance in building safe applications doing a few things right. There are immediate safety concerns with AI applications that can and should be fixed. In fact, there are existing resources for data governance and consent in international development, which if adhered to, will help in safer AI applications. Organizations, social impact or not, should be investing in good design and safety practices. + + +As for the longer term risks, I think these will be better handled by people who are a couple of steps removed from product development in the social sector. These are groups who have one eye to the latest in R\&D of foundational models, and one eye to the diversity of global contexts in which AI is being deployed. Some of the long term risks will eventually become immediate risks. This group can be the bridge relaying feedback to foundational model companies, and ideating on possible solutions for the social sector. diff --git a/src/blog/a-new-thing-under-the-sun.mdx b/src/blog/a-new-thing-under-the-sun.mdx index 9c11af44..4c989c84 100644 --- a/src/blog/a-new-thing-under-the-sun.mdx +++ b/src/blog/a-new-thing-under-the-sun.mdx @@ -10,8 +10,9 @@ cover: ../images/cover-civic-tech.png *This is adapted from the keynote address, Tarunima Prabhakar gave at the annual symposium organized by the Australian Research Council Center of Excellence on Automated Decision Making and Society on July 13-15, 2023. It hasn't been edited for correctness and may contain (several) grammatical errors.* +
- +
I am the co-founder and research lead of Tattle- a civic tech organization in India. We build tools and datasets to respond to inaccurate and harmful content in India. If researchers in the room are surprised why I am here, you are not alone- I was also surprised when I received the invitation. Tattle started as a small side research project in 2018: not too long back parents were chiding teenagers for staring at a screen all the time. But by 2018, they were on a mobile screen all the time, and in India they were predominantly using one app- WhatsApp. The number of WhatsApp users dovetailed, and continues to dovetail the number of Internet users in India. The WhatsApp groups- be these family, friends or alumni groups- would be raging with opinions and fierce debates. Having tried on an ad hoc basis to intervene in these conversations, we had given up. It wasn’t clear to me how one could even intervene. The culpability of a platform in content going viral, when there was no algorithmic amplification wasn’t clear. It was the most popular platform in the Global South, and in my view less understood than Facebook, Twitter or Youtube. Twitter is heavily researched due to its API, and Facebook has faced intense media scrutiny in the post Brexit/Trump moment. It wasn’t and isn't yet fully clear how one would go about understanding information flows on a closed messaging app. So we said, let’s start archiving what we are seeing on our WhatsApp groups. It could provide some answers- for example, what of this local content was viral enough to get caught by the fact checking community in India, or what made its way to social media platforms? This was a side project. Both me, and my co-founder had full-time jobs. We made it open source to allow for other like-minded individuals to join, or take it over if we ran out of steam. My full time job was as a researcher studying how predictive analytics and machine learning were being deployed towards development goals such as financial inclusion in India. @@ -49,7 +50,7 @@ In all these cases, the side you pick has more to do with how much you trust the I grew up with in the heyday of the Internet. Internet felt like the best thing on Earth- the internet was about the small people talking to each other, building with each other. That vision was naive. But this current picture, where for all our problems on the internet our best bet is to plead platforms or governments, both of which have good incentives not to, feels like swinging to the other end. It is a rather disempowering position. The designers of platforms understand that we are dealing with socio-technical systems. In our critiques we will call them out as such. So, civic engagement should also be socio-technical. Civic tech isn’t about reducing the burden of action from platforms and governments. It is precisely the opposite- it is citizens using technology to understand these powerful entities better and holding them to account. It is also a reflexive exercise because by building socio-technical platforms one is committing to enter the playing field whose rules we are trying to alter. But, we enter the playing field not as a player determined to score a goal but rather as a sensor, like a buoy on the ocean, that is describing how proposals to change the rules is changing things on the inside. -Let me explain what I mean by this, through a case study: +**Let me explain what I mean by this, through a case study:** At some point in 2020 we had about 1.6 TB worth of images and videos from platforms in India. We were figuring out how to open this responsibly. We couldn’t possibly manually comb through this data. This data mirrored the content that circulated on social media and chat apps in India- there were selfies, funny memes, conspiracy theories and some outright hateful content. These are not exclusive categories. Conspiracy theories can intersect with hateful content in creative pieces of speculative fiction; and memes can cleverly use humour to build narratives. In deciding what data we should or should not release, we were encountering the same content-moderation questions that a social media platform faces. We started looking for off-the-shelf, open-source solutions to help us flag abusive posts in the data. We knew that content moderation tools disproportionately focused on English, but trying to create a mild content moderation layer made us realize just how dire the situation was. Big-tech platforms have been called out for not devoting adequate attention to non-English speaking populations but it isn’t just big tech that needs some layer of content moderation. Email service provides, internal workplace fora also use spam filters, often those developed and opened by researchers or small teams, to flag harmful content. But a basic list of slur or abusive words was not available in most Indian languages. Ironically, at some point a CEO of an Indian social media platform backed by a VCs, complained about this on social media- there weren’t off the shelf tools that could enable them to build a responsible social media platform that could compete with global (i.e American) ones. @@ -69,7 +70,7 @@ That work is situated and accounts for the context in which it is deployed. Armed with these guidelines, we set about building own machine learning driven moderation tool. But what we realized very quickly is that at every step of the way of building a machine learning system, these guidelines need to be interpreted. And that there are multiple interpretations possible. -At a higher process level, this is how we interpreted these principles: +**At a higher process level, this is how we interpreted these principles:** 1. Our core project team comprised of feminist scholars who spoke the three languages we started working with. Ambika Tandon who is the author of one of the feminist principles for AI I shared earlier and I shared the project management responsibilities. 2. We decided that what constitutes abuse should be determined by those at the receiving end of abuse and not by an average social media user. diff --git a/src/blog/add-react-components-to-a-phoenix-app.mdx b/src/blog/add-react-components-to-a-phoenix-app.mdx index 4abe0ed1..0f853080 100644 --- a/src/blog/add-react-components-to-a-phoenix-app.mdx +++ b/src/blog/add-react-components-to-a-phoenix-app.mdx @@ -8,7 +8,7 @@ tags: devlog cover: ../images/react-component-in-phoenix.png --- -# Problem : +## Problem : - We need SOME client side interactivity - Our team has frontend developers who know react @@ -18,9 +18,9 @@ cover: ../images/react-component-in-phoenix.png ![](../images/react-component-in-phoenix.png) -# Solution : +## Solution : -## Insert the following heex markup in your liveview code +### Insert the following heex markup in your liveview code ```heex
@@ -28,7 +28,7 @@ cover: ../images/react-component-in-phoenix.png This is what I meant by keeping the API consistent with how you would use a Phoenix Component using Phoenix Hooks. -## Create a file for your React Component +### Create a file for your React Component assets/js/counter.js ```jsx @@ -94,13 +94,13 @@ export var CounterHook = { ``` -Some Notable points about the Hook : +**Some Notable points about the Hook :** 1. In the mounted function we use client side javascript to parse attributes, format them and pass them as props to a React Component. 2. We mandate two props onReceive and send to be passed to every React Component so that it can communicate directly with the liveview process by sending and receiving events. 3. Sending an id prop, although its not enforced anywhere yet. This can be useful when you have multiple instances of the same component and you want to send an event from the server to any particular one of them. -Some Notable points about the React Component +**Some Notable points about the React Component** 1. Its familiar to react developers 2. We continue using tailwind for styling. This is a big win because I value being able to style my components in accordance with the rest of our app; so since we already use tailwind for the rest of the UI components, this is great. @@ -114,7 +114,7 @@ This looks a bit like the equivalent we have grown used to in heex. ``` 4. We use useEffect() to setup listeners for server events. -## Add an event handler in your liveview +### Add an event handler in your liveview ```ex def handle_event("count-update", params, socket) do @@ -131,7 +131,7 @@ def handle_event("count-update", params, socket) do end ``` -## Register the hook in app.js +### Register the hook in app.js This should be familiar to anyone who has used phoenix hooks. @@ -150,7 +150,7 @@ let liveSocket = new LiveSocket("/live", Socket, { }); ``` -## Change esbuild configuration to support JSX +### Change esbuild configuration to support JSX This part is new. In your config.exs, you need to add the loader flag - --loader:.js=jsx to the args option. The full value should look like ```ex diff --git a/src/blog/archive-ml.mdx b/src/blog/archive-ml.mdx index 161e8c13..1226153f 100644 --- a/src/blog/archive-ml.mdx +++ b/src/blog/archive-ml.mdx @@ -14,7 +14,9 @@ When archives get digitized, they result in a database that can be analyzed comp In prescribing lessons for the discipline of machine learning from archiving, Jo and Gebru write:\ "Data collection in significant ML subfields...is indiscriminate. Curatorial archives lie on the other extreme of the intervention spectrum." But to draw this comparison is to say that there is a continuum, from the databases that feed machine learning to the archives with considered collection and curation: +

[https://dl.acm.org/doi/pdf/10.1145/3351095.3372829](https://dl.acm.org/doi/pdf/10.1145/3351095.3372829) +

It is also important to remember that not all archives are curated. For example, the Cairo Genizah came about as a result of "Rabbinic prohibition that a religious text cannot be aimlessly discarded, but must be carefully stowed away". [Limn: A Hoard of Hebrew MSS](https://limn.it/articles/a-hoard-of-hebrew-mss/) diff --git a/src/blog/considerations-in-archiving-misinformation-fromencrypted-messaging-apps.mdx b/src/blog/considerations-in-archiving-misinformation-fromencrypted-messaging-apps.mdx index 33b0f163..8f4f66b0 100644 --- a/src/blog/considerations-in-archiving-misinformation-fromencrypted-messaging-apps.mdx +++ b/src/blog/considerations-in-archiving-misinformation-fromencrypted-messaging-apps.mdx @@ -1,7 +1,7 @@ --- name: Considerations in Archiving Content from Encrypted Messaging Apps excerpt: This blog discusses some of the challenges faced, and related design decisions that Tattle has taken in implementing one of it’s goals- the creation of a globally accessible archive of multi-media messages circulated on chat apps. -author: Tarunima +author: Tarunima Prabhakar project: "" date: 2019-07-18 tags: archives, web-scraping, bts @@ -18,7 +18,7 @@ Tattle is a civic tech project emanating from India that aims to make verified i This blog discusses some of the challenges faced, and related design decisions that Tattle has taken in implementing one of it’s goals- the creation of a globally accessible archive of multi-media messages circulated on chat apps. -## The Curious Case of WhatsApp +### The Curious Case of WhatsApp In India, WhatsApp’s rising popularity has mirrored a rapid increase in mobile phone and Internet penetration [2]. The ease of creating and sharing audio-visual content has made the platform accessible even to users with minimal traditional and digital literacy. @@ -30,7 +30,7 @@ These aspects make content discovery and tracing the source of origin of a parti An archive of content circulated on WhatsApp, and associated metadata such as time of receiving the message, addresses some of these issues. For fact checkers, it provides a database against which they can verify incoming content. For researchers, it is a source of content and temporal analysis.  But an archive is not neutral. It exerts power in what it chooses to include and exclude. It also exerts power through its mode of management and rules of access. -## Data Acquisition: The Greedy Approach +#### Data Acquisition: The Greedy Approach Over the last two years, several researchers and fact checking groups across the world have collected content circulated on WhatsApp for different purposes. While most researchers have focused on scraping public WhatsApp groups[3,4], fact checking groups source content from a number of channels, including emails and fact checking helplines run over WhatsApp[5]. @@ -38,14 +38,13 @@ The lynchings in India highlight the urgency of timely detection and response. W ## Open Challenges -### Ethics of Data Collection from Public WhatsApp Groups +#### Ethics of Data Collection from Public WhatsApp Groups At present there are no guidelines for ethical data collection on WhatsApp. Every research group has self-defined a configuration of consent framework and data sharing practices. For example, Narayanan et al.[3] chose to declare their presence and intent in every public group they joined and as a result were removed from some of them. Garimella et al.[4] on the other hand did not declare their presence and intent but have not made all of their data public. At Tattle, we do not declare our intent when we join public WhatsApp groups. Our working assumption is that public WhatsApp groups are an exception to otherwise private communication on the platform and are made public with the explicit intent of being discoverable. This is an assumption that we will revisit as the project evolves. -### -Flagging Problematic Content +### Flagging Problematic Content Not all collected data can be opened to the public. A casual google search for public WhatsApp groups results in lists of ‘Adult’ WhatsApp groups [6]. In greedy scraping of WhatsApp groups, we often encounter pornographic and violent content. For example, groups launched under a popular theme of ‘Jobs’ are often appropriated for pornographic content. Such content cannot be shared on a public archive and must be flagged soon after collection. @@ -53,7 +52,7 @@ Owing to the volume of data scraped, Tattle will use machine learning based appr How best to incorporate the preferences of multiple stakeholders in what should or should not be opened, is a question that Tattle as any open archive must address. -### Unwanted spotlight on previously obscure content [7] +#### Unwanted spotlight on previously obscure content [7] With an online open access archive it is difficult to predict or control the ways in which the data will be used. One concern is that by surfacing content circulating in different geographies, the archive will become a source for ill-intended content creators, confounding the problem the archive is directed to solve. @@ -63,8 +62,7 @@ Misinformation on WhatsApp is often hyperlocal[8].  While local fact checkers a We recognize that while not all data collected can be made public, it may still be useful for research. Preserving some data for restricted access has practical applications, though it raises questions around how these rights are managed. Aronson notes, “…the ethics of providing access to archival materials is a ‘thorny problem.’ It is not a one and done policy decision, instead requires constant deliberation and negotiation.”[10] -## -Something New Under the Sun? +### Something New Under the Sun? Several of the concerns pertinent to Tattle’s archival activities have been discussed in different forms in disciplines spanning media studies, archival sciences, information studies and anthropology. Yet, it is in the interaction of a multi-lingual, mixed media content; encrypted networks and coordinated disinformation campaigns that the uniqueness of archiving content from chat apps emerges. @@ -76,6 +74,8 @@ We would love any inputs on navigating the issues raised in this blog. Also do l ## References +

+ [1] De, Anamitra. "Can We Trust Facebook to Keep Our "Digital Living Rooms" Safe From Liars, Racists, and Haters? | Omidyar Network." Omidyar Network Blog. March 11, 2019. Accessed June 01, 2019. https://www.omidyar.com/blog/can-we-trust-facebook-keep-our-digital-living-rooms-safe-liars-racists-and-haters. --- @@ -113,3 +113,4 @@ We would love any inputs on navigating the issues raised in this blog. Also do l --- [10] Aronson, Jay D. (2017) “Preserving Human Rights Media” Genocide Studies and Prevention: An International Journal: Vol. 11: Iss. 1: 82-99. Accessed May 31, 2019. [https://scholarcommons.usf.edu/gsp/vol11/iss1/9/](https://scholarcommons.usf.edu/gsp/vol11/iss1/9/). +

\ No newline at end of file diff --git a/src/blog/factshala-project-reading-list.mdx b/src/blog/factshala-project-reading-list.mdx index 9d08cc2f..a1ec4bed 100644 --- a/src/blog/factshala-project-reading-list.mdx +++ b/src/blog/factshala-project-reading-list.mdx @@ -7,7 +7,7 @@ date: 2023-07-10 tags: media-literacy --- -## Course Work and Curriculum on Media Literacy in India +### Course Work and Curriculum on Media Literacy in India Our research began with a clear focus: identifying practical skills that our game could impart to school students. Initially, we delved into exploring the current curriculum and coursework on media literacy being implemented in Indian classrooms. While we recognized the importance of teaching skills such as critical thinking and bias identification, we sought to understand the specific methods through which these skills were being taught. Our aim was to uncover coursework and exercises that effectively imparted media literacy skills in a practical manner, with the intention of incorporating and teaching these skills through our game. @@ -15,7 +15,7 @@ Our research began with a clear focus: identifying practical skills that our gam - [This](https://www.youtube.com/watch?v=LNV6FvevzrY), [this](https://www.youtube.com/watch?v=YFUPrJVrTto), [this](https://www.youtube.com/watch?v=9sWXpLmtq-I) and [this](https://www.youtube.com/watch?v=29HPohQE0pc) are all mock classroom videos designed under Kerala state government's Satyamev Jayate media literacy programme - UNESCO's resource [Think Critically, Click Wisely](https://www.unesco.org/en/articles/media-and-information-literate-citizens-think-critically-click-wisely) -## Repositories of Content from Outside India +### Repositories of Content from Outside India Keeping in mind the relatively low diffusion of media literacy in existing curricula in India, we also had a look at learning tools and frameworks from outside India geared towards imparting media literacy and critical thinking skills to students of all ages @@ -23,7 +23,7 @@ Keeping in mind the relatively low diffusion of media literacy in existing curri - The University of Newcastle Australia's [course](https://www.futurelearn.com/courses/critical-media-literacy) on critical media literacy and culture and how to actually apply and practice analytical frameworks to assessing texts, situations and events - Media Savvy Citizens' [take](https://www.mediasavvycitizens.com/highlights) on imparting the same set of skills but to educators and teachers so that they can confidently teach in an ever-evolving digital landscape -## First Principles: Starting from Scratch +### First Principles: Starting from Scratch Training educators, whether it be internally driven or external, is critical to improving the level of education received when we talk about teaching media literacy. Such skills can be those dedicated to generating awareness on issues like diversity and inclusion, leadership and collaboration, and socio-emotional learning. The following are learning platforms that are aimed towards inculcating the same in educators: diff --git a/src/blog/feluda-recognised-digital-public-good-dpga.mdx b/src/blog/feluda-recognised-digital-public-good-dpga.mdx index cf6d7f07..fc131ba0 100644 --- a/src/blog/feluda-recognised-digital-public-good-dpga.mdx +++ b/src/blog/feluda-recognised-digital-public-good-dpga.mdx @@ -17,5 +17,8 @@ In a world where information pollution knows no borders, the DPGA and UNDP advoc By promoting digital public goods, the DPGA aims to empower societies worldwide in their fight against misinformation, fostering transparency, collaboration, and adaptability to meet diverse needs. Feluda's recognition as a DPG exemplifies its potential to significantly impact the global fight against information pollution, guided by the principles of openness and collaboration. ## Further Reading Resources + +

* https://digitalpublicgoods.net/information-pollution/ * https://tattle.co.in/products/kosh +

\ No newline at end of file diff --git a/src/blog/handpicked-issues.mdx b/src/blog/handpicked-issues.mdx index 4f1fe35e..2d49b53b 100644 --- a/src/blog/handpicked-issues.mdx +++ b/src/blog/handpicked-issues.mdx @@ -16,11 +16,13 @@ These interactions left us motivated to dig into our bag of problem statements a We want to share 3 high impact issues we have identified by talking to [RATI foundation](https://ratifoundation.org/) and the [Deepfake Analysis Unit](https://www.dau.mcaindia.in/). These issues are about harms caused by non consensual sexual imagery and audio deepfakes. While it would be challenging to work on them alone, they all would gain so much from collaborative community participation. We've scoped these issues out into Github Issues so we can discuss approaches and work in the open to solve them. +
| | | | | | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | |
![Icon](../images/handpicked-issues/ic_orange.png)
| Create a benchmark dataset for Audio Deepfakes | [tattle-made/feluda#365](https://github.com/tattle-made/feluda/issues/365 "Create a benchmark dataset of Audio Deepfakes") | Web Scraping, ML, REST API, Datasets, Annotation | |
![Icon](../images/handpicked-issues/ic_banana.png)
| Retrieve portions of non consensual sexual imagery in a video | [tattle-made/feluda#365](https://github.com/tattle-made/feluda/issues/364 "Retrieve portions containing non consensual sexual imagery in a video") | Computer Vision, ML, Annotation | |
![Icon](../images/handpicked-issues/ic_strawberry.png)
| Repair Instaloader to scrape posts on Instagram by hashtag \`#web-scraping | [instaloader/instaloader#2206](https://github.com/instaloader/instaloader/issues/2206 "hastag.get_posts() doesn't seem to be working anymore") | Web Scraping, Reverse Engineering APIs | +
## The Bigger Picture diff --git a/src/blog/hot-take-on-open-source-AI.mdx b/src/blog/hot-take-on-open-source-AI.mdx new file mode 100644 index 00000000..e8b31f87 --- /dev/null +++ b/src/blog/hot-take-on-open-source-AI.mdx @@ -0,0 +1,56 @@ +--- +name: A Hot Take on Open Source and AI +excerpt: Musings on Open Source Software, AI, DPGs +author: Tarunima Prabhakar +project: "" +date: 2025-09-15 +tags: open souce AI, tech, policy +--- +Reference [[make-it-real]] + +Over the last few months I've been in a number of conversations with business executives, media and policy professionals and researchers, who in the sincerest of intentions have said something like "but what even is open source AI" or the scarier version "oh but we don’t have a definition of open source". I think people intend for these statements to be provocations to start a conversation but, in my humble opinion, these become more like confounders. + +Let me explain my frustration using the metaphor of a chilli. Yes, that ordinary ingredient used in most kitchens across the world. + +![Many Hot Chilli Peppers](../images/Madame_Jeanette_and_other_chillies.jpg "Many Hot Chilli Peppers") +By [Takeaway](https://tattle.co.in/blog/hot-take-on-open-source-AI/), licensed under CC BY-SA 4.0 via Wikimedia Commons + +Anyone who spends time in a kitchen- most mothers, some fathers, the born-in-pandemic amateur chefs and botanists know how to identify a chilli. If we want to get technical, it belongs to the [genus Capsicum](https://en.wikipedia.org/wiki/Chili_pepper) and is eaten raw, dried, fried, crushed. It is one versatile little thing that packs a punch in a dish. There are a bunch of dishes- Mexican Chilli, Chilli Chicken, Honey Chilli potato- where chilli, strictly by volume or weight, isn’t the main ingredient. But chilli is added as an adjective because it adds something unique and important to the dish. Chilli is so important that its influence has spilled far outside the culinary stage. [Iconic rock bands](https://en.wikipedia.org/wiki/Red_Hot_Chili_Peppers) are named after it. Restaurants ranging from [American fast food chains](https://en.wikipedia.org/wiki/Chili%27s) to small dhabas in India fight over calling themselves Chilli. Whether their food uses chilli or black pepper as the primary spice is a different matter. It has spun off crazy subcultures of [chilli eating competitions](https://en.wikipedia.org/wiki/Hot_pepper_challenge) that serve no functional purpose. None of this, however, confuses anybody about the organic edible thing called chilli. No one is going into malls buying a plastic toy in the shape of a chilli, mistaking it for the real thing, and adding it to their food. + +There are a lot of other kinds of plants such as white pepper, black pepper and ginger that can be added to spice food. Sometimes the black pepper growers association might feel envious of the popularity of chilli and crash the chilli growers association conference and scream, “But we do exactly what you do. We are also a great spice\!” This doesn’t make black pepper a chilli. A plant must contain capsaicinoids to be called a chilli. Sorry black pepper, you just ain’t chilli. + +Now in the crazed decade that is the 2020s, an evil/mad/genius food manufacturer- let’s call it X for simplicity- produces a new range of hot sauces. X pushes these sauces to Michelin star restaurants and grocery stores alike, promising a culinary revolution. The sauce never spoils, it is cheaper than your spices and you can forget your worries about salting your food right. The sauce with its special adaptive properties will figure out exactly how much saltiness to express. So says X. + +X does an aggressive push for their chilli sauce to all the kitchens doling out Mexican Chilli, Honey Chilli Potato and Chilli Chicken. But the chefs in this kitchen are circumspect. They don’t know the exact ingredients of the sauce but the professionals have probed enough to know that the sauce also contains some sichuan pepper, ginger, black pepper and other non-spicy things like salt and sugar. Given all these other ingredients, can they use this magical sauce and advertise their dishes with the word chilli in it? They are hesitant to buy it. So X pushes the loose association of botanists to come up with rules against which sauce can be labeled chilli sauce. They, after all, classified plants just okay. Is it a chilli sauce if 20% of the ingredients are chilli? 30%? + +In the meantime, the mothers, some fathers and the born-in-pandemic amateur chefs merrily go about cooking in their respective kitchen knowing exactly how to identify a chilli. Some of them give the sauce a try but trust their own expertise over X’s marketing spiel and mix it with other spices- ultimately they just care about a tasty and healthy meal. Some purists eschew the sauce altogether. + +Now switch out: + +* Chilli for open source Software +* Spice for software +* Sauce for AI + +And I think we get a pretty good picture of what is happening with open source and AI. + +After that spicy background, I offer four hot takes: + +1. The definition of open source software is fairly settled. Any classification exercise, be it of a plant, software or AI, is a heavily social process. There are power dynamics in whose voices count in naming something[^1]. What happens to objects on the boundaries of classes can be arbitrary (remember when Pluto was still a planet?). But time becomes a good test of any classification system and with open source software, we’ve had two decades. The definition is pretty clear. Just as chilli is identifiable by some chemical properties (presence of capsaicin), open source software is identifiable by legally testable properties. +None of us working with FOSS have any doubt about how to identify free and open source software. Even when the black pepper folks (DPGs) say that they are brethren of chilli (open source software), it is easy to factually check this. Even if the success of open source software has inspired domains outside of software, the ‘vibe’ of openness is tied to material practices in software. There is a checklist of the four freedoms- to use, study, modify, distribute software- that software licenses must meet. + +2. This is a relatively minor point- the creation of a derivative product shouldn’t be pulling the legitimacy of an original ingredient into question. Just because DPG/black pepper is gaining popularity (no less because chilli made spices popular), and we now have AI/sauces doesn’t mean we need to be confused about what open source software/chilli is. + +3. Just as the creation of sauce involves a bunch of chemical processes (glycation, acidulation) where ingredients bond in a way that makes it challenging to talk about properties coming from constituent ingredients, so it is with AI. AI is a new kind of technical artifact. This whole affair of defining open source AI started because corporations were using ‘open source’ as a marketing push for AI. There was a fair bit of discussion and deliberation that happened in the process of [OSI coming up with OSAID](https://discuss.opensource.org/t/the-open-source-ish-ai-definition-osaid/580/6). That definition might not last but we are far beyond the point of “what even is open source AI.” By and large, everyone recognizes that it won’t be as easy to certify AI products against open source licenses as it is to certify software, because AI is software and data amalgamated in a way that makes it hard to separate the contribution of either. More importantly, arguing about the chilli quotient of a sauce might not be the most productive use of our time, when all other aspects of the other production process are also concerning. The content and quality of other ingredients matters. The process and quality of the facility in which the sauce was made also matters. With AI, all aspects of the production process need investigation. This is why I think that the question of “what even is open source AI?” is a distraction or a confounder. If Meta or Anthropic trains their models on books and papers scraped from Libgen but then opens all the data and weights and code for LLama, they would have met all aspects of their model open. But should we celebrate this as a success? + The open source software definition was supposed to be a mental shortcut for four tangible things that a piece of code enables. It is clear that an open source AI definition won’t map onto a similarly simple check list of four freedoms because data and compute are significantly expensive for any person to exercise the four freedoms with AI. I think people are right to watch the licenses that models are released with, with hawk eyes. But, this won't be enough to ensure autonomy to tinker and innovate, which is what the four freedoms enable. FOSS communities need to get more involved in conversations of data governance (beyond open data) and politics of hardware supply chains. All of that, at present, seems more important than splitting hair over what should constitute an Open Source AI definition. + +4. And last, after all this confusion, maybe we should stop invoking ‘open source’ as a synonym of ‘good’. People use open source as an adjective for digital IDs, governments, infrastructures, charity... Just as the band Red Hot Chilli Pepper shares little in common with the plant outside of the name, many of these concepts that might be inspired by FOSS share little in common with FOSS software. The material difference between government and software, money and software, IDs and software matters. Just applying the suffix ‘open’ to big concepts doesn’t make them all unanimously good. This also holds for AI. + +It is worth stating that the most egregious misuses of the open source language don’t (usually) come from FOSS practitioners. But a narrative battle around open source has been underway for a while, and we need to take some responsibility for not tackling it with more force. Perhaps we were so used to fighting like [the underdogs](https://en.wikipedia.org/wiki/Microsoft_and_open_source) that we took every adaptation of the language, however loose, to be a win. Or perhaps if we had taken more effort to build public understanding of OSS, a narrative spin would have been less successful. To go back to the chilli metaphor, imagine an alternate world where only some people could distinguish a chilli from sichuan pepper, but the average population understanding stayed at the level of spice. And so when black pepper or sichuan pepper sellers said they are the same as chilli, people had no reason to doubt it. After all, the chilli sellers hadn’t said anything earlier. + +FOSS practitioners (and the general public) are caught in a maelstrom of jargon. The one thing we need is to defend and steady the turf of open source software- the definition of open source software is not contested and we need to make this point as loudly and as frequently as possible, and in forums outside our FOSS cliques. We need to up our game as public communicators to bring back the precision of language used to describe technical objects. In chasing that precision we will automatically find ourselves beyond the boundary of open source AI, examining the properties of all the constituents that come together to become AI. + +There is also a provocation for the ‘tech for good’ and policy folks (in the odd chance that anyone gets this far)- can we challenge ourselves to write and talk about national infrastructures and digital sovereignty in the age of AI without using the terms Digital Public Goods and Open Source AI? This one little change alone could do a lot to improve the quality of the conversation. + +_A special thanks to Denny George and Srravya Chandramowli for revieweing an earlier, less hinged version of this blog._ + +[^1]: In this context, Srravya Chandramowli recommended the book: [Sorting Things Out](https://mitpress.mit.edu/9780262522953/sorting-things-out/) diff --git a/src/blog/how-to-set-a-playtest.mdx b/src/blog/how-to-set-a-playtest.mdx index 855154fd..de5f5843 100644 --- a/src/blog/how-to-set-a-playtest.mdx +++ b/src/blog/how-to-set-a-playtest.mdx @@ -15,7 +15,7 @@ Well, “read through it” is an overstatement. I glanced at the set-up instruc So, what does it take to learn a new game? A 30-page-long PDF, a foreign language, an entire weekend? Ideally not. All it should take is a well-worded rulebook, or a game master who knows what she’s doing. This was the first problem I tried to solve when I joined the Viral Spiral team: What is the best way to teach people this game? -## Winner Winner Chicken Dinner +### Winner Winner Chicken Dinner In my experience, people have amazingly short attention spans. I once spent forty minutes explaining the complicated ins and outs of ‘Scythe’ (an engine-building game from Stonemaier) to a table of four. After I was done, one of the guys around the table scratched his head and asked me, “Wait, so how do I win?” @@ -35,7 +35,7 @@ Tired: “So the cards have biases and affinities. And you belong to different c Wired: “You start your turn by drawing a card from the deck. You can then choose to keep the card to yourself or share it with someone around the table, which will score you a point.” -- “Why wouldn’t I share a card if it’ll score me a point?” -- “Well, you see, some cards have biases and affinities…” -## No Such Thing as a Self-Explanatory Rule +### No Such Thing as a Self-Explanatory Rule After I became well-acquainted with the rules of Catan, I once tried explaining it to a friend. I thought I did a pretty good job, so you can imagine my surprise when, in her turn, she tried building a road extending out of a port. “No!” I said, “You can only build a road along a hexagonal tile.” diff --git a/src/blog/make-it-real.mdx b/src/blog/make-it-real.mdx new file mode 100644 index 00000000..c8582742 --- /dev/null +++ b/src/blog/make-it-real.mdx @@ -0,0 +1,50 @@ +--- +name: Make It Real - Mapping AI-Facilitated Gendered Harm +excerpt: Launch announcement for report written with RATI Foundation +author: Tarunima, Siddharth, Kaustubha +project: "" +date: 2025-11-03 +tags: announcement, synthetic-media, online-harms +cover: ../images/cover-rati-report-make-it-real.png +--- +import {Box, Text} from 'grommet' +import {ExternalLink} from "../components/atomic/TattleLinks" + +![](../images/hero-rati-report-make-it-real.png) + +Today Tattle and Rati are releasing a report, Make it Real, a report that examines how AI-generated content, popularly known as ‘deepfakes’, is impacting and reshaping online harassment. + +Drawing from cases reported to Rati's helpline Meri Trustline, the report reveals a concerning trend: while media & headlines often center on celebrities and politicians targeted through AI, a more personal crisis is also unfolding. Ordinary survivors are being targeted through images that are artificially generated but possess the capacity for real harm. Survivors’ reputation is attacked and consent is erased through technology. These violations are muted by shame, fear and trauma- the incidents are rarely revealed to close family circles, let alone feature in larger discourse. + +This report is based on the courageous calls that some survivors made to The Trustline. The salient findings of the report are: + +- **Majority of Abusive Digital Manipulation Is AI-Generated.** Digital manipulation existed long before AI through manual edits, Photoshop or crude alterations but AI has transformed its speed and realism. Today, the majority of manipulated content reported shows some form of AI-generated or AI-enhanced imagery. + +- **AI Creates Access & Violation Where No Offline Contact and Consent Exist.** +In the majority of cases involving AI-generated sexual content, the perpetrator and target had no prior connection. AI was used precisely because the abuser lacked real-world access to the victim’s private images. + +- **AI Amplifies Misogyny placing Women and Marginalized Genders at Greater Risk.** +While digitally manipulated content is also disproportionately targeted towards gender minorities (72% of the cases were targeted towards women), nearly all cases (92%) involving AI were targeted towards women. + +- **Platform Safety Systems as Barriers, Not Safeguards.** +The biggest obstacle in securing a conducive response to a safety issue is the overall reporting system provided by a platform. On platforms where reporting any content is difficult such as X, reporting AI-generated content is also difficult. Platforms that define harm more “broadly”are more likely to address AI-generated content. Meme-like content, sitting in platform policy grey zone, continues to thrive even with AI-generated content. + +- **Copyright as a Personal Safety Tool.** +Reporting the content under the DMCA for copyright infringement has often proven to be more effective in taking down offending content than framing and reporting the abuse under the category of gender-based harm. + +- **Law Is Not the Gap. Access to Justice Is.** +Existing legal architecture in India can account for the risks of AI generated content. The key barriers are in applying existing legal provisions to gain recourse. There is a need to build the capacity of personnel across justice and enforcement systems to recognize and respond to manipulated content, in ways that are scientific, sensitive and clear of victim-blaming narratives. + +This report aims to humanize these less visible experiences of AI Generated online abuse and provide new evidence on how the emerging technology is shaping digital vulnerabilities. + + +If you are being harassed online or know someone who is, reach out and seek support. Call or WhatsApp Meri Trustline - 6363176363 Monday to Friday 9am to 5pm. + + + + + + Read Report + + + diff --git a/src/blog/misinfocon-india-working-groups.mdx b/src/blog/misinfocon-india-working-groups.mdx index ee991963..96f795d6 100644 --- a/src/blog/misinfocon-india-working-groups.mdx +++ b/src/blog/misinfocon-india-working-groups.mdx @@ -12,7 +12,7 @@ cover: ../images/cover-misinfocon-wg.png These were the broad topics or working groups that people in the gathering converged on, on day 3: -# Zombie Claims +## Zombie Claims - Research how Zombie Misinformation functions, what it means, how it works and why one time debunking does not work. - Research wikipedia as a source for zombie misinformation @@ -25,7 +25,7 @@ These were the broad topics or working groups that people in the gathering conve - Tell platforms that they can use these systems - Make this tech available for big and small platforms -# Reduce Debunking Latency +### Reduce Debunking Latency - Work with Meedan’s check tipline to tackle this - Create popup news rooms around specific events @@ -35,21 +35,21 @@ These were the broad topics or working groups that people in the gathering conve - Make this data available to different audiences (newsroom, individuals) - Solve for claims reappearing in multiple languages by pre-translating every factcheck -# Platform Failure +## Platform Failure - Explore whether self regulation can work. If not then how do you fix it with regulation? - Compile the tech solutions that we should advocate for. - Study How do people exploit anonymity to spread misinformation? Can we rely on algorithmic mechanisms for reducing virality from anonymous sources/ platforms? - Think about tactics that leverage regulatory arbitrage (across geographies). -# Liberation from Big tech Platforms +### Liberation from Big tech Platforms - Create a data donation service - Create a traceable archive for communities - Archive for fact checked content (?) - Build a tool that lets you post on short video platforms while also keeping local archives of content produced. -# Producing Content +## Producing Content - Create a Narrative show that shows how information or misinformation is a part of our daily family life. Beyond media literacy that is framed as wrong action. @@ -60,18 +60,18 @@ These were the broad topics or working groups that people in the gathering conve - Ideate on Digital storytelling for fact checked content beyond long-form text storytelling - Provide a digest to creators on what topics to tackle -# Increasing reach of Fact Checks +### Increasing reach of Fact Checks - SEO training - Study and implement UI tweaks that work for different demographics -# Source Tracking +## Source Tracking - Think about citational justice in terms of wikipedia - Can you use a metadata tag/ poisoning to track networks. - Libraries and archives are thinking of this in context of provenance and AI -# MIA Data +## MIA Data - A crowdsourced data directory - Pre-internet Tata used to create a dataset @@ -79,13 +79,13 @@ These were the broad topics or working groups that people in the gathering conve - See if we can join the factly project - Activating networks for maintaining/cleaning datasets -# Data Void +## Data Void - Assist Meedan's work on gendered disinformation: - Definitions are a problem (one form of a gender based violence) - Identifying gendered disinformation (support on tech for contextual detection). -# Critical Reading +## Critical Reading - Make Media literacy curriculum content (targeted for age groups for critical thinking) - Keeping updated based on research @@ -93,9 +93,9 @@ These were the broad topics or working groups that people in the gathering conve - Tabulating list of existing curriculum - Cataloging ‘misinformation techniques’ (informal logics) -# Social media and archiving +### Social media and archiving - Archiving was a theme that came up in multiple working groups. The notes from the dedicated breakout session on the topic can be found [here](https://docs.google.com/document/d/1JwqgALWCH4oKAc0HoTztleW8eWDtL9RK01ul5RX0BHI/edit#heading=h.4cywptwoc66x). -# More documentation +## More documentation The documentation on the work continuing from MisinfoCon is being maitained here: [https://tattle-made.github.io/misinfocon-india-docs/working-groups](https://tattle-made.github.io/misinfocon-india-docs/working-groups) diff --git a/src/blog/mitigating-harms-of-synthetic-media.mdx b/src/blog/mitigating-harms-of-synthetic-media.mdx index 3eff131e..cc49f0d0 100644 --- a/src/blog/mitigating-harms-of-synthetic-media.mdx +++ b/src/blog/mitigating-harms-of-synthetic-media.mdx @@ -17,11 +17,11 @@ While there are distinctions between deepfakes, shallowfakes, images manufacture The proposed solutions to Synthetic Media related attacks largely fall under these categories : -1. The Advocacy Solutions +### 1. The Advocacy Solutions In these you enforce companies to build guard rails into their tools or stop offering these services. The boat on this might have long sailed. Even if the behemoths in this domain were to implement these safety features to save face, there will always be open source tools and unsigned apks floating around that miscreants will grab a hold of. -2. The Cat and Mouse solutions +### 2. The Cat and Mouse solutions Building [watermarking](https://www.ischool.berkeley.edu/news/2023/hany-farid-watermarking-chatgpt-dall-e-and-other-generative-ais-could-help-protect-against) [tech](https://techcrunch.com/2023/05/23/microsoft-pledges-to-watermark-ai-generated-images-and-videos/) into the generative AI systems so that its possible to detect images generated via AI, building synthetic media detectors, training people to spot synthetic media. These solutions all have a cat and mouse characteristic where it looks like one will be constantly busy building reactive tech or skills to catch up with the new ways generative AI systems become capable of evading detection diff --git a/src/blog/onosaid.mdx b/src/blog/onosaid.mdx index a3ffbafb..a11d59f1 100644 --- a/src/blog/onosaid.mdx +++ b/src/blog/onosaid.mdx @@ -3,7 +3,7 @@ name: On the OSAID Definition excerpt: Some reflections on the Open Source AI definition date: 2024-11-05 tags: responsible-ai -author: Tarunima +author: Tarunima Prabhakar --- I'll start with some belated thoughts on the [Open Source AI definition](https://opensource.org/ai/open-source-ai-definition) that was released last week. At IndiaFOSS in September this year, I spoke about the OSAID and urged people to give feedback on it. I was concerned that people weren't following a very important development closely enough. A version of the talk was [published on the OSI blog](https://opensource.org/blog/data-transparency-in-open-source-ai-protecting-sensitive-datasets) that sparked some discussion on the [OSI forum](https://discuss.opensource.org/t/data-transparency-in-open-source-ai-protecting-sensitive-datasets/588 "Data Transparency in Open Source AI: Protecting Sensitive Datasets"), but this wasn't a conversation that I wanted to casually dip into. I am sharing here, some more considered reflections. diff --git a/src/blog/show-and-tell-sep-24.mdx b/src/blog/show-and-tell-sep-24.mdx index 0d1429e8..25ae6c0c 100644 --- a/src/blog/show-and-tell-sep-24.mdx +++ b/src/blog/show-and-tell-sep-24.mdx @@ -11,7 +11,7 @@ cover: ../images/cover-show-and-tell-sept.png We onboarded four great contributors to Tattle projects as part of the DMP2024 Program organized by Samagra Foundation. -The contributors worked on the following four issues : +**The contributors worked on the following four issues :** 1. [Show Uli Slur Metadata within the browser](https://github.com/tattle-made/Uli/issues/544) 2. [Clustering large number of videos](https://github.com/tattle-made/feluda/issues/81) diff --git a/src/blog/social-media-companies-politics.mdx b/src/blog/social-media-companies-politics.mdx index 0b93e31c..14259077 100644 --- a/src/blog/social-media-companies-politics.mdx +++ b/src/blog/social-media-companies-politics.mdx @@ -19,7 +19,7 @@ What have social media companies and platforms done in response? Besides shallow Such gamified terms of engagement and metric-based (followers, retweets, traction, etc) understandings of "free speech" serve only the vested interests of the social media companies themselves and cement their limited liability stance, where such agents are not merely distributors of information but active purveyors of different forms and types of content. These power centres are not mere hosting platforms but full-blown media corporations that not only host but also curate content as per their will and design. Thus, such platforms facilitate the active participation of the citizenry in the political process through the shaping of public discourse and creating information disorder without any accountability or responsibility. -## Recommendations Made by Speakers +### Recommendations Made by Speakers - The RTI campaign in India began at the grassroots in a village and mobilised upwards and expansively from there towards dissemination and awareness of one's right to information and the responsibilities one has for what one says or does in a democratic process/universe. While parliamentary oversight over social media companies should be there, there should be some regulation and accountability built and driven from the bottom-up, like how the campaign against the three redacted farm laws by the MKSS took place in India. There should be a formation of a strong and broad coalition of all concerned stakeholders and the enactment of a charter on how disinformation can be tackled. diff --git a/src/blog/tattles-take-on-the-indian-telecom-bill.mdx b/src/blog/tattles-take-on-the-indian-telecom-bill.mdx index ea2952d2..ff7658ad 100644 --- a/src/blog/tattles-take-on-the-indian-telecom-bill.mdx +++ b/src/blog/tattles-take-on-the-indian-telecom-bill.mdx @@ -37,9 +37,11 @@ Our submission to the Ministry can be viewed [here](https://drive.google.com/fil More resources on understanding the Bill and its issues and our perspectives on MisInfo are here - +
- https://www.thequint.com/voices/opinion/draft-telecom-bill-2022-what-justifies-surveillance-and-shutdowns-in-india-new-telecom-bill - https://www.medianama.com/2022/10/223-telecom-bill-2022-provisions-create-regulatory-overlap/ - https://internetfreedom.in/the-draft-indian-telecommunication-bill/ - https://twitter.com/tattlemade/status/1309137170926534656 - https://twitter.com/tattlemade/status/1422555494904913926 - https://twitter.com/tattlemade/status/1291744030586355712 +
\ No newline at end of file diff --git a/src/blog/the-need-futility-of-archiving.mdx b/src/blog/the-need-futility-of-archiving.mdx index a5c1638d..1c8bdf21 100644 --- a/src/blog/the-need-futility-of-archiving.mdx +++ b/src/blog/the-need-futility-of-archiving.mdx @@ -7,7 +7,7 @@ date: 2023-01-23 tags: archives, web-scraping --- -This is an open ended reflection on the need and seeming futility of archiving on social media. +**This is an open ended reflection on the need and seeming futility of archiving on social media.** We [started Tattle](https://web.archive.org/web/20200803175133/https://tattle.co.in/) with the goal of making an open and searchable archive of content circulating on chat apps in India. Why? Because we got tired of seeing the same messages being circulated year after year on WhatsApp, as if the message had no context, no history. The same photograph of a [soldier sleeping in a snow-filled trench](https://www.boomlive.in/no-these-are-not-indian-soldiers-at-siachen/) used to refer to soldiers in Ukraine in 2014, and soldiers in Siachen in 2017. diff --git a/src/blog/uli-e2e-testing.mdx b/src/blog/uli-e2e-testing.mdx index 43ad0c42..a27dd068 100644 --- a/src/blog/uli-e2e-testing.mdx +++ b/src/blog/uli-e2e-testing.mdx @@ -11,12 +11,12 @@ This post came about when we set out testing one of our products, Uli, a browser You can learn more about it [here](https://twitter.com/tattlemade/status/1570401058073219073?t=SlMhPup1_jEYPAtLykr-GQ&s=19). In this post, we'll go through some of the challenges we faced and how we tackled them. -## Challenge #1: Automating browsers - +### Challenge #1: Automating browsers - We set out to do automated testing of these features using Selenium WebDriver on Python. It helps us automate browsers and look for specific elements on a page loaded inside a browser instance. [Getting started](https://www.selenium.dev/documentation/webdriver/getting_started/) -## Challenge #2: Loading the extension inside the browser - +### Challenge #2: Loading the extension inside the browser - Running a Selenium Webdriver using a python script opens a fresh instance of the browser, thus we needed to install a build of the extension every time we run the script. A great method that we found for this in order to load up local copies of the extension was over here: @@ -31,7 +31,7 @@ Firefox if not signed. Currently, the easiest way to sign an extension is using Using the web-ext sign command, you can use your own API-key to sign your local extension build and use it for testing. This should generate a unique file signed using your credentials that you can use in the guides posted above. -## Challenge #3: Testing the features - +### Challenge #3: Testing the features - Now that we had the local builds of the extension ready, the next step was to test the various features of Uli. One of the primary features of Uli is being able to redact known slurs that are in our crowdsourced list of slurs from tweets. diff --git a/src/blog/understanding-viral-spiral-project-page.mdx b/src/blog/understanding-viral-spiral-project-page.mdx index 1f23fd2c..e247381e 100644 --- a/src/blog/understanding-viral-spiral-project-page.mdx +++ b/src/blog/understanding-viral-spiral-project-page.mdx @@ -21,19 +21,19 @@ A quick way to check what is on the team's plate at present. What they are worki **NOTE**: The next set of 4 tabs are associated to milestones defined [here](https://github.com/tattle-made/viral-spiral-backend/milestones) -## 0.2.0 +### 0.2.0 Make the game external Playtest Ready Game. Includes all super powers. The purpose is to resume controlled playtests with players to get feedback on usability, gameplay mechanics. -## 0.3.0 +### 0.3.0 Tackle Scalability Conerns. Make the game ready to be played without any supervision from our team. Lets aim to support scaling upto 50 simultaneous gameplay sessions. -## 0.4.0 +### 0.4.0 This milestone is not clearly thought out as of now but its to make space for accomodating any radical changes that might come from user feedback after 0.3.0. If the game doesn't find an audience, we might not need to take up anything here. -## 1.0.0 +### 1.0.0 This is an end date to mark the end of the project If we're able to successfully monetize the project in someway, we would love to spend time adapting the game content to other contexts and languages. We might also like to improve the graphics and sound design for the game. All those wishlist features that will make the team feel like the game is "complete" will go here. @@ -42,6 +42,6 @@ If we're able to successfully monetize the project in someway, we would love to A way to visualize the varios tasks in a timeline. -# Identify your niche +### Identify your niche Find out what the team is working on in what ways you can help bring your ideas to life by working along with them. diff --git a/src/blog/video-recording-adms-symposium.mdx b/src/blog/video-recording-adms-symposium.mdx index 416c7d05..54059876 100644 --- a/src/blog/video-recording-adms-symposium.mdx +++ b/src/blog/video-recording-adms-symposium.mdx @@ -10,7 +10,8 @@ cover: ../images/cover-adms-keynote.jpg hi +
- +
Text Transcript can be found [here](/blog/a-new-thing-under-the-sun) \ No newline at end of file diff --git a/src/blog/viral-spiral-reading-list.mdx b/src/blog/viral-spiral-reading-list.mdx index 04d3de23..88035666 100644 --- a/src/blog/viral-spiral-reading-list.mdx +++ b/src/blog/viral-spiral-reading-list.mdx @@ -7,7 +7,10 @@ date: 2022-07-15 tags: media-literacy cover: ../images/cover-vs-reading-list.png --- + +
![cover image](../images/cover-vs-reading-list.png) +
## Inoculation @@ -32,24 +35,24 @@ in a manner that is fundamentally different from reading research papers and oth - [Wineburg, S., McGrew, S. (2018)](https://purl.stanford.edu/yk133ht8603) - [Wineburg, Sam and breakstone, joel and mcgrew, sarah and Smith, Mark and Ortega, Teresa (2021)](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3936112) -## Misinformation Typology +### Misinformation Typology - [Wardle, Claire at First Draft News from 2017](https://firstdraftnews.org/articles/fake-news-complicated/) -## Fear Speech +### Fear Speech - "Short is the Road that Leads from Fear to Hate": Fear Speech in Indian WhatsApp Groups, [Saha, P., Mathew, B., Garimella, K., Mukherjee, A](https://arxiv.org/abs/2102.03870) The differentiation of a fear speech as a category from hate speech, a kind of content that is designed to invoke fear of a community as opposed to vilifying it inspired us to come up with storylines in the game world. -## Source Criticism +### Source Criticism This is a tool from the arsenal of historians. It's a reminder that the problem of misinformation is not unique to the internet or social media. We could take a cue from how people in the past addressed these long standing problems. - [Check out Linnaeus University's brief explainer on the same](https://lnu.se/en/library/search-and-evaluate/source-criticism2/) -## Identity and Biases +### Identity and Biases The role played by people’s identities, affinities and biases in how they interact with media online is a central feature of our game’s characters and their actions. @@ -58,7 +61,7 @@ game’s characters and their actions. - [Hangartner, D., Gennaro, G., Alasiri, S., Donnay, Karsten. (2021)](https://www.pnas.org/doi/10.1073/pnas.2116310118) - [Bechler, C., Tormala, Z. (2021)](https://www.journals.uchicago.edu/doi/abs/10.1086/711732) -## Game/Media as Intervention against Misinformation +### Game/Media as Intervention against Misinformation - [Check out this game, Cranky Uncle, using critical thinking tools to help counter misinformation](https://crankyuncle.com/) - [Roozenbeek, J., van der Linden, S. (2019)](https://www.nature.com/articles/s41599-019-0279-9) @@ -74,7 +77,7 @@ game’s characters and their actions. practices employed by disinformation agents. One key way Viral Spiral differs from this game is that we chose to look at people whose incentives are personal clout and popularity on social media, rather than being part of an organized group peddling misinformation knowingly. -## Uncategorised References +### Uncategorised References - [Check out the harmful and overreaching effects a false WhatsApp message can have in Indian society](https://www.lse.ac.uk/media-and-communications/research/research-projects/whatsapp-vigilantes) - [Check out this game on how misinformation pertaining to COVID-19 can go viral](https://www.goviralgame.com/books/go-viral/title) diff --git a/src/community/baarish.mdx b/src/community/baarish.mdx new file mode 100644 index 00000000..ff19efa6 --- /dev/null +++ b/src/community/baarish.mdx @@ -0,0 +1,7 @@ +--- +img: ../images/community/baarish.jpg +name: Aditi (Baarish) Aggarwal +role: Research Development and Grants Specialist +url: "www.linkedin.com/in/baarish" +isCurrentContributor: true +--- diff --git a/src/community/kritika.mdx b/src/community/kritika.mdx new file mode 100644 index 00000000..99a5cfa8 --- /dev/null +++ b/src/community/kritika.mdx @@ -0,0 +1,7 @@ +--- +img: ../images/community/kritika.jpg +name: Kritika Rupauliha +role: Senior Fullstack Engineer +url: "https://www.linkedin.com/in/kritika-rupauliha/" +isCurrentContributor: true +--- \ No newline at end of file diff --git a/src/community/saurav-verma.mdx b/src/community/saurav-verma.mdx new file mode 100644 index 00000000..a9d01710 --- /dev/null +++ b/src/community/saurav-verma.mdx @@ -0,0 +1,8 @@ +--- +img: ../images/community/dp-saurav.jpg +name: Saurav Verma +role: Outreach and Program Manager +url: "https://www.linkedin.com/in/sauravkumarverma" +isCurrentContributor: false +--- + diff --git a/src/community/yohan-mathew.mdx b/src/community/yohan-mathew.mdx index b61f16eb..2a4a6933 100644 --- a/src/community/yohan-mathew.mdx +++ b/src/community/yohan-mathew.mdx @@ -1,6 +1,7 @@ --- +img: ../images/community/yohan.jpg name: Yohan Mathew -role: DevOps and Automation +role: AI Governance and Safety Advisor url: "https://www.linkedin.com/in/ymathew/" -isCurrentContributor: false +isCurrentContributor: true --- diff --git a/src/components/BlogSidebar.jsx b/src/components/BlogSidebar.jsx index 2dae324c..711a5509 100644 --- a/src/components/BlogSidebar.jsx +++ b/src/components/BlogSidebar.jsx @@ -3,9 +3,22 @@ import { Box, Heading, Text } from "grommet" import { GatsbyImage } from "gatsby-plugin-image" import { PlainLink } from "./atomic/TattleLinks" +/** + * RelatedPostItem displays a related blog post with an optional cover image, + * the title (linked to the blog page), and an excerpt preview. + * + * @param {object} props - Component props + * @param {object} props.post - The related blog post data + * @param {string} props.post.title - Title of the blog post + * @param {string} props.post.slug - URL slug for the blog post + * @param {object} [props.post.coverImage] - Gatsby image object for the cover image + * @param {string} [props.post.excerpt] - Short excerpt or summary of the blog post + * @returns {JSX.Element} The rendered related post item + */ + const RelatedPostItem = ({ post }) => ( @@ -37,6 +50,14 @@ const RelatedPostItem = ({ post }) => ( ) +/** + * BlogSidebar displays a list of related blog posts in the sidebar. + * + * @param {object} props - Component props + * @param {Array} props.relatedPosts - Array of related blog post objects + * @returns {JSX.Element} The rendered sidebar with related posts or a fallback message + */ + const BlogSidebar = ({ relatedPosts }) => { return ( { background="visuals-two" round="small" elevation="small" - width="100%" - className="mt-10 lg:mt-0 lg:ml-10 w-full text-center flex flex-col" + className="w-full lg:w-48 mt-10 lg:mt-0 lg:ml-10 flex flex-col" > - + Related Posts diff --git a/src/components/Calendar.js b/src/components/Calendar.js index 0aa793bd..6791817b 100644 --- a/src/components/Calendar.js +++ b/src/components/Calendar.js @@ -1,6 +1,27 @@ import React, { useEffect } from "react"; import * as d3 from "d3"; + +/** + * Calendar component renders a heatmap-style calendar using D3.js + * to visualize blog post frequency per day across multiple years. + * + * @component + * + * @param {Object} props - Component props. + * @param {Object[]} props.data - Array of blog post objects. + * @param {string} props.data[].date - ISO string of post date (e.g., "2025-01-17"). + * + * @returns {JSX.Element} A responsive calendar heatmap of blog activity. + * + * @remarks + * Uses `d3.scaleLinear` to color days by post count. + * Clears previous SVGs before rendering new ones. + * Shows tooltip on hover with post details. + */ + + + const Calendar = ({ data }) => { const startYear = d3.min(data, (d) => new Date(d.date).getFullYear()); const endYear = d3.max(data, (d) => new Date(d.date).getFullYear()); diff --git a/src/components/DropDownMenu.js b/src/components/DropDownMenu.js index 612d1307..b38987b9 100644 --- a/src/components/DropDownMenu.js +++ b/src/components/DropDownMenu.js @@ -11,6 +11,18 @@ import { **/ import { NavigationLabel, Theme } from "./atomic/core-style" +/** + * DropDownMenu renders a tooltip-style dropdown with navigational links. + * It uses Grommet's Drop component, toggled by a button click, and closes on outside click or Escape key. + * + * @param {Object} props - Component props + * @param {Array<{ id: string, target: string }>} props.options - Menu items with unique ID and target URL. + * @param {string} props.title - Button label for triggering the dropdown. + * + * @returns {JSX.Element} The dropdown menu component. + */ + + const DropDownMenu = ({ options, title }) => { const [fetching, setFetching] = useState(false) const [showToolTip, setShowToolTip] = useState(false) diff --git a/src/components/LatestBlogsUpdates.jsx b/src/components/LatestBlogsUpdates.jsx index 5f62af5f..4ffdc5fb 100644 --- a/src/components/LatestBlogsUpdates.jsx +++ b/src/components/LatestBlogsUpdates.jsx @@ -4,6 +4,16 @@ import NarrowContentWrapper from "./atomic/layout/narrow-content-wrapper" import { Box, Heading } from "grommet" import { LatestEntries } from "./LatestEntries" +/** + * LatestBlogsUpdates component fetches and displays the latest blog posts and updates. + * + * - Uses Gatsby's useStaticQuery to retrieve blog and update data. + * - Renders two sections: Latest Blogs and Latest Updates (if included later). + * + * @returns {JSX.Element} The rendered component showing the latest content. + */ + + export default function LatestBlogsUpdates() { const data = useStaticQuery(graphql` { diff --git a/src/components/LatestEntries.jsx b/src/components/LatestEntries.jsx index 75d7b24d..dc20a71a 100644 --- a/src/components/LatestEntries.jsx +++ b/src/components/LatestEntries.jsx @@ -3,6 +3,13 @@ import { Box, ResponsiveContext, Text } from "grommet" import { PlainLink } from "./atomic/TattleLinks" import { ExternalLink } from "react-feather" +/** + * Formats a date string to "MMM DD YYYY" format (e.g., "Jun 22 2025"). + * + * @param {string|Date} date - The original date input. + * @returns {string} Formatted date string. + */ + function formatDateLatestEntries(date) { let dateString = new Date(date).toDateString("ind") return dateString.split(" ").slice(1).join(" ") @@ -11,6 +18,14 @@ function formatDateLatestEntries(date) { /** * @param {string} author */ + +/** + * Formats the author string to show abbreviated form (e.g., "John Doe et al."). + * + * @param {string} author - Full author name string, usually comma-separated. + * @returns {string} Abbreviated author string. + */ + function formatAuthor(author) { if (typeof author !== "string") return "" let firstDividerIndex = -1 @@ -31,6 +46,15 @@ function formatAuthor(author) { return author } +/** + * Renders a list of blog or update entries based on the `isUpdate` flag. + * + * @param {Object} props + * @param {Object[]} props.entries - Array of entry objects (blog/update). + * @param {boolean} props.isUpdate - Determines if the list is for updates or blogs. + * @returns {JSX.Element} Rendered list of entries. + */ + // Component to Display latest entries of Blogs and Updates export function LatestEntries({ entries, isUpdate }) { const size = useContext(ResponsiveContext) diff --git a/src/components/LatestProductBlogsUpdates.jsx b/src/components/LatestProductBlogsUpdates.jsx index f6aeedaa..e68f2d66 100644 --- a/src/components/LatestProductBlogsUpdates.jsx +++ b/src/components/LatestProductBlogsUpdates.jsx @@ -4,6 +4,18 @@ import { projectSlugMaker } from "../lib/project-slug-maker" import { Box, Heading } from "grommet" import { LatestEntries } from "./LatestEntries" +/** + * `LatestProductBlogsUpdates` renders a list of the latest blog posts and updates + * related to the provided project names. + * + * @component + * @param {Object} props - Component props + * @param {string[]} props.projects - An array of project names as strings + * + * @returns {JSX.Element|null} A list of latest blogs and updates filtered by projects, + * or `null` if no projects are provided. + */ + export function LatestProductBlogsUpdates({ projects }) { //projects is an array of string const data = useStaticQuery(graphql` diff --git a/src/components/TagsRenderer.jsx b/src/components/TagsRenderer.jsx index 5ca5a1a5..c0ec27a3 100644 --- a/src/components/TagsRenderer.jsx +++ b/src/components/TagsRenderer.jsx @@ -4,6 +4,7 @@ import { Link } from "gatsby" import { Box, Heading, Button } from "grommet" import TagBubbleBlog from "./atomic/TagBubbleBlog" +// import './styles/global.css'; /** * Renders a list of tags with an option to toggle between showing all tags or a limited number. @@ -28,16 +29,17 @@ export default function TagsRenderer({ } return ( - - - - {tagTypeHeading} - - + + + + + {tagTypeHeading} + + - - {showAllTags - ? sortedUniqueTags.map((tag) => ( + + {showAllTags + ? sortedUniqueTags.map((tag) => ( )) - : sortedUniqueTags.slice(0, 10).map((tag) => ( - + : sortedUniqueTags.slice(0, 10).map((tag) => ( + ))} - {sortedUniqueTags.length > 10 && ( - - )} + {sortedUniqueTags.length > 10 && ( + + )} + ) } + diff --git a/src/components/atomic/AppShell.js b/src/components/atomic/AppShell.js index 9bab6b3a..c1c81e2c 100644 --- a/src/components/atomic/AppShell.js +++ b/src/components/atomic/AppShell.js @@ -15,6 +15,16 @@ import { PlainLink } from "./TattleLinks" * @function ContentPageLayout **/ +/** + * Renders the main layout shell for content pages, applying theme, layout width, and responsive design. + * Includes a modal banner for highlighting key content (e.g., case studies). + * + * @param {Object} props + * @param {React.ReactNode} props.children - The main content to render within the layout. + * @param {string} [props.contentWidth] - Optional maximum width for the content area. + * @returns {JSX.Element} Layout wrapper with modal and themed shell. + */ + const MODAL_TEXT = "Read our New Case Study of the Information Chaos During India's Second Covid-19 Wave" const MODAL_PATH = "/articles/covid-whatsapp-public-groups/" diff --git a/src/components/atomic/BlogHeaderCard.js b/src/components/atomic/BlogHeaderCard.js index ee47d2d5..3ec3f301 100644 --- a/src/components/atomic/BlogHeaderCard.js +++ b/src/components/atomic/BlogHeaderCard.js @@ -2,6 +2,17 @@ import React from "react" import { Box, Heading, Text } from "grommet" import { byline } from "../default-blog-index-layout" +/** + * Renders the header section of a blog post card with title, author, date, and project info. + * + * @param {Object} props + * @param {string} props.name - Blog post title. + * @param {string} props.author - Author name. + * @param {string} props.date - Publication date string. + * @param {string} [props.project] - Optional project associated with the blog. + * @returns {JSX.Element} Blog header card component. + */ + const BlogHeaderCard = ({ name, author, date, project }) => ( diff --git a/src/components/atomic/IconFeed.jsx b/src/components/atomic/IconFeed.jsx index 281404bd..9cd1736d 100644 --- a/src/components/atomic/IconFeed.jsx +++ b/src/components/atomic/IconFeed.jsx @@ -1,5 +1,11 @@ import React from "react" +/** + * SVG icon component for the feed symbol. + * + * @returns {JSX.Element} SVG feed icon. + */ + export default function IconFeed() { return ( const MailchimpSubscribeForm = () => { diff --git a/src/components/atomic/MasonryLayout.jsx b/src/components/atomic/MasonryLayout.jsx index ad869843..aac1f489 100644 --- a/src/components/atomic/MasonryLayout.jsx +++ b/src/components/atomic/MasonryLayout.jsx @@ -1,6 +1,17 @@ import React from "react" import PropTypes from "prop-types" +/** + * Renders a responsive masonry-style layout using flexbox. + * Distributes child elements evenly across specified number of columns with a given gap. + * + * @param {Object} props + * @param {number} props.columns - Number of columns in the layout. + * @param {number} props.gap - Gap (in px) between columns. + * @param {React.ReactElement[]} props.children - Elements to display within the masonry layout. + * @returns {JSX.Element} Masonry layout wrapper. + */ + // Reference : https://medium.com/the-andela-way/how-to-create-a-masonry-layout-component-using-react-f30ec9ca5e99 function MasonryLayout({columns=2,children, gap=20}){ diff --git a/src/components/atomic/MasonryLayoutResponsive.jsx b/src/components/atomic/MasonryLayoutResponsive.jsx index dbc5b854..fe547862 100644 --- a/src/components/atomic/MasonryLayoutResponsive.jsx +++ b/src/components/atomic/MasonryLayoutResponsive.jsx @@ -2,6 +2,15 @@ import React, { useEffect, useState } from "react" import MasonryLayout from "./MasonryLayout" import { Heading } from "grommet" +/** + * Responsive wrapper for MasonryLayout. + * Adjusts column count based on window width. + * + * @param {Object} props + * @param {React.ReactNode[]} props.children - Items to display in the layout. + * @returns {JSX.Element} Responsive masonry layout. + */ + const getColumnCount = (width) => { if (width < 768) { // Small (Phones) diff --git a/src/components/atomic/PeopleCard.js b/src/components/atomic/PeopleCard.js index 25094f67..d2e51318 100644 --- a/src/components/atomic/PeopleCard.js +++ b/src/components/atomic/PeopleCard.js @@ -4,6 +4,16 @@ import { ExternalLink } from "react-feather" import { PlainExternalLink } from "./TattleLinks" import { StaticImage } from "gatsby-plugin-image" +/** + * Displays a person's name, role, and external link as a profile card. + * + * @param {Object} props + * @param {string} props.name - Person's name. + * @param {string} props.role - Person's role or title. + * @param {string} props.url - External URL (e.g., profile link). + * @returns {JSX.Element} Profile card component. + */ + const PeopleCard = ({ name, role, url }) => ( { const Responsive = ({ children, diff --git a/src/components/atomic/ResponsiveImage.js b/src/components/atomic/ResponsiveImage.js index d36dddb6..72e36242 100644 --- a/src/components/atomic/ResponsiveImage.js +++ b/src/components/atomic/ResponsiveImage.js @@ -14,6 +14,13 @@ const WeirdBox = styled(Box)` * @function ResponsiveImage **/ +/** + * Renders an image with layout adjusted for screen size. + * Uses Grommet's ResponsiveContext to switch layout for small screens. + * + * @returns {JSX.Element} Responsive image component. + */ + export const ResponsiveImage = () => { const size = React.useContext(ResponsiveContext) diff --git a/src/components/atomic/SimpleHeader.js b/src/components/atomic/SimpleHeader.js index 1eb189dd..369c3656 100644 --- a/src/components/atomic/SimpleHeader.js +++ b/src/components/atomic/SimpleHeader.js @@ -10,6 +10,17 @@ import { Menu, X } from "react-feather" import { NavigationLabel, Theme } from "./core-style" import NarrowSection from "./layout/narrow-section" +/** + * Renders a responsive site header with primary navigation and hamburger menu for mobile. + * + * @param {Object} props + * @param {string} props.label - Label displayed on the header (usually the site title or section). + * @param {string} props.target - Link target for the main header label. + * @param {Object[]} props.primaryNav - List of primary navigation items. + * @param {Function} props.onHamburgerClicked - Handler for hamburger menu click (used in mobile view). + * @returns {JSX.Element} Header component with navigation. + */ + const dropDownOptions = [ { id: 1, target: "/products/kosh", label: "Archive" }, { id: 2, target: "/products/jod-bot", label: "Jod Bot" }, @@ -98,7 +109,7 @@ const MobileNavItemExternalLink = ({ target, label }) => ( const PrimaryNav = ({ primaryNav }) => { return ( - + - {[0, 1, 2, 3].map(i => ( + {[0, 1, 2, 3, 4].map(i => ( { const size = useContext(ResponsiveContext) return ( diff --git a/src/components/atomic/Spinner.js b/src/components/atomic/Spinner.js index fb6e2633..4991b6ff 100644 --- a/src/components/atomic/Spinner.js +++ b/src/components/atomic/Spinner.js @@ -1,6 +1,13 @@ import React from "react"; import { Box } from "grommet"; +/** + * Renders a centered animated spinner using SVG. + * Used as a loading indicator during async operations. + * + * @returns {JSX.Element} Spinner (loading indicator) component. + */ + const spinning = ( { return ( diff --git a/src/components/atomic/TagBubbleBlog.js b/src/components/atomic/TagBubbleBlog.js index 7810de49..cd413b77 100644 --- a/src/components/atomic/TagBubbleBlog.js +++ b/src/components/atomic/TagBubbleBlog.js @@ -1,26 +1,57 @@ -import React from "react"; -import { Box, Text } from "grommet"; +import React, { useContext } from "react"; +import { Box, Text, ResponsiveContext } from "grommet"; + +/** + * Renders a responsive blog tag bubble with an underlined label and count. + * + * The component adjusts text size based on screen size + * and ensures the tag does not wrap internally. + * + * @param {Object} props - Component props. + * @param {Object} props.data - Data object containing tag information. + * @param {string} props.data.label - The text label of the tag. + * @param {number} props.data.count - The number of posts associated with the tag. + * + * @returns {JSX.Element} A styled tag bubble component. + */ const TagBubbleBlog = ({ data }) => { + const screenSize = useContext(ResponsiveContext); + const isPhone = screenSize === "small"; + + const textSize = isPhone ? "xsmall" : "small"; + return ( - + - - {data.label} - - - {data.count} + + {data.label} + + {data.count} ); }; -export default TagBubbleBlog; +export default TagBubbleBlog; \ No newline at end of file diff --git a/src/components/atomic/TattleLinks.js b/src/components/atomic/TattleLinks.js index a873bc55..c0988d6d 100644 --- a/src/components/atomic/TattleLinks.js +++ b/src/components/atomic/TattleLinks.js @@ -2,6 +2,19 @@ import { Link } from "gatsby" import React from "react" import styled from "styled-components" +/** + * Styled link components used across the site for consistent theming and behavior. + * + * Exports: + * - Link: Themed internal link. + * - PlainLink: Internal link without decoration. + * - PlainSectionLink: Internal section-based link with minimal styling. + * - PlainHeavyLink: Bold or emphasized internal link. + * - ExternalLink: Styled anchor tag for external navigation. + * - PlainExternalLink: Minimal external link, opens in new tab. + * - SmartPlainLink: Automatically chooses between internal and external link. + */ + const ThemedLink = styled(Link)` text-decoration: "none"; box-shadow: "none"; diff --git a/src/components/atomic/TattleLogo.js b/src/components/atomic/TattleLogo.js index 4e2b0f6b..4f806a5a 100644 --- a/src/components/atomic/TattleLogo.js +++ b/src/components/atomic/TattleLogo.js @@ -2,6 +2,14 @@ import React, { useContext } from "react" import { ResponsiveContext, Box } from "grommet" import { Link, PlainLink } from "./TattleLinks" +/** + * Renders the Tattle logo linked to the homepage. + * + * @param {Object} props + * @param {string} [props.fill] - Optional fill color for the logo SVG. + * @returns {JSX.Element} Tattle logo component. + */ + const TattleLogo = ({ data, theme }) => { const fill = data && data.fill ? data.fill : "#E76D67" const scale = data && data.scale ? data.scale : 1 diff --git a/src/components/atomic/core-style.js b/src/components/atomic/core-style.js index 431c7dff..3dd63f0a 100644 --- a/src/components/atomic/core-style.js +++ b/src/components/atomic/core-style.js @@ -1,6 +1,20 @@ import styled from "styled-components" import { PlainLink } from "./TattleLinks" +/** + * Core styled components and theme definitions used throughout the application. + * + * Exports: + * - NavigationLabel: Label used in primary navigation. + * - FooterNavigationLabel: Label used in the site footer navigation. + * - SectionLabels: Label styling for different page sections. + * - Theme: Grommet theme configuration for consistent styling. + * - LandingPageHeading: Styled heading for landing pages. + * - LandingPageSubHeading: Styled subheading for landing pages. + * - LandingPageLink: Styled link used on landing pages. + * - LandingPageParagraph: Paragraph styling for landing content. + */ + const NavigationLabel = styled.span` font-family: Raleway; font-style: normal; diff --git a/src/components/atomic/customCodeBlock.js b/src/components/atomic/customCodeBlock.js index 6bce21ec..c191e82e 100644 --- a/src/components/atomic/customCodeBlock.js +++ b/src/components/atomic/customCodeBlock.js @@ -1,6 +1,16 @@ import React from "react"; import { CodeBlock, CopyBlock } from "react-code-blocks"; +/** + * Renders a syntax-highlighted code block with optional copy-to-clipboard functionality. + * + * @param {Object} props + * @param {string} props.className - Class name containing language info (e.g., "language-js"). + * @param {boolean} props.copy - Whether to show a copy button for the code. + * @param {string} props.children - The actual code content to display. + * @returns {JSX.Element} Syntax-highlighted code block component. + */ + const CustomCodeBlock = (props) => { const { className, copy, children } = props; const language = className && className.match(/(?<=language-)(\w.*?)\b/) diff --git a/src/components/atomic/inlineCodeBlock.js b/src/components/atomic/inlineCodeBlock.js index 29a12555..3909cf10 100644 --- a/src/components/atomic/inlineCodeBlock.js +++ b/src/components/atomic/inlineCodeBlock.js @@ -1,6 +1,14 @@ import React from "react"; import { dracula } from "react-code-blocks"; +/** + * Renders a styled inline code snippet. + * + * @param {Object} props + * @param {string} props.children - The code content to display inline. + * @returns {JSX.Element} Inline code block element. + */ + const InlineCodeBlock = ({ children }) => ( diff --git a/src/components/atomic/layout/narrow-content-wrapper.js b/src/components/atomic/layout/narrow-content-wrapper.js index 3c0772a8..c8398371 100644 --- a/src/components/atomic/layout/narrow-content-wrapper.js +++ b/src/components/atomic/layout/narrow-content-wrapper.js @@ -6,6 +6,17 @@ import { useLocation } from "@reach/router" * @function NarrowSection **/ +/** + * Wraps content in a responsive, centered container with adjustable width and justification. + * Designed for consistent layout across blog and other pages. + * + * @param {Object} props + * @param {React.ReactNode} props.children - The content to render inside the wrapper. + * @param {string} [props.justify] - Optional justification (e.g., "center", "start"). + * @param {string} [props.width] - Optional custom width for the container. + * @returns {JSX.Element} Responsive content wrapper. + */ + const NarrowContentWrapper = ({ children, justify, width, ...props }) => { const size = useContext(ResponsiveContext) const location = useLocation() diff --git a/src/components/atomic/layout/narrow-section.js b/src/components/atomic/layout/narrow-section.js index 02368be5..2dca2fa9 100644 --- a/src/components/atomic/layout/narrow-section.js +++ b/src/components/atomic/layout/narrow-section.js @@ -6,6 +6,14 @@ import { Box, ResponsiveContext } from "grommet" * type : 'top' **/ +/** + * Renders a narrow-width section wrapper for page content. + * + * @param {Object} props + * @param {React.ReactNode} props.children - The content to display inside the section. + * @returns {JSX.Element} Narrow section component. + */ + const NarrowSection = ({ children }) => { return ( diff --git a/src/components/atomic/seo.js b/src/components/atomic/seo.js index 4d709cd4..a6311000 100644 --- a/src/components/atomic/seo.js +++ b/src/components/atomic/seo.js @@ -11,6 +11,19 @@ import Helmet from "react-helmet" import { useStaticQuery, graphql } from "gatsby" import { useLocation } from "@reach/router" +/** + * Injects SEO-related metadata into the document head using React Helmet. + * Uses Gatsby's useStaticQuery to pull site-wide metadata. + * + * @param {Object} props + * @param {string} [props.description] - Page description for meta tag. + * @param {string} [props.lang="en"] - HTML language attribute. + * @param {Object[]} [props.meta=[]] - Additional meta tag definitions. + * @param {string} props.title - Title of the page (required). + * @param {string} [props.heading] - Optional heading (not used in meta but may be for accessibility or custom logic). + * @returns {JSX.Element} Head metadata managed by React Helmet. + */ + function SEO({ description = "", lang = `en`, meta = [], title, heading }) { const location = useLocation() const pathname = location.pathname diff --git a/src/components/atomic/text-styles.js b/src/components/atomic/text-styles.js index d2497fda..04108fa9 100644 --- a/src/components/atomic/text-styles.js +++ b/src/components/atomic/text-styles.js @@ -2,6 +2,14 @@ import React from "react" import styled from "styled-components" import { Text, Paragraph } from "grommet" +/** + * Reusable text style components for consistent typography. + * + * Exports: + * - HeadingOne: Large heading text style. + * - BodyOne: Standard body text style. + */ + const BodyOne = styled(Text)` font-family: "Bitter"; font-size: "30px"; diff --git a/src/components/atomic/theme.js b/src/components/atomic/theme.js index aaa59802..3fcab3a3 100644 --- a/src/components/atomic/theme.js +++ b/src/components/atomic/theme.js @@ -1,5 +1,12 @@ import { css } from "styled-components" +/** + * Custom Grommet theme used across the Tattle application. + * Defines global styles like font family, colors, and component theming. + * + * @type {Object} + */ + const TattleTheme = { global: { font: { diff --git a/src/components/default-blog-dashboard.js b/src/components/default-blog-dashboard.js index 5a150519..bc2ad1c8 100644 --- a/src/components/default-blog-dashboard.js +++ b/src/components/default-blog-dashboard.js @@ -3,6 +3,17 @@ import DefaultLayout from "./default-layout" // Changed to DefaultLayout import React from "react" import Calendar from "./Calendar" + +/** + * BlogDashboard component renders the main blog dashboard layout. + * It maps blog data (slug, name, date) into a heatmap-style calendar UI. + * + * @component + * @param {Object} props - React props + * @param {Object} props.data - GraphQL data containing all blog MDX nodes + * @returns {JSX.Element} Blog dashboard with a calendar view inside a DefaultLayout + */ + export default function BlogDashboard({ data }) { let blogCellsData = data.allMdx.nodes.map(blog => { return { diff --git a/src/components/default-blog-index-layout.js b/src/components/default-blog-index-layout.js index 3d8f8c77..45490717 100644 --- a/src/components/default-blog-index-layout.js +++ b/src/components/default-blog-index-layout.js @@ -11,6 +11,19 @@ export const byline = (name, project) => { if (name) return `${name}` } +/** + * BlogIndex lists all blog posts with tag filters. + * + * Renders normal and project tags using `TagsRenderer`, + * and displays posts via `AllBlogsIndexLayout`. + * + * @param {Object} props + * @param {Object} props.data - Blog data from GraphQL. + * @param {Object} props.pageContext - Page context from Gatsby. + * + * @returns {JSX.Element} + */ + const BlogIndex = ({ data, pageContext }) => { const blogs = data.allMdx.nodes const cover_blog_index = data.cover_blog_index diff --git a/src/components/default-blog-layout.js b/src/components/default-blog-layout.js index adc7a82f..6d350f00 100644 --- a/src/components/default-blog-layout.js +++ b/src/components/default-blog-layout.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react" import { graphql } from "gatsby" -import { Box, ResponsiveContext } from "grommet" +import { Box, ResponsiveContext, Paragraph } from "grommet" import { MDXProvider } from "@mdx-js/react" import { Link } from "gatsby" import { primaryNav, footerItems } from "../config/options" @@ -17,6 +17,21 @@ import { projectSlugMaker } from "../lib/project-slug-maker" import TagsRenderer from "./TagsRenderer" import { getSrc, getImage } from "gatsby-plugin-image" +/** + * Blog post layout component. + * It renders the post content using MDXProvider and AppShell, + * and displays post metadata, project tags, and a related posts section. + * + * @component + * @param {Object} props + * @param {Object} props.data - GraphQL data passed by Gatsby. + * @param {Object} props.data.mdx - MDX node containing post content and frontmatter metadata. + * @param {Object} props.pageContext - Context from Gatsby's createPage API. + * @param {Array} props.pageContext.blogNodes - List of all blog post nodes for related posts. + * @param {React.ReactNode} props.children - Rendered MDX content of the blog post. + * @returns {JSX.Element} Rendered blog post layout + */ + const shortcodes = { Link, BlogHeaderCard, @@ -149,14 +164,16 @@ export default function PageTemplate({ - + {/* Main Content */} - {children} + + {children} + {/* Sidebar Section */} - + {/* - + */} diff --git a/src/components/default-layout-narrow.js b/src/components/default-layout-narrow.js index a37374b0..2f664dce 100644 --- a/src/components/default-layout-narrow.js +++ b/src/components/default-layout-narrow.js @@ -7,6 +7,15 @@ import { useLocation } from "@reach/router" * @function DefaultLayout **/ +/** + * Narrow layout wrapper that updates label from the current route. + * + * @param {Object} props + * @param {React.ReactNode} props.children - Layout content + * @returns {JSX.Element} + */ + + const DefaultLayoutNarrow = ({ children }) => { const location = useLocation() const [label, setLabel] = useState("") diff --git a/src/components/default-layout.js b/src/components/default-layout.js index aba72847..c5bfec57 100644 --- a/src/components/default-layout.js +++ b/src/components/default-layout.js @@ -7,6 +7,17 @@ import { useLocation } from "@reach/router" * @function DefaultLayout **/ + +/** + * Full-width layout wrapper using AppShell. + * + * Updates internal label state based on current route. + * + * @param {Object} props + * @param {React.ReactNode} props.children - Content to render inside the layout + * @returns {JSX.Element} + */ + const DefaultLayout = ({ children }) => { const location = useLocation() const [label, setLabel] = useState("") diff --git a/src/components/default-people-layout.js b/src/components/default-people-layout.js index 7d3d682e..f3a82a6b 100644 --- a/src/components/default-people-layout.js +++ b/src/components/default-people-layout.js @@ -7,6 +7,18 @@ import { primaryNav, footerItems } from "../config/options" import AppShell from "./atomic/AppShell" import PeopleCard from "./atomic/PeopleCard" +/** + * Page layout for individual people pages using MDX data. + * + * Renders a PeopleCard and optional children inside AppShell. + * + * @param {Object} props + * @param {Object} props.data - Page data containing MDX frontmatter + * @param {Object} props.data.mdx - MDX node with frontmatter + * @param {React.ReactNode} props.children - Additional content to render + * @returns {JSX.Element} + */ + const shortcodes = { Link } export default function PageTemplate({ data: { mdx },children }) { diff --git a/src/components/default-sitemap-layout.js b/src/components/default-sitemap-layout.js index cddd3528..aad62e14 100644 --- a/src/components/default-sitemap-layout.js +++ b/src/components/default-sitemap-layout.js @@ -6,6 +6,16 @@ import { PlainLink } from "./atomic/TattleLinks" import DefaultLayoutNarrow from "./default-layout-narrow" import { generateDisplayName } from "../lib/generate-display-name" +/** + * SiteMapPage component renders a navigable sitemap tree using file and directory data. + * + * @component + * @param {Object} props + * @param {Object} props.pageContext - Gatsby page context (currently unused). + * @param {Object} props.data - GraphQL data containing all file nodes. + * @returns {JSX.Element} A tree-structured visual site map. + */ + export default function SiteMapPage({ pageContext, data }) { //PagContext may have siteMapNodes of type Tree Structure commented below. const nodes = data.allFile.nodes @@ -44,6 +54,13 @@ export default function SiteMapPage({ pageContext, data }) { current.push({ name: node.name, isDir: false, node: node }) }) + /** + * Recursively renders a list of sitemap nodes, skipping ignored directories. + * + * @param {Array} treeNodes - Array of nodes with name, children, and metadata. + * @returns {Array} Rendered list elements or nulls for ignored nodes. + */ + return tree } let a = [] @@ -91,11 +108,10 @@ export default function SiteMapPage({ pageContext, data }) {
  • {displayName} diff --git a/src/components/default-tag-page-layout.js b/src/components/default-tag-page-layout.js index cb71f93e..8354522c 100644 --- a/src/components/default-tag-page-layout.js +++ b/src/components/default-tag-page-layout.js @@ -3,11 +3,29 @@ import { graphql, Link } from "gatsby" import useBlogTags from "../hooks/useBlogTags" import TagPage from "./TagPage" +/** + * Returns a formatted byline string based on the presence of author and project. + * + * @param {string} author - The name of the author. + * @param {string} project - The associated project name. + * @returns {string | undefined} A formatted byline (e.g., "Author - Project"), or just "Author" if project is not provided. + */ + const byline = (author, project) => { if (author && project) return `${author} - ${project}` if (author) return `${author}` } +/** + * TagTemplate component renders a tag-specific page that lists all blog posts + * associated with a particular tag. + * + * @param {Object} props - The props object. + * @param {Object} props.data - GraphQL data object, expected to contain allMdx nodes. + * @param {Object} props.pageContext - Context passed by Gatsby, including the tag name. + * @returns {JSX.Element} A TagPage component displaying filtered posts. + */ + export default function TagTemplate({ data, pageContext }) { const { tagCounts } = useBlogTags() const { allMdx } = data diff --git a/src/components/default-tag-project-page-layout.js b/src/components/default-tag-project-page-layout.js index 0fead287..c903f36c 100644 --- a/src/components/default-tag-project-page-layout.js +++ b/src/components/default-tag-project-page-layout.js @@ -4,6 +4,15 @@ import { projectSlugMaker } from "../lib/project-slug-maker" import useBlogTags from "../hooks/useBlogTags" import TagPage from "./TagPage" +/** + * TagProjectPage component renders a list of blogs filtered by a specific project tag. + * + * @param {Object} props - Component props. + * @param {Object} props.data - GraphQL data (unused). + * @param {Object} props.pageContext - Gatsby page context containing the project slug. + * @returns {JSX.Element} Filtered TagPage by project tag. + */ + export default function TagProjectPage({ data, pageContext }) { const { projectTagsCounts } = useBlogTags() const blogNodes = data.allMdx.nodes diff --git a/src/components/default-updates-project-tag-page-layout.js b/src/components/default-updates-project-tag-page-layout.js index 508d8f4e..b2813239 100644 --- a/src/components/default-updates-project-tag-page-layout.js +++ b/src/components/default-updates-project-tag-page-layout.js @@ -4,6 +4,15 @@ import { projectSlugMaker } from "../lib/project-slug-maker" import useUpdateTags from "../hooks/useUpdateTags" import UpdatesTagPage from "./UpdatesTagPage" +/** + * UpdatesTagProjectPage component renders a list of updates filtered by a specific project tag. + * + * @param {Object} props - Component props. + * @param {Object} props.data - GraphQL data (unused). + * @param {Object} props.pageContext - Gatsby page context containing the project slug. + * @returns {JSX.Element} Filtered UpdatesTagPage by project tag. + */ + export default function UpdatesTagProjectPage({ data, pageContext }) { const { projectTagsCounts } = useUpdateTags() const updateNodes = data.allMdx.nodes diff --git a/src/components/molecule/covid-whatsapp-tsne-map.jsx b/src/components/molecule/covid-whatsapp-tsne-map.jsx index 44270b9a..a73651ff 100644 --- a/src/components/molecule/covid-whatsapp-tsne-map.jsx +++ b/src/components/molecule/covid-whatsapp-tsne-map.jsx @@ -5,6 +5,14 @@ import dataBase64 from "../../data/covid-whatsapp-public-groups/base64.json" import Clusters from "../../data/covid-whatsapp-public-groups/cluster.json" import { active } from "d3" +/** + * Shows COVID WhatsApp t-SNE map with filterable categories. + * + * Renders cluster points in SVG and updates view based on selected categories. + * + * @returns {JSX.Element} The rendered cluster map UI. + */ + const VIZ_WIDTH = 800 const VIZ_HEIGHT = 600 @@ -98,7 +106,7 @@ const SVGViz = ({ activeCategories, showPreview }) => { x={x[ix] * 800} y={y[ix] * 600} height={10} - width={10} + width={10 } href={`data:image/jpeg;base64,${dataBase64[imageId].base64}`} onMouseEnter={e => showPreview(true, ix, e.clientX, e.clientY)} imageRendering={"optimizeSpeed"} diff --git a/src/components/molecule/vaccine-hesitancy-cluster-visualization.jsx b/src/components/molecule/vaccine-hesitancy-cluster-visualization.jsx index f5258032..b6d84dc1 100644 --- a/src/components/molecule/vaccine-hesitancy-cluster-visualization.jsx +++ b/src/components/molecule/vaccine-hesitancy-cluster-visualization.jsx @@ -19,6 +19,14 @@ import { data as dataCluster7 } from "../../data/vaccine-hesitancy/vaccine_7clus import { LDAvis } from "../../data/vaccine-hesitancy/ldavis" import { PlainExternalLink } from "../../components/atomic/TattleLinks" +/** + * Renders the Vaccine Hesitancy cluster visualization using D3 inside a styled container. + * + * Handles cluster selection and option change events. + * + * @returns {JSX.Element} Vaccine hesitancy visualization component. + */ + const D3Div = styled.div` path { fill: none; diff --git a/src/config/research.js b/src/config/research.js index 1145b27c..fc73788c 100644 --- a/src/config/research.js +++ b/src/config/research.js @@ -1,5 +1,18 @@ export const research = { featured: [ + { + date: "4 November, 2025", + title: + "Make it Real : Mapping Safety Responses to AI-facilitated Gendered Harms", + description: + "A report we worked on with RATI foundation that examines how AI-generated content, popularly known as ‘deepfakes’, is impacting and reshaping online harassment. Drawing from cases reported to Rati's helpline Meri Trustline, the report reveals a concerning trend: while media & headlines often center on celebrities and politicians targeted through AI, a more personal crisis is also unfolding. Ordinary survivors are being targeted through images that are artificially generated but possess the capacity for real harm. The incidents are rarely revealed to close family circles, let alone feature in larger discourse. ", + url: "https://tattle.co.in/blog/make-it-real/", + tags: [ + "Report", + "deepfakes", + "online gender-based violence", + ], + }, { date: "16 June 2024", title: @@ -14,20 +27,7 @@ export const research = { "online gender-based violence", ], }, - { - date: "22 November, 2022", - title: "Does Incentivization Promote Sharing “True” Content Online?", - description: - "To understand the impact of incentives on online information-sharing behavior, this study created a mock social media platform and found that both financial and social incentives increased the sharing of true information, with demographic factors such as age, education, and political ideology also playing a role.", - url: "https://osf.io/preprints/psyarxiv/nykxz", - tags: [ - "incentives", - "information-sharing", - "misinformation", - "social media", - "web monetization", - ], - }, + ], all: [ { @@ -71,6 +71,20 @@ export const research = { "https://itforchange.net/feminist-perspectives-on-social-media-governance-0", tags: ["ML", "Uli", "Development Journey"], }, + { + date: "22 November, 2022", + title: "Does Incentivization Promote Sharing “True” Content Online?", + description: + "To understand the impact of incentives on online information-sharing behavior, this study created a mock social media platform and found that both financial and social incentives increased the sharing of true information, with demographic factors such as age, education, and political ideology also playing a role.", + url: "https://osf.io/preprints/psyarxiv/nykxz", + tags: [ + "incentives", + "information-sharing", + "misinformation", + "social media", + "web monetization", + ], + }, { date: "30 July, 2021", title: diff --git a/src/images/Madame_Jeanette_and_other_chillies.jpg b/src/images/Madame_Jeanette_and_other_chillies.jpg new file mode 100644 index 00000000..300a6816 Binary files /dev/null and b/src/images/Madame_Jeanette_and_other_chillies.jpg differ diff --git a/src/images/annual_report_2024- dau.jpg b/src/images/annual_report_2024- dau.jpg new file mode 100644 index 00000000..621bbc7b Binary files /dev/null and b/src/images/annual_report_2024- dau.jpg differ diff --git a/src/images/community/baarish.jpg b/src/images/community/baarish.jpg new file mode 100644 index 00000000..bcc58648 Binary files /dev/null and b/src/images/community/baarish.jpg differ diff --git a/src/images/community/dp-saurav.jpg b/src/images/community/dp-saurav.jpg new file mode 100644 index 00000000..4be3219a Binary files /dev/null and b/src/images/community/dp-saurav.jpg differ diff --git a/src/images/community/kritika.jpg b/src/images/community/kritika.jpg new file mode 100644 index 00000000..21464e66 Binary files /dev/null and b/src/images/community/kritika.jpg differ diff --git a/src/images/community/yohan.jpg b/src/images/community/yohan.jpg new file mode 100644 index 00000000..11a6f722 Binary files /dev/null and b/src/images/community/yohan.jpg differ diff --git a/src/images/cover-rati-report-make-it-real.png b/src/images/cover-rati-report-make-it-real.png new file mode 100644 index 00000000..7c807112 Binary files /dev/null and b/src/images/cover-rati-report-make-it-real.png differ diff --git a/src/images/expenditures-2024-2025-chart.png b/src/images/expenditures-2024-2025-chart.png new file mode 100644 index 00000000..f988316e Binary files /dev/null and b/src/images/expenditures-2024-2025-chart.png differ diff --git a/src/images/hero-rati-report-make-it-real.png b/src/images/hero-rati-report-make-it-real.png new file mode 100644 index 00000000..4a2a852f Binary files /dev/null and b/src/images/hero-rati-report-make-it-real.png differ diff --git a/src/pages/career/ai-safety-program-manager.mdx b/src/pages/career/ai-safety-program-manager.mdx new file mode 100644 index 00000000..37d866d1 --- /dev/null +++ b/src/pages/career/ai-safety-program-manager.mdx @@ -0,0 +1,50 @@ +import DefaultLayoutNarrow from "@/components/default-layout-narrow"; + +export default function Layout({ children }) { + return {children}; +} + +## Program Manager + +Date of Posting: 11 August 2025 + +## About the Position +Tattle is a civic tech organization that build tools and datasets to enable civic action against online harms. We support researchers as well as conduct in-house research on platform governance, gendered harassment, AI Safety and 'responsible' AI. + +We are looking for a meticulous project manager to coordinate across a number of tracks in our growing AI Safety track. The projects span developing research frameworks, consulting organizations on their AI safety practices and building tooling for safe and resposible AI. You can see some of our work here: +* [https://tattle.co.in/](https://tattle.co.in/) + +### Necessary skills: +- Ability to coordinate with external stakeholders, gather requirements, schedule work and keep the projects running on a timeline. +- Impeccable documentation skills and familiarity with documentation tools including GitHub issues, Google docs, spreadsheets +- Experience of managing tech projects and/or products. +- 4+ years of program/project management + + +**Start Date:** As soon as possible. + +**Location:** Remote but must be willing to travel for team and project meetings. + +**Duration:** This is a full time position. + +**Remuneration:** Based on experience. + +**Deadline:** Rolling + +## About Tattle + +- Tattle works on the policy of "As open as possible. As closed as necessary." We are a [FOSS project](<[https://github.com/tattle-made](https://github.com/tattle-made)>). You are encouraged to write honestly about research findings, and aren't bound by prohibitive NDAs. +- We are a fully remote team that communicates asynchronously. + +- At Tattle, we value inter-disciplinary thinking and unconventional life experiences. We provide space to incorporate your unique learnings in all that we make. + +- We provide a stress free work environment. We have a flexible 8 hours workday policy, without interruptions from us beyond that. + +- We are a fully remote team that communicates asynchronously. + +### We Value Diversity and Multiplicity of Experiences +At Tattle, we cherish diversity of experiences and perspectives. It adds value to our work. To this end, we strongly encourage candidates, even if they feel they don't meet all the above listed criteria but find alignment with the project and are driven to learn, to apply. + +## [Apply Here](https://forms.gle/wqfXhp6JAh9qRSKu5) + + diff --git a/src/pages/career/project-manager.mdx b/src/pages/career/project-manager.mdx new file mode 100644 index 00000000..37d866d1 --- /dev/null +++ b/src/pages/career/project-manager.mdx @@ -0,0 +1,50 @@ +import DefaultLayoutNarrow from "@/components/default-layout-narrow"; + +export default function Layout({ children }) { + return {children}; +} + +## Program Manager + +Date of Posting: 11 August 2025 + +## About the Position +Tattle is a civic tech organization that build tools and datasets to enable civic action against online harms. We support researchers as well as conduct in-house research on platform governance, gendered harassment, AI Safety and 'responsible' AI. + +We are looking for a meticulous project manager to coordinate across a number of tracks in our growing AI Safety track. The projects span developing research frameworks, consulting organizations on their AI safety practices and building tooling for safe and resposible AI. You can see some of our work here: +* [https://tattle.co.in/](https://tattle.co.in/) + +### Necessary skills: +- Ability to coordinate with external stakeholders, gather requirements, schedule work and keep the projects running on a timeline. +- Impeccable documentation skills and familiarity with documentation tools including GitHub issues, Google docs, spreadsheets +- Experience of managing tech projects and/or products. +- 4+ years of program/project management + + +**Start Date:** As soon as possible. + +**Location:** Remote but must be willing to travel for team and project meetings. + +**Duration:** This is a full time position. + +**Remuneration:** Based on experience. + +**Deadline:** Rolling + +## About Tattle + +- Tattle works on the policy of "As open as possible. As closed as necessary." We are a [FOSS project](<[https://github.com/tattle-made](https://github.com/tattle-made)>). You are encouraged to write honestly about research findings, and aren't bound by prohibitive NDAs. +- We are a fully remote team that communicates asynchronously. + +- At Tattle, we value inter-disciplinary thinking and unconventional life experiences. We provide space to incorporate your unique learnings in all that we make. + +- We provide a stress free work environment. We have a flexible 8 hours workday policy, without interruptions from us beyond that. + +- We are a fully remote team that communicates asynchronously. + +### We Value Diversity and Multiplicity of Experiences +At Tattle, we cherish diversity of experiences and perspectives. It adds value to our work. To this end, we strongly encourage candidates, even if they feel they don't meet all the above listed criteria but find alignment with the project and are driven to learn, to apply. + +## [Apply Here](https://forms.gle/wqfXhp6JAh9qRSKu5) + + diff --git a/src/pages/career/qualitative-researcher.mdx b/src/pages/career/qualitative-researcher.mdx index e293b403..20f99517 100644 --- a/src/pages/career/qualitative-researcher.mdx +++ b/src/pages/career/qualitative-researcher.mdx @@ -6,17 +6,22 @@ export default function Layout({ children }) { ## Qualitative Researcher -Date of Posting: 10 July 2024 +Date of Posting: 11 August 2025 ## About the Position -Tattle is a civic tech organization that build tools and datasets to understand and respond to misinformation in India. We support researchers as well as conduct in-house research on platform governance, gendered harassment, and 'responsible' AI. Through our all work we aim to enable more transparent research on misinformation on Indian Social Media. +Tattle is a civic tech organization that build tools and datasets to enable civic action against online harms. We support researchers as well as conduct in-house research on platform governance, gendered harassment, AI Safety and 'responsible' AI. -We are looking for a social scientist to support across different projects. You can read about the active projects on the [Tattle website](https://tattle.co.in/). The candidate should have expertise in one of these fields: media studies, feminist studies, STS, HCI. The candidate should have keen interest in digital/platform governance. Familiarity with impact evaluation is a bonus. +We are looking for a qualitative researcher to support our work on our growing AI Safety track. There is no specific qualification but experience with ethnographic methods and an interest in platform governance and digital rights is a must. People who have played this role in the past have come from anthropology, media studies, feminist studies, STS, HCI. You can see the kind of projects you might be embedded in here: +* [Uli Dataset](https://aclanthology.org/2024.woah-1.16/) +* [Safety Benchmarks for MLCommons](https://drive.google.com/file/d/1OKpZ7qqT6hjbzaeUC7UnBF1oCTn7c70z/view) ### Necessary skills: -- Ability to independently scope and execute research ideas. -- Ability to sift through, compile and derive insights from academic literature.- Excellent documentation skills. -- Record of presenting complex ideas to a general audience. +- Ability to faciliate focus group discussions on topics of digital risks with community based organizations and nonprofits who might not have technical expertise +- Ability to sift through, compile and derive insights from academic literature +- Excellent documentation skills. This includes managing literature through reference management systems such as Zotero +- Record of presenting complex ideas to a general audiences +- A masters or PhD or equivalent work experience + **Start Date:** As soon as possible. @@ -30,16 +35,17 @@ We are looking for a social scientist to support across different projects. You ## About Tattle -- Tattle works on the policy of "As open as possible. As closed as necessary." We are a [FOSS project](<[https://github.com/tattle-made](https://github.com/tattle-made)>). You are encouraged to write honestly about research findings, and aren't bound to any prohibitive NDAs. +- Tattle works on the policy of "As open as possible. As closed as necessary." We are a [FOSS project](<[https://github.com/tattle-made](https://github.com/tattle-made)>). You are encouraged to write honestly about research findings, and aren't bound by prohibitive NDAs. - We are a fully remote team that communicates asynchronously. - At Tattle, we value inter-disciplinary thinking and unconventional life experiences. We provide space to incorporate your unique learnings in all that we make. -- We provide a stress free work environment. We have a flexible 8 hours workday policy, without any interruptions from us beyond that. +- We provide a stress free work environment. We have a flexible 8 hours workday policy, without interruptions from us beyond that. - We are a fully remote team that communicates asynchronously. ### We Value Diversity and Multiplicity of Experiences At Tattle, we cherish diversity of experiences and perspectives. It adds value to our work. To this end, we strongly encourage candidates, even if they feel they don't meet all the above listed criteria but find alignment with the project and are driven to learn, to apply. -## [Apply Here](https://docs.google.com/forms/d/e/1FAIpQLSf9kH9vWZoscBzxMITtxbTlmUJlogj9HejsZXUjs_H1dzssuQ/viewform?usp=sf_link) +## [Apply Here](https://forms.gle/wqfXhp6JAh9qRSKu5) + diff --git a/src/pages/join-us.jsx b/src/pages/join-us.jsx index 084f1fdd..bfc3d4c9 100644 --- a/src/pages/join-us.jsx +++ b/src/pages/join-us.jsx @@ -6,8 +6,6 @@ import { Link } from "gatsby"; export default function JoinUs() { const jobs = [ { title: "Software Engineer", link: "/career/sde1" }, - { title: "Qualitative Researcher", link: "/career/qualitative-researcher" }, - { title: "Digital Marketing Consultant", link: "/career/digitalmarketing-consultant" }, { title: "Backend and DevOps Engineer", link: "/career/backend-engineer" }, { title: "Senior Software Engineer", link: "/career/senior-fullstack-developer" }, ]; diff --git a/src/pages/links.jsx b/src/pages/links.jsx index 11ab4295..73ec0184 100644 --- a/src/pages/links.jsx +++ b/src/pages/links.jsx @@ -21,7 +21,7 @@ const Links = () => { > - We are conducting User Research with the following groups: + We are conducting Livestreamed Playtests for Viral Spiral with the following groups: {" "} @@ -29,14 +29,14 @@ const Links = () => { {" "} - 2. Indian content creators who face online abuse. + 2. Gamers of all stripes - board games, mobile games, casual games, RPGs etc {`✉️ Email us at admin@tattle.co.in`} + href={"https://forms.gle/2cxjpyVBi7TuKtaz9"} + >{`🗓️ Sign up for Playtests`} Recent Links @@ -51,22 +51,22 @@ const Links = () => { 1. - Media Literacy in Classrooms - Insights from Instructors + A hot take on Open Source AI {" "} 2.{" "} - a 40 minute Playthrough Video + Exploratory Analysis of Fact Checks and URLS in Community Notes on X {" "} - of Viral Spiral + 3. Add Indian slurs to your Instagram 'hidden words' by using @@ -74,37 +74,11 @@ const Links = () => { href={"https://uli.tattle.co.in/hidden-words-for-instagram/"} target={"_blank"} > - {" "} - this web app + - - 4. Our in-progress - - {" "} - media literacy reading list{" "} - - for a forthcoming in-classroom game - - - - 5. Track what we are working on - - {" "} - and find ways to contribute - - + diff --git a/src/pages/products/kosh.jsx b/src/pages/products/kosh.jsx index b5569088..4fd64fb8 100644 --- a/src/pages/products/kosh.jsx +++ b/src/pages/products/kosh.jsx @@ -177,9 +177,9 @@ const Kosh = () => ( pad={"small"} background={"visuals-1"} direction="row-responsive" - wrap={true} + wrap={"true"} > - +